sql – 如何编写Oracle查询以查找可能的重叠日期的总长度

我正在努力寻找以下任务的查询

我有以下数据,并希望找到每个唯一ID的总网络日

ID  From        To          NetworkDay
1   03-Sep-12   07-Sep-12   5
1   03-Sep-12   04-Sep-12   2
1   05-Sep-12   06-Sep-12   2
1   06-Sep-12   12-Sep-12   5
1   31-Aug-12   04-Sep-12   3
2   04-Sep-12   06-Sep-12   3
2   11-Sep-12   13-Sep-12   3
2   05-Sep-12   08-Sep-12   3

问题是日期范围可以重叠,我无法想出sql会给我以下结果

ID  From        To          NetworkDay
1   31-Aug-12   12-Sep-12   9
2   04-Sep-12   08-Sep-12   4
2   11-Sep-12   13-Sep-12   3

接着

ID  Total Network Day
1   9
2   7

如果网络日计算是不可能的,只需到第二个表就足够了.

希望我的问题很清楚

解决方法

我们可以使用Oracle Analytics(即Oracle OVER … PARTITION BY)子句来执行此操作. PARTITION BY子句类似于GROUP BY,但没有聚合部分.这意味着我们可以将行分组在一起(即分割它们),并且它们作为单独的组对它们执行操作.当我们在每一行进行操作时,我们可以访问上一行的列.这是PARTITION BY给我们的功能. (PARTITION BY与性能分区不相关)

那么那么我们如何输出不重叠的日期?我们首先根据(ID,DFROM)字段订购查询,然后使用ID字段来创建分区(行组).然后我们使用以下表达式来测试上一行的TO值和当前行FROM值的重叠值:(在伪代码中)

max(prevIoUs.DTO,current.DFROM) as DFROM

如果基本表达式不重叠,则返回原始DFROM值,如果存在重叠,则返回上一个TO值.由于我们的行被排序,我们只需要关心最后一行.在上一行与当前行完全重叠的情况下,我们希望该行具有“零”日期范围.所以我们为DTO领域做同样的事情:

max(prevIoUs.DTO,current.DFROM) as DFROM,max(prevIoUs.DTO,current.DTO) as DTO

一旦我们用调整后的DFROM和DTO值生成了新的结果集,我们可以将它们进行聚合并计算DFROM和DTO的范围间隔.

请注意,数据库中的大多数日期计算不包括在内,如您的数据.那么像DATEDIFF(dto,dfrom)这样的东西就不会包含dto实际引用的日期,所以我们首先要调整一天.

我没有访问Oracle服务器,但我知道这是可能的Oracle分析.查询应该是这样的:
(请更新我的帖子,如果你得到这个工作.)

SELECT id,max(dfrom,LAST_VALUE(dto) OVER (PARTITION BY id ORDER BY dfrom) ) as dfrom,max(dto,LAST_VALUE(dto) OVER (PARTITION BY id ORDER BY dfrom) ) as dto
  from (
    select id,dfrom,dto+1 as dto from my_sample   -- adjust the table so that dto becomes non-inclusive
      order by id,dfrom
  ) sample;

这里的秘密是返回当前行之前的值的LAST_VALUE(dto)OVER(PARTITION BY id ORDER BY dfrom)表达式.
所以这个查询应该输出不重叠的新的dfrom / dto值.那么这是一个简单的问题,它可以对这个进行分析(dto-dfrom),并且总计总和.

使用MysqL

我确实有访问MysqL服务器,所以我确实在那里工作. MysqL没有像Oracle这样的结果分区(Analytics),所以我们必须使用结果集变量.这意味着我们使用@var:= xxx类型表达式来记住最后一个日期值,并按照dfrom / dto进行调整.相同的算法只需要一点点和更复杂的语法.我们还必须忘记ID字段更改时的最后日期值!

所以这里是样本表(你有相同的值):

create table sample(id int,dfrom date,dto date,networkDay int);
insert into sample values
    (1,'2012-09-03','2012-09-07',5),(1,'2012-09-04',2),'2012-09-05','2012-09-06','2012-09-12','2012-08-31',3),(2,'2012-09-11','2012-09-13','2012-09-08',3);

对于查询,我们输出如上所述的未分组的结果集:
变量@ld为“last date”,变量@lid为“last id”.随着@lid的更改,我们将@ld重置为null. FYI在MysqL中:=运算符是分配发生的地方,a =运算符只是等于.

这是一个3级查询,但它可以减少到2.我去了一个额外的外部查询,以保持更多的可读性.内部最多的查询很简单,它将dto列调整为不包含,并且执行正确的行排序.中间查询执行dfrom / dto值的调整,使其不重叠.外部查询简单删除未使用的字段,并计算间隔范围.

set @ldt=null,@lid=null;
select id,no_dfrom as dfrom,no_dto as dto,datediff(no_dto,no_dfrom) as days from (
select if(@lid=id,@ldt,@ldt:=null) as last,dto,if(@ldt>=dfrom,dfrom) as no_dfrom,if(@ldt>=dto,dto) as no_dto,@ldt:=if(@ldt>=dto,dto),@lid:=id as id,datediff(dto,dfrom) as overlapped_days
  from (select id,dto + INTERVAL 1 DAY as dto from sample order by id,dfrom) as sample
  ) as nonoverlapped
  order by id,dfrom;

上面的查询给出结果(通知dfrom / dto在这里不重叠):

+------+------------+------------+------+
| id   | dfrom      | dto        | days |
+------+------------+------------+------+
|    1 | 2012-08-31 | 2012-09-05 |    5 |
|    1 | 2012-09-05 | 2012-09-08 |    3 |
|    1 | 2012-09-08 | 2012-09-08 |    0 |
|    1 | 2012-09-08 | 2012-09-08 |    0 |
|    1 | 2012-09-08 | 2012-09-13 |    5 |
|    2 | 2012-09-04 | 2012-09-07 |    3 |
|    2 | 2012-09-07 | 2012-09-09 |    2 |
|    2 | 2012-09-11 | 2012-09-14 |    3 |
+------+------------+------------+------+

相关文章

SELECT a.*,b.dp_name,c.pa_name,fm_name=(CASE WHEN a.fm_n...
if not exists(select name from syscolumns where name=&am...
select a.*,pano=a.pa_no,b.pa_name,f.dp_name,e.fw_state_n...
要在 SQL Server 2019 中设置定时自动重启,可以使用 Window...
您收到的错误消息表明数据库 'EastRiver' 的...
首先我需要查询出需要使用SQL Server Profiler跟踪的数据库标...