sql >> Database >  >> RDS >> PostgreSQL

Vergelijk meerdere perioden

Na verduidelijking in commentaar.

Uw taak zoals ik die begrijp:

Controleer op alle geleverde individuele perioden (filter ) of ze vallen onder de gecombineerde datumbereiken van codesets in uw tabel (invoice ).

Het kan worden gedaan met gewone SQL, maar het is geen triviale taak . De stappen kunnen zijn:

  1. Lever datumbereiken als filters.

  2. Combineer datumbereiken in invoice tabel per code.Kan resulteren in een of meer bereiken per code.

  3. Zoek naar overlappingen tussen filters en gecombineerde facturen

  4. Classificeren:volledig gedekt / gedeeltelijk gedekt.Kan resulteren in één volledige dekking, één of twee gedeeltelijke dekkingen of geen dekking.Verlaag tot maximale dekking.

  5. Geef één rij weer voor elke combinatie van (filter, code) met de resulterende dekking, in een verstandige sorteervolgorde

Ad-hocfilterbereiken

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;

Of zet ze in een (tijdelijke) tabel en gebruik in plaats daarvan de tabel.

Combineer overlappende / aangrenzende perioden per code

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

Alternatieve definitieve SELECT (kan sneller zijn of niet, je zult moeten testen):

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;

Combineren tot één zoekopdracht

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;

Getest en werkt voor mij op PostgreSQL 9.1.




  1. FOUT:extra gegevens na de laatst verwachte kolom bij gebruik van PostgreSQL COPY

  2. Laravel 5 - Elequent GROUP BY mislukt

  3. Buffer te klein voor conversie van CLOB naar CHAR of BLOB naar RAW

  4. DB-ontwerp om de gebruiker in staat te stellen producten en productspecificaties te definiëren en zelf bestellingen in te voegen