Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Jak znaleźć całkowitą liczbę wykorzystanych dni w miesiącu?

Dane testowe :

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          

Zapytanie :

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';

Wyjście :

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

Wyjaśnienie :

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

To UNPIVOT tabeli tak, aby daty rozpoczęcia i zakończenia były w tej samej kolumnie (dt ) i otrzymują odpowiednią wartość +1 dla początku i -1 dla daty zakończenia.

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 ) )

Podaje daty rozpoczęcia i zakończenia oraz skumulowaną sumę tych wygenerowanych wartości. Początek zakresu zawsze będzie miał value=1 i total=1 a koniec zakresu zawsze będzie miał total=0 . Jeśli data znajduje się w połowie zakresu, będzie miała wartość total>1 lub value=-1 i total=1 . Używając tego, jeśli pomnożysz value i total wtedy początek zakresu jest wtedy, gdy value*total=1 a koniec zakresu jest wtedy, gdy value*total=0 a każda inna wartość wskazuje datę w połowie zakresu.

Co to daje:

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 ) )

Następnie możesz odfiltrować daty, kiedy start_end jest NULL co da ci tabelę z naprzemiennym start i end wiersze, których możesz użyć LAG aby obliczyć różnicę liczby dni:

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

Wszystko, co musisz wtedy zrobić, to SUM wszystkie różnice dla end - start; co daje powyższe zapytanie.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak przekazać kod powrotu ze skryptu Oracle z powrotem do skryptu WINDOWS Batch, który go wywołał?

  2. Co to jest OracleType ROWNUM

  3. Następny dzień roboczy (od poniedziałku do piątku) w Oracle?

  4. Czy istnieje sposób na nadanie podzapytaniu aliasu w Oracle 11g SQL?

  5. LAST_NUMBER w sekwencji wyroczni