【问题标题】:Oracle SQL overlap between begin date and end date in 2 or more records2 个或更多记录中的开始日期和结束日期之间的 Oracle SQL 重叠
【发布时间】:2017-03-14 04:30:54
【问题描述】:

数据库my_table:

id     seq     start_date     end_date
1      1       01-01-2017     02-01-2017
1      2       07-01-2017     09-01-2017
1      3       11-01-2017     11-01-2017
2      1       20-01-2017     20-01-2017
3      1       01-02-2017     02-02-2017
3      2       03-02-2017     04-02-2017
3      3       08-01-2017     09-02-2017
3      4       09-01-2017     10-02-2017
3      5       10-01-2017     12-02-2017

我的要求是获取第一个日期(通常是 seq 1 开始日期)和结束日期(通常是最后一个 seq 结束日期)以及每个唯一 ID 在所有 seq 期间发生的日期数。

发生日期:

id      1              2              3
        01-01-2017     20-01-2017     01-02-2017
        02-01-2017                    02-02-2017
        07-01-2017                    03-02-2017
        08-01-2017                    04-02-2017
        09-01-2017                    08-02-2017
        11-01-2017                    09-02-2017
                                      10-02-2017
                                      11-02-2017
                                      12-02-2017
total   6              1              9

这是我想要的结果:

id     start_date     end_date       num_date
1      01-01-2017     11-01-2017     6
2      20-01-2017     20-01-2017     1
3      01-02-2017     12-02-2017     9

我试过了

SELECT   id
         , MIN(start_date)
         , MAX(end_date)
         , SUM(end_date - start_date + 1)
FROM     my_table
GROUP BY id

并且此 SQL 语句在 id 1 和 2 中工作正常,因为开始日期和结束日期之间没有重叠日期。但是对于 id 3,结果num_date 是 11。请您建议 SQL 语句来解决这个问题吗?谢谢。

还有一个问题:数据库中的日期是datetime格式。如何将其转换为date。我尝试使用TRUNC 函数,但它有时会将日期转换为昨天。

【问题讨论】:

  • 这是您的数据中唯一可能的重叠,间隔的结束日期等于下一个的开始日期?或者两个间隔可以重叠超过一天?
  • 不会有重叠区间。
  • 您能举一个例子,TRUNC 将日期与时间截断为 YESTERDAY 吗?我从未见过(老实说我不相信)。
  • Re:附上显示 TRUNC 的图像。可能是 START_DTE 位于不同的时区? Oracle 没有我知道的“日期时间”格式,但它可能是 TIMESTAMP WITH TIME ZONE
  • ?? TRUNC 应该有00:00:00 的时间部分,你怎么能得到这样的结果? start_dte 和 end_dte 的数据类型是什么? (您可以通过运行DESCRIBE MY_TABLE 找到。)

标签: sql oracle date aggregate overlap


【解决方案1】:

您需要计算end_date 等于以下start_date 的次数。为此,您需要使用lag()lead() 分析函数。您可以使用 case 表达式进行比较,但遗憾的是,您不能在同一查询中将 case 表达式包装在 COUNTSUM 中;你需要一个子查询和一个外部查询。

类似这样的东西;未经测试,因为您没有提供 CREATE TABLE 和 INSERT 语句来重新创建示例数据。

select   id, min(start_date) as start_date, max(end_date) as end_date,
         sum(end_date - start_date + 1 - flag) as num_days
from     ( select id, start_date, end_date, 
                  case when start_date = lag(end_date) 
                            over (partition by id order by end_date) then 1
                                                                     else 0 end as flag
           from   my_table
         )
group by id;

【讨论】:

    【解决方案2】:
    SELECT id,
           MIN( start_date ) AS start_date,
           MAX( end_date )   AS end_date,
           SUM( end_date - start_date + 1 ) AS num_days
    FROM   (
      SELECT id,
             GREATEST(
               start_date,
               COALESCE(
                 LAG( end_date ) OVER ( PARTITION BY id ORDER BY seq ) + 1,
                 start_date
               )
             ) AS start_date,
             end_date
      FROM   your_table
    )
    WHERE  start_date <= end_date
    GROUP BY id;
    

    【讨论】:

    • OP 不计算不同项目的数量;他正在计算 start_date 和 end_date 之间的所有天数。这可能是 8 天,但计数仅为 2。
    • 另一个想法(这里不相关,因为你建议你需要UNION而不是UNION ALL) - 用UNION ALL“规范化”一个非正常表,它更有效到UNPIVOT,因为这只需要读取一次基表。最近自己学了这个,确实进步很大。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-02
    • 1970-01-01
    • 1970-01-01
    • 2020-01-05
    • 2021-08-05
    • 1970-01-01
    相关资源
    最近更新 更多