【Transact-SQL】统计某字段中的值第一次出现后的2小时内出现的次数

 

table1 name               createdate a              2011-03-01 10:00:00 a              2011-03-01 11:00:00 a              2011-03-01 14:00:00 b              2011-03-01 13:00:00 b              2011-03-01 13:20:00 b              2011-03-01 14:00:00   查询结果为 name             createdate                    count a             2011-03-01 10:00:00               2 a             2011-03-01 14:00:00               1 b             2011-03-01 13:00:00               3  就相当于是统计name字段中的值在第一次出现后的2小时内,总共出现了几次?

 

这个是网上的解答:

declare @table1 table(name nvarchar,createdate smalldatetime)  insert into @table1 select 'a','2011-03-01 10:00:00' union all select 'a','2011-03-01 11:00:00' union all select 'a','2011-03-01 14:00:00' union all select 'b','2011-03-01 13:00:00' union all select 'b','2011-03-01 13:20:00' union all select 'b','2011-03-01 14:00:00'  select name,        createdate,        (select count(createdate)         from @table1 b          where a.name=b.name and                a.createdate<=b.createdate and                dateadd(hh,2,a.createdate) >= b.createdate        ) as count  from @table1 a  where not exists           (select 1 from            @table1 b            where a.name=b.name and                  a.createdate>b.createdate and                  a.createdate<dateadd(hh,2,b.createdate))  group by name,createdate 


但是这个解答其实是有问题的,当把临时表中的第3条数据的createdate改为'2011-03-01 12:00:00',那么显示的结果是:

name        createdate                           count
a                2011-03-01 10:00:00        3
b                2011-03-01 13:00:00        3

在其中没有包括createdate为'2011-03-01 12:00:00'的记录,因为这个时间到为'2011-03-01 10:00:00'是超过2个小时了,也就是说为'2011-03-01 10:00:00'是第一个出现时间,到为'2011-03-01 11:59:59'为止,接下来应该是从'2011-03-01 12:00:00'开始的下个区间了,而这里显然是有问题的。

以下是我写的解法,虽然效率不是太高,但是能解决这个问题:

declare @table1 table(name nvarchar,createdate smalldatetime)  insert into @table1 select 'a','2011-03-01 10:00:00' union all select 'a','2011-03-01 11:00:00' union all select 'a','2011-03-01 12:00:00' union all select 'b','2011-03-01 13:10:00' union all select 'b','2011-03-01 13:20:00' union all select 'b','2011-03-01 14:30:00' union all select 'b','2011-03-01 15:15:00' union all select 'b','2011-03-01 16:00:00' union all select 'b','2011-03-01 17:00:00'      ;with aa  --按照name分区,同时按照createdate排序编号 as ( select name,        createdate,        ROW_NUMBER() over(partition by name                              order by createdate) as k1 from @table1  ),  r as ( select v.name,        starts=v.k1,  --区间开始的编号                ends=isnull(                min(case when v.k1<a.k1                               and DATEADD(hour,2,v.createdate) <= a.createdate                               then a.k1                           else null                     end)-1,                 max(case when v.k1<a.k1                               then a.k1                          else v.k1                     end)              ),     --区间结尾的编号                                                isnull(                min(case when v.k1<a.k1                               and DATEADD(hour,2,v.createdate) <= a.createdate                               then a.k1                           else null                     end)-1,                 max(case when v.k1<a.k1                               then a.k1                          else v.k1                     end)              ) - v.k1 as diff     --区间结尾编号与区间开始编号之间的差值                                from aa v   inner join aa a          on v.name = a.name  --只关联name相等的 group by v.name,          v.k1 having isnull(                min(case when v.k1<a.k1                               and DATEADD(hour,2,v.createdate) <= a.createdate                               then a.k1                           else null                     end)-1,                 max(case when v.k1<a.k1                               then a.k1                          else v.k1                     end)              )  >=v.k1            and                     isnull(                max(case when v.k1>a.k1 and                               DATEADD(hour,-2,v.createdate) >= a.createdate                              then v.k1 - 1                          else null                     end) + 1,                 min(case when v.k1>a.k1                               then a.k1                          else v.k1                     end)               ) = v.k1 )  --select * from r   select aa.name,        aa.createdate,        diff + 1 from r inner join aa          on aa.name = r.name            and aa.k1 =r.starts where not exists 		(select 1 		 from r rr 		 where rr.name = r.name and 		       rr.starts <> r.starts and 		       rr.starts < r.starts and 		       (rr.ends = r.ends or 		        rr.ends = r.starts) 		)            

 不过发现我的这个解法也是有问题的,最大的问题在与不能准确的确定上限在那里,还得继续考虑问题的解法。

下面这个也是有问题的:

