【问题标题】:How do I find the total number of used days in a month?如何找到一个月的总使用天数?
【发布时间】:2017-06-22 17:14:04
【问题描述】:

我得到的是一个月内使用服务的总天数。 (Start_Date 和 End_Date 都包括在内)

样本数据 1:

User  Start_Date     End_Date
A     01-Jun-2017    30-Jun-2017
B     06-Jun-2017    30-Jun-2017

Ans:服务使用天数 = 30 天。

样本数据 2:

User  Start_Date     End_Date
C     06-Jun-2017    10-Jun-2017
D     02-Jun-2017    02-Jun-2017

Ans:服务使用天数 = 6 天。

我如何编写代码来找到相同的代码,在 SQL 中比在 PLSQL 中更可取。

【问题讨论】:

  • 你能解释一下需要的逻辑吗?
  • 不理解推导逻辑(服务使用天数)。用户“A”的样本数据为 30 天,但 B 的样本数据为 25 天。在您的问题中,您刚刚提到了 30'
  • 根据您的解释,这是结束日期和开始日期之间的基本区别,或者解释不正确。从表中选择用户,(end_date - start_date)作为“使用的服务”。
  • start_date 和 end_date 总是在您感兴趣的月份吗?或者你可以让 start_date = 29-May-2017,end_date = 03-Jun-2017,涵盖 6 月的三天?如果这些都是可能的,那么这个问题就更有趣了。另外:是否有另一列显示正在使用什么(可能是哪件设备,或哪个房间等) - 解决方案是否需要按设备或按房间等为您提供答案?

标签: sql oracle


【解决方案1】:

测试数据

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          

查询

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

输出

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

说明

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

这将UNPIVOT 表格,以便开始日期和结束日期在同一列中 (dt),并为开始日期指定相应的值 +1,为结束日期指定 -1。

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

将给出开始和结束日期以及这些生成值的累积总和。范围的开头总是有value=1total=1,范围的结尾总是有total=0。如果日期在某个范围的中间,那么它将具有total>1value=-1total=1。使用它,如果您将valuetotal 相乘,则范围的开始是value*total=1 的时间,范围的结束时间是value*total=0 的时间,并且任何其他值都表示日期在范围的中间。

这是什么意思:

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

然后,您可以过滤掉 start_endNULL 的日期,这将为您留下一个表格,其中包含交替的 startend 行,您可以使用 LAG 来计算天数差异:

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

那么你需要做的就是SUMend - start的所有差异;这给出了上面的查询。

【讨论】:

  • 你完全理解我的要求@MT0。测试用例再好不过了。谢谢冠军。
  • 嘿,你能帮我让代码使用以下数据吗:(两行的开始和/或结束日期相同)SET 1:A 1-Jun-2017 3- 2017 年 6 月 B 2017 年 6 月 1 日 2017 年 6 月 2 日 结果:NULL 预期结果:3 SET 2:A 2017 年 6 月 1 日 2017 年 6 月 3 日 B 2017 年 1 月 2017 年 6 月 3 日 结果:1 预期结果:3
  • @Raghu 更新 - 很好地抓住了这些案例。只需将ROWNUM 添加到最内层SUMORDER BY 子句,以便按行计算值(而不是针对相同的日期和开始/结束值分组)。
【解决方案2】:

正如@Pravin Satav 所说,您的要求不是很清楚,我从您的解释中了解到这样的情况:

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

【讨论】:

  • 这不考虑两个(或更多)范围重叠时的重复计算天数。
  • 一定是不能从 OP 中说明的要求中说清楚。
  • 好吧,样本数据 1 示例中的 OP 声明结果应该是 30 天 - 您的答案是 30+25=55 天。
  • 正如@Pravin Satav 在早期帖子 cmets 中指出的那样,这一点引起了人们的怀疑,并且作者从未澄清过。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-03
  • 1970-01-01
  • 2017-02-22
  • 2012-05-31
  • 2010-12-13
  • 2020-02-13
  • 2016-01-30
相关资源
最近更新 更多