sql >> Database teknologi >  >> RDS >> PostgreSQL

Sammenlign flere datointervaller

Efter afklaring i kommentar.

Din opgave, som jeg forstår den:

Tjek for alle leverede individuelle datointervaller (filter ) om de er omfattet af den kombinerede datointervaller for sæt koder i din tabel (invoice ).

Det kan gøres med almindelig SQL, men det er ikke en triviel opgave . Trinene kunne være:

  1. Angiv datointervaller som filtre.

  2. Kombiner datointervaller i invoice tabel pr. kode. Kan resultere i et eller flere intervaller pr. kode.

  3. Se efter overlapninger mellem filtre og kombinerede fakturaer

  4. Klassificer:fuldt dækket / delvist dækket. Kan resultere i en fuld dækning, en eller to delvise dækninger eller ingen dækning. Reducer til maksimalt dækningsniveau.

  5. Vis en række for hver kombination af (filter, kode) med den resulterende dækning i en fornuftig sorteringsrækkefølge

Ad hoc-filterintervaller

WITH filter(filter_id, startdate, enddate) AS (
    VALUES
      (1, '2012-05-01'::date, '2012-06-05'::date) -- list filters here.
     ,(2, '2012-05-01', '2012-05-31')
     ,(3, '2012-06-01', '2012-06-30')
    )
SELECT * FROM filter;

Eller læg dem i en (midlertidig) tabel og brug tabellen i stedet.

Kombiner overlappende/tilstødende datointervaller pr. kode

WITH a AS (
    SELECT code, startdate, enddate
          ,max(enddate) OVER (PARTITION BY code ORDER BY startdate) AS max_end
-- Calculate the cumulative maximum end of the ranges sorted by start
    FROM   invoice
    ), b AS (
    SELECT *
          ,CASE WHEN lag(max_end) OVER (PARTITION BY code
                                        ORDER BY startdate) + 2 > startdate
-- Compare to the cumulative maximum end of the last row.
-- Only if there is a gap, start a new group. Therefore the + 2.
           THEN 0 ELSE 1 END AS step
    FROM   a
    ), c AS (
    SELECT code, startdate, enddate, max_end
          ,sum(step) OVER (PARTITION BY code ORDER BY startdate) AS grp
-- Members of the same date range end up in the same grp
-- If there is a gap, the grp number is incremented one step
    FROM   b
    )
SELECT code, grp
      ,min(startdate) AS startdate
      ,max(enddate) AS enddate
FROM   c
GROUP  BY 1, 2
ORDER  BY 1, 2

Alternativ endelig SELECT (kan være hurtigere eller ej, du bliver nødt til at teste):

SELECT DISTINCT code, grp
          ,first_value(startdate) OVER w AS startdate
          ,last_value(enddate) OVER w AS enddate
FROM   c
WINDOW W AS (PARTITION BY code, grp ORDER BY startdate
             RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) 
ORDER  BY 1, 2;

Kombiner til én forespørgsel

WITH 
    -- supply one or more filter values
    filter(filter_id, startdate, enddate) AS (
    VALUES
      (1, '2012-05-01'::date, '2012-06-05'::date) -- cast values in first row
     ,(2, '2012-05-01', '2012-05-31')
     ,(3, '2012-06-01', '2012-06-30')
    )
    -- combine date ranges per code
    ,a AS (
    SELECT code, startdate, enddate
          ,max(enddate) OVER (PARTITION BY code ORDER BY startdate) AS max_end
    FROM   invoice
    ), b AS (
    SELECT *
          ,CASE WHEN (lag(max_end) OVER (PARTITION BY code ORDER BY startdate)
                      + 2) > startdate THEN 0 ELSE 1 END AS step
    FROM   a
    ), c AS (
    SELECT code, startdate, enddate, max_end
          ,sum(step) OVER (PARTITION BY code ORDER BY startdate) AS grp
    FROM   b
    ), i AS ( -- substitutes original invoice table
    SELECT code, grp
          ,min(startdate) AS startdate
          ,max(enddate) AS enddate
    FROM   c
    GROUP  BY 1, 2
    )
    -- match filters
    , x AS (
    SELECT f.filter_id, i.code
            ,bool_or(f.startdate >= i.startdate
              AND f.enddate   <= i.enddate) AS full_cover
    FROM   filter f
    JOIN   i ON i.enddate >= f.startdate
            AND i.startdate <= f.enddate -- only overlapping
    GROUP  BY 1,2
    )
SELECT f.*, i.code
      ,CASE x.full_cover
        WHEN TRUE  THEN 'fully covered'
        WHEN FALSE THEN 'partially covered'
        ELSE            'invoice missing'
       END AS covered
FROM   (SELECT DISTINCT code FROM i) i
CROSS  JOIN filter f -- all combinations of filter and code
LEFT   JOIN x USING (filter_id, code)    -- join in overlapping
ORDER  BY filter_id, code;

Testet og virker for mig på PostgreSQL 9.1.




  1. Kompakte eller omnummerere ID'er for alle tabeller, og nulstille sekvenser til max(id)?

  2. Pas på vildledende data fra SET STATISTICS IO

  3. Talk-dias:Partitioneringsforbedringer i PostgreSQL 11

  4. MySQL-tabel med AUTO_INCREMENT primært id frigiver ikke nummeret efter en rollback