declare @table1 table(name nvarchar,createdate smalldatetime)  insert into @table1 select 'a','2011-03-01 10:00:00' union all select 'a','2011-03-01 11:00:00' union all select 'a','2011-03-01 12:00:00'  --union all select 'b','2011-03-01 13:10:00' --union all select 'b','2011-03-01 13:20:00' --union all select 'b','2011-03-01 14:30:00' --union all select 'b','2011-03-01 15:15:00' --union all select 'b','2011-03-01 16:00:00' --union all select 'b','2011-03-01 17:15:00'      ;with a  --按照name分区,同时按照createdate排序编号 as ( select name,        createdate,        ROW_NUMBER() over(partition by name                              order by createdate) as k1 from @table1  ),  c as ( select a1.name,        a1.createdate,        a1.k1,        MIN(a2.createdate) as nextCreatedate,        MIN(a2.k1) as nextK1 from a a1 inner join a a2         on a1.name = a2.name            and a2.createdate < DATEADD(hour,2,a1.createdate)            and a2.createdate >= a1.createdate            and a1.k1 <= a2.k1 group by a1.name,          a1.createdate,          a1.k1 ),  w as ( select name,        createdate,        k1                --null,        --null,        --null from a where k1 = 1  union all  select c.name,        c.nextCreatedate,        c.nextK1         from W inner join a         on a.name = w.name            and dateadd(hour,2,w.createdate) <= a.createdate            and w.k1 <= a.k1            and w.createdate <> '2011-03-01 12:00:00'  inner join c         on w.name = c.name             and w.createdate = c.createdate            and w.k1 = c.k1 where w.k1 <=3 )  SELECT *  FROM w


下面的解法是正确的,不过用的是T-SQL,不是纯sql了:

declare @table1 table(name nvarchar(100),createdate smalldatetime)  declare @table2 table(name nvarchar(100),createdate smalldatetime,rnum bigint)  declare @temp table(name nvarchar(100),createdate smalldatetime,rnum bigint)  declare @i int = 1;  insert into @table1 select 'a','2011-03-01 10:00:00' union all select 'a','2011-03-01 11:00:00' union all select 'a','2011-03-01 12:00:00'  union all select 'b','2011-03-01 13:10:00' union all select 'b','2011-03-01 13:20:00' union all select 'b','2011-03-01 14:30:00' union all select 'b','2011-03-01 15:15:00' union all select 'b','2011-03-01 16:00:00' union all select 'b','2011-03-01 17:16:00' union all select 'b','2011-03-01 17:15:00'     ;with a  --按照name分区,同时按照createdate排序编号 as ( select name,        createdate,        ROW_NUMBER() over(partition by name                              order by createdate) as k1 from @table1  )  insert into @table2 select * from a  insert into @temp select name,        createdate,        rnum from @table2 where rnum = 1  --select * from @temp   while @i <= (select MAX(rnum) from @table2) begin     insert into @temp      	select t2.name, 	       min(t2.createdate), 	       @i +1	 	from @temp t1 	inner join @table2 t2         	        on t1.name = t2.name 	           and t2.createdate >= dateadd(hour,2,t1.createdate)	            	where t1.rnum = @i      	group by t2.name 	             	set @i = @i + 1	 end  ;with r as ( select name,        createdate from @temp group by name,          createdate )  select r.name,        r.createdate,        COUNT(1) from r inner join @table1 t         on t.name = r.name            and t.createdate >= r.createdate            and t.createdate <DATEADD(HOUR,2,r.createdate) group by r.name,          r.createdate            

 

其实这个问题是个递归问题,由上一个找到下一个,但是得构造一下:

declare @table1 table(name nvarchar,createdate smalldatetime)  insert into @table1 select 'a','2011-03-01 10:00:00' union all select 'a','2011-03-01 11:00:00' union all select 'a','2011-03-01 12:00:00' union all select 'a','2011-03-01 12:20:00'  union all select 'b','2011-03-01 13:10:00' union all select 'b','2011-03-01 13:20:00' union all select 'b','2011-03-01 14:30:00' union all select 'b','2011-03-01 15:15:00' union all select 'b','2011-03-01 16:00:00' union all select 'b','2011-03-01 17:20:00' union all select 'b','2011-03-01 17:15:00' union all select 'b','2011-03-01 19:16:00' union all select 'b','2011-03-01 17:15:00'     ;with a  --按照name分区,同时按照createdate排序编号 as ( select name,        createdate,        ROW_NUMBER() over(partition by name                              order by createdate) as k1 from @table1  ),  c   --对于每个时间,找到大于这个时间2小时的时间中最小那个时间 as ( select a1.name,        a1.createdate,        a1.k1,        MIN(a2.createdate) as nextCreatedate,        MIN(a2.k1) as nextK1 from a a1 inner join a a2         on a1.name = a2.name            and a2.createdate >= DATEADD(hour,2,a1.createdate)             group by a1.name,          a1.createdate,          a1.k1  union all  select a.name,null,null,a.createdate,1  --构造递归运行时需要的层级 from a where k1 = 1 ),  w   --递归查询 as ( select c.name,        c.createdate,        c.k1,        c.nextCreatedate,        c.nextK1,        1 as lev from c where createdate is null       and k1 is null  union all  select c.name,        c.createdate,        c.k1,        c.nextCreatedate,        c.nextK1,        lev + 1 from W inner join c         on w.name = c.name            and w.nextCreatedate = c.createdate  )  SELECT distinct name,        nextCreatedate,        nextK1  FROM w 



 

 

 

你可能感兴趣的:(table)