How do I find the total number of used days in a month?

前端 未结 2 783
北荒
北荒 2020-12-10 09:16

I am arriving at the total number of days a service has been used in a month. (Start_Date and End_Date are - both inclusive)

Sample Data 1:

User  Sta         


        
相关标签:
2条回答
  • 2020-12-10 09:39

    Test Data:

    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          
    

    Query:

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

    Output:

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

    Explanation:

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

    This will UNPIVOT the table so that the start and end dates are in the same column (dt) and are given a corresponding value of +1 for a start and -1 for an end date.

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

    Will give the start and end dates and the cumulative sum of those generated values. The start of a range will always have value=1 and total=1 and the end of a range will always have total=0. If a date is mid-way through a range then it will either have total>1 or value=-1 and total=1. Using this, if you multiply value and total then the start of a range is when value*total=1 and the end of a range is when value*total=0 and any other value indicates a date that is midway through a range.

    Which is what this gives:

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

    You can then filter out the dates when the start_end is NULL which will leave you with a table with alternating start and end rows which you can use LAG to calculate the number of days difference:

    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
    

    All you need to do then is to SUM all the differences for the end - start; which gives the query above.

    0 讨论(0)
  • 2020-12-10 09:44

    As @Pravin Satav addressed, your requirement it's not very clear, something like this is what I understood from your explanation:

    SELECT sum(CASE WHEN end_date=start_date THEN 1 ELSE (end_date-start_date)+1 END) as total_days
    FROM my_table
    WHERE <conditions that determine your "sample data">;
    
    0 讨论(0)
提交回复
热议问题