zoukankan      html  css  js  c++  java
  • 【TransactSQL】统计某字段中的值第一次出现后的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
    



     

  • 相关阅读:
    利用CSS计数函数counter()实现计数
    弹跳加载动画特效Bouncing loader
    HTML页面中解决内容元素随窗口变化布局变乱问题
    CSS中列表项list样式
    框模型中设置内容区域元素占地尺寸box-sizing属性
    PHP100视频教程-->视频下载
    HTML页面中5种超酷的伪类选择器:hover效果
    HTML中获取input中单选按钮radio数据(性别例子)
    HTML中 DOM操作的Document 对象详解(收藏)
    14、输入一个链表,从尾到头打印链表每个节点的值。
  • 原文地址:https://www.cnblogs.com/momogua/p/8304608.html
Copyright © 2011-2022 走看看