数据表记录了用户每次离开房间时的离开时间、游戏时长、在线时长,现在需统计用户每天的在线时长数据。现实中存在用户前一天进入了房间玩游戏,然后在次日离开房间,对于这类数据在数据库中依然只会记录一条记录,对应其离开房间时间、游戏时长、在线时长。如果我们直接用此记录查询,会出现用户当天在线时长>24小时的现象,业务上我们可以解释为跨天在线(游戏),但在逻辑上当天在线时长>24小时,显然是有问题的。因此我们需要将跨天的记录进行拆分。
用图形描述我们期望达到的效果:
如果起止时间跨天的,我们以0点将其拆分成多条记录,计算各区间的在线时长,然后将游戏时长从起始时间开始填充,游戏时长用完补0。
参考,得到满足需求的处理脚本:
WITH x0 AS ( SELECT ‘A‘ AS Ident ,139185 AS PlayTime ,DATEADD(SS,-256199,‘2016-01-11 20:59:43.243‘) date_begin ,CONVERT(DATETIME ,‘2016-01-11 20:59:43.243‘) AS date_end ,256199 AS OnlineTime UNION ALL SELECT ‘B‘ AS Ident ,6085 AS PlayTime ,DATEADD(SS,-6199,‘2015-10-18 00:12:42.840‘) date_begin ,CONVERT(DATETIME ,‘2015-10-18 00:12:42.840‘) AS date_end ,6199 AS OnlineTime ),/*计算两个时间点之间相差的天数*/ x1 AS ( SELECT Ident ,PlayTime ,date_begin ,date_end ,OnlineTime ,DATEDIFF(DAY ,date_begin ,date_end) AS cacl_day--开始时间和结束时间相差的天数 FROM x0 ),/*将隔天的时间分解*/ x2 AS ( SELECT Ident ,PlayTime ,CASE WHEN msv.number=0 THEN date_begin ELSE CONVERT(VARCHAR(10) ,DATEADD(DAY ,msv.number ,date_begin) ,120) END AS date_begin ,CASE WHEN msv.number=x.cacl_day THEN date_end ELSE CONVERT(VARCHAR(10) ,DATEADD(DAY ,msv.number ,date_begin) ,120) +‘ 23:59:59.997‘ END AS date_end ,OnlineTime ,CASE WHEN msv.number=x.cacl_day THEN 1 ELSE 0 END AS flag--如果是最后一天,标识为1,否则为0 FROM x1 x ,MASTER..spt_values msv WHERE msv.type = ‘P‘ AND msv.number<= x.cacl_day ), x3 AS ( SELECT Ident ,PlayTime ,date_begin ,date_end ,OnlineTime ,CASE WHEN CONVERT(CHAR(8) ,date_end ,108)=‘23:59:59‘ THEN DATEDIFF(SS ,date_begin ,date_end)+1 ELSE DATEDIFF(SS ,date_begin ,date_end) END AS cacl_OnlineTime ,flag ,ROW_NUMBER() OVER(PARTITION BY Ident ORDER BY date_end) AS rn--行号 FROM x2 ) SELECT * ,CASE WHEN playtime>=( SELECT SUM(cacl_OnlineTime) FROM x3 x WHERE x.Ident = x3.Ident AND x.rn<= x3.rn ) THEN cacl_OnlineTime WHEN playtime<( SELECT SUM(cacl_OnlineTime) FROM x3 x WHERE x.Ident = x3.Ident AND x.rn<= x3.rn ) AND playtime>( SELECT ISNULL(SUM(cacl_OnlineTime) ,0) FROM x3 x WHERE x.Ident = x3.Ident AND x.rn<x3.rn ) THEN playtime-( SELECT ISNULL(SUM(cacl_OnlineTime) ,0) FROM x3 x WHERE x.Ident = x3.Ident AND x.rn<x3.rn ) ELSE 0 END cacl_PlayTime FROM x3 ORDER BY Ident,date_begin
数据是能拆分,但如果记录数较多,最好分批处理。如果还要与原数据表中非跨天的记录结合查询,即用拆分好的记录替代原跨天记录,这个消耗也不小。
原文:http://www.cnblogs.com/Uest/p/5207803.html