我有以下数据,并希望找到每个唯一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
如果网络日计算是不可能的,只需到第二个表就足够了.
希望我的问题很清楚
解决方法
那么那么我们如何输出不重叠的日期?我们首先根据(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 | +------+------------+------------+------+