sql >> Database >  >> RDS >> Oracle

Hoe vind ik het totaal aantal gebruikte dagen in een maand?

Testgegevens :

CREATE TABLE your_table ( usr, start_date, end_date ) AS (
  SELECT 'A', DATE '2017-06-01', DATE '2017-06-03' FROM DUAL UNION ALL
  SELECT 'B', DATE '2017-06-02', DATE '2017-06-04' FROM DUAL UNION ALL -- Overlaps previous
  SELECT 'C', DATE '2017-06-06', DATE '2017-06-06' FROM DUAL UNION ALL
  SELECT 'D', DATE '2017-06-07', DATE '2017-06-07' FROM DUAL UNION ALL -- Adjacent to previous
  SELECT 'E', DATE '2017-06-11', DATE '2017-06-20' FROM DUAL UNION ALL
  SELECT 'F', DATE '2017-06-14', DATE '2017-06-15' FROM DUAL UNION ALL -- Within previous
  SELECT 'G', DATE '2017-06-22', DATE '2017-06-25' FROM DUAL UNION ALL
  SELECT 'H', DATE '2017-06-24', DATE '2017-06-28' FROM DUAL UNION ALL -- Overlaps previous and next
  SELECT 'I', DATE '2017-06-27', DATE '2017-06-30' FROM DUAL UNION ALL
  SELECT 'J', DATE '2017-06-27', DATE '2017-06-28' FROM DUAL;          -- Within H and I          

Zoekopdracht :

SELECT SUM( days ) AS total_days
FROM   (
  SELECT dt - LAG( dt ) OVER ( ORDER BY dt ) + 1 AS days,
         start_end
  FROM   (
    SELECT dt,
           CASE SUM( value ) OVER ( ORDER BY dt ASC, value DESC, ROWNUM ) * value
             WHEN 1 THEN 'start'
             WHEN 0 THEN 'end'
           END AS start_end
    FROM   your_table
    UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )
  )
  WHERE start_end IS NOT NULL
)
WHERE start_end = 'end';

Uitvoer :

TOTAL_DAYS
----------
        25

Uitleg :

SELECT dt, value
FROM   your_table
UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )

Dit zal UNPIVOT de tabel zodat de begin- en einddatum in dezelfde kolom staan ​​(dt ) en krijgen een corresponderende waarde van +1 voor een begin en -1 voor een einddatum.

SELECT dt,
       SUM( value ) OVER ( ORDER BY dt ASC, value DESC, ROWNUM ) AS total,
       value
FROM   your_table
UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )

Geeft de begin- en einddatum en de cumulatieve som van die gegenereerde waarden. Het begin van een bereik heeft altijd value=1 en total=1 en het einde van een bereik heeft altijd total=0 . Als een datum halverwege een bereik ligt, heeft deze ofwel total>1 of value=-1 en total=1 . Als u dit gebruikt, als u value vermenigvuldigt, en total dan is het begin van een bereik wanneer value*total=1 en het einde van een bereik is wanneer value*total=0 en elke andere waarde geeft een datum aan die halverwege een bereik ligt.

Wat dit geeft:

SELECT dt,
       CASE SUM( value ) OVER ( ORDER BY dt ASC, value DESC, ROWNUM ) * value
         WHEN 1 THEN 'start'
         WHEN 0 THEN 'end'
       END AS start_end
FROM   your_table
UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )

U kunt dan de datums uitfilteren waarop de start_end is NULL waardoor je een tabel krijgt met afwisselend start en end rijen die u kunt gebruiken LAG om het aantal dagen verschil te berekenen:

SELECT dt - LAG( dt ) OVER ( ORDER BY dt ) + 1 AS days,
       start_end
FROM   (
  SELECT dt,
         CASE SUM( value ) OVER ( ORDER BY dt ASC, value DESC, ROWNUM ) * value
           WHEN 1 THEN 'start'
           WHEN 0 THEN 'end'
         END AS start_end
  FROM   your_table
  UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )
)
WHERE start_end IS NOT NULL

Het enige wat u dan hoeft te doen is SUM alle verschillen voor de end - start; die de bovenstaande vraag geeft.



  1. get_result() Werkt niet, zelfs mysqlnd is ingeschakeld

  2. SQL-prestaties UNION versus OR

  3. Gebruik FILE_IDEX() om de ID van een databasebestand in SQL Server te retourneren

  4. het verzamelen van jaarlijkse geaggregeerde gegevens voor latere snelle toegang