Re: interval origami - Mailing list pgsql-sql

From Adam Jensen
Subject Re: interval origami
Date
Msg-id a824b8c9-24d3-d577-c2d8-7845a1da6646@riseup.net
Whole thread Raw
In response to Re: interval origami  (Adam Jensen <hanzer@riseup.net>)
Responses Re: interval origami
List pgsql-sql
On 11/30/18 7:04 PM, Adam Jensen wrote:
> On 11/30/18 4:02 PM, Adam Jensen wrote:
>> On 11/30/18 3:19 PM, Adam Jensen wrote:
>>> The 'numrange' type with the 'overlaps' and 'intersection' operators
>>> seem to cover the fundamental computations in a very natural way.
>>
>> Actually, those operators might not be entirely sufficient. Given two
>> ranges like this:
>>
>> 10.0|39.0|interesting
>> 15.0|21.0|fail
>>
>> Something like the negative or inverse of the intersection is needed:
>>
>> 10.0|15.0|interesting
>> 21.0|39.0|interesting
> 
> I've mapped out nine time segment overlap scenarios:
> 
> 1. good(10, 40) | bad(05, 15) -> good(15, 40)
> 2. good(10, 40) | bad(10, 15) -> good(15, 40)
> 3. good(10, 40) | bad(20, 30) -> good(10, 20), good(30, 40)
> 4. good(10, 40) | bad(20, 40) -> good(10, 20)
> 5. good(10, 40) | bad(20, 45) -> good(10, 20)
> 6. good(10, 40) | bad(05, 40) -> good()
> 7. good(10, 40) | bad(05, 45) -> good()
> 8. good(10, 40) | bad(10, 40) -> good()
> 9. good(10, 40) | bad(10, 45) -> good()
> 
> Letting gs/gf and bs/bf represent "good start-time"/"good finish-time"
> and so on, pseudo-code to remove the bad segments looks like this:
> 
> find overlap: good(gs, gf) | bad(bs, bf)
> 
> CASE
>     WHEN ((bs <= gs) AND (bf <  gf)) THEN  # 1 & 2
>         -> (bf, gf)
>     WHEN ((bs >  gs) AND (bf <  gf)) THEN  # 3
>         -> (gs, bs), (bf, gf)
>     WHEN ((bs >  gs) AND (bf >= gf)) THEN  # 4 &
>         -> (gs, bs)
>     WHEN ((bs <= gs) AND (bf >= gf)) THEN  # 6 & 7 & 8 & 9
>         -> ()
> END CASE;
> 
> And my first attempt at writing a PostgreSQL function looks like this:
> 
> CREATE FUNCTION find_overlap(gs REAL, gf REAL, bs REAL, bf REAL)
>     RETURNS TABLE (start REAL, stop REAL) AS $$
>     BEGIN
>         CASE
>             WHEN ((bs <= gs) AND (bf <  gf)) THEN
>                 RETURN NEXT (bf, gf);
>                 RETURN;
>             WHEN ((bs >  gs) AND (bf <  gf)) THEN
>                 RETURN NEXT (gs, bs);
>                 RETURN NEXT (bf, gf);
>                 RETURN;
>             WHEN ((bs >  gs) AND (bf >= gf)) THEN
>                 RETURN NEXT (gs, bs);
>                 RETURN;
>             WHEN ((bs <= gs) AND (bf >= gf)) THEN
>                 RETURN;
>         END CASE;
>     END; $$
> LANGUAGE plpgsql;
> 
> It results in:
> 
> ERROR:  RETURN NEXT cannot have a parameter in function with OUT parameters
> LINE 6: RETURN NEXT (bf, gf);
> 
> Page 83 of the book "PostgreSQL Server Programming" mentions this
> situation but doesn't actually describe or explain anything; nor does it
> present a working example...
> 
> Any ideas?
> 

This seems to work. Trial and error got me there.

CREATE FUNCTION find_overlap(gs REAL, gf REAL, bs REAL, bf REAL)
    RETURNS TABLE (start REAL, stop REAL) AS $$
    BEGIN
        CASE
            WHEN ((bs <= gs) AND (bf <  gf)) THEN
                RETURN QUERY VALUES (bf, gf);
            WHEN ((bs >  gs) AND (bf <  gf)) THEN
                RETURN QUERY VALUES (gs, bs), (bf, gf);
            WHEN ((bs >  gs) AND (bf >= gf)) THEN
                RETURN QUERY VALUES (gs, bs);
            WHEN ((bs <= gs) AND (bf >= gf)) THEN
                RETURN;
        END CASE;
    END; $$
LANGUAGE plpgsql;

1. good(10, 40) | bad(05, 15) -> good(15, 40)
2. good(10, 40) | bad(10, 15) -> good(15, 40)
3. good(10, 40) | bad(20, 30) -> good(10, 20), good(30, 40)
4. good(10, 40) | bad(20, 40) -> good(10, 20)
5. good(10, 40) | bad(20, 45) -> good(10, 20)
6. good(10, 40) | bad(05, 40) -> good()
7. good(10, 40) | bad(05, 45) -> good()
8. good(10, 40) | bad(10, 40) -> good()
9. good(10, 40) | bad(10, 45) -> good()

SELECT find_overlap(10,40, 05,15);
SELECT find_overlap(10,40, 10,15);
SELECT find_overlap(10,40, 20,30);
SELECT find_overlap(10,40, 20,40);
SELECT find_overlap(10,40, 20,45);
SELECT find_overlap(10,40, 05,40);
SELECT find_overlap(10,40, 05,45);
SELECT find_overlap(10,40, 10,40);
SELECT find_overlap(10,40, 10,45);



pgsql-sql by date:

Previous
From: Adam Jensen
Date:
Subject: Re: interval origami
Next
From: Adam Jensen
Date:
Subject: Re: interval origami