原文:【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小时内,总共出现了几次?

这个是网上的解答:


  1. declare @table1 table(name nvarchar,createdate smalldatetime)
  2. insert into @table1
  3. select 'a','2011-03-01 10:00:00'
  4. union all select 'a','2011-03-01 11:00:00'
  5. union all select 'a','2011-03-01 14:00:00'
  6. union all select 'b','2011-03-01 13:00:00'
  7. union all select 'b','2011-03-01 13:20:00'
  8. union all select 'b','2011-03-01 14:00:00'
  9. select name,
  10. createdate,
  11. (select count(createdate)
  12. from @table1 b
  13. where a.name=b.name and
  14. a.createdate<=b.createdate and
  15. dateadd(hh,2,a.createdate) >= b.createdate
  16. ) as count
  17. from @table1 a
  18. where not exists
  19. (select 1 from
  20. @table1 b
  21. where a.name=b.name and
  22. a.createdate>b.createdate and
  23. a.createdate<dateadd(hh,2,b.createdate))
  24. 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'开始的下个区间了,而这里显然是有问题的。

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


  1. declare @table1 table(name nvarchar,createdate smalldatetime)
  2. insert into @table1
  3. select 'a','2011-03-01 10:00:00'
  4. union all select 'a','2011-03-01 11:00:00'
  5. union all select 'a','2011-03-01 12:00:00'
  6. union all select 'b','2011-03-01 13:10:00'
  7. union all select 'b','2011-03-01 13:20:00'
  8. union all select 'b','2011-03-01 14:30:00'
  9. union all select 'b','2011-03-01 15:15:00'
  10. union all select 'b','2011-03-01 16:00:00'
  11. union all select 'b','2011-03-01 17:00:00'
  12. ;with aa --按照name分区,同时按照createdate排序编号
  13. as
  14. (
  15. select name,
  16. createdate,
  17. ROW_NUMBER() over(partition by name
  18. order by createdate) as k1
  19. from @table1
  20. ),
  21. r
  22. as
  23. (
  24. select v.name,
  25. starts=v.k1, --区间开始的编号
  26. ends=isnull(
  27. min(case when v.k1<a.k1
  28. and DATEADD(hour,2,v.createdate) <= a.createdate
  29. then a.k1
  30. else null
  31. end)-1,
  32. max(case when v.k1<a.k1
  33. then a.k1
  34. else v.k1
  35. end)
  36. ), --区间结尾的编号
  37. isnull(
  38. min(case when v.k1<a.k1
  39. and DATEADD(hour,2,v.createdate) <= a.createdate
  40. then a.k1
  41. else null
  42. end)-1,
  43. max(case when v.k1<a.k1
  44. then a.k1
  45. else v.k1
  46. end)
  47. ) - v.k1 as diff --区间结尾编号与区间开始编号之间的差值
  48. from aa v
  49. inner join aa a
  50. on v.name = a.name --只关联name相等的
  51. group by v.name,
  52. v.k1
  53. having isnull(
  54. min(case when v.k1<a.k1
  55. and DATEADD(hour,2,v.createdate) <= a.createdate
  56. then a.k1
  57. else null
  58. end)-1,
  59. max(case when v.k1<a.k1
  60. then a.k1
  61. else v.k1
  62. end)
  63. ) >=v.k1
  64. and
  65. isnull(
  66. max(case when v.k1>a.k1 and
  67. DATEADD(hour,-2,v.createdate) >= a.createdate
  68. then v.k1 - 1
  69. else null
  70. end) + 1,
  71. min(case when v.k1>a.k1
  72. then a.k1
  73. else v.k1
  74. end)
  75. ) = v.k1
  76. )
  77. --select * from r
  78. select aa.name,
  79. aa.createdate,
  80. diff + 1
  81. from r
  82. inner join aa
  83. on aa.name = r.name
  84. and aa.k1 =r.starts
  85. where not exists
  86. (select 1
  87. from r rr
  88. where rr.name = r.name and
  89. rr.starts <> r.starts and
  90. rr.starts < r.starts and
  91. (rr.ends = r.ends or
  92. rr.ends = r.starts)
  93. )

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

下面这个也是有问题的:


  1. declare @table1 table(name nvarchar,createdate smalldatetime)
  2. insert into @table1
  3. select 'a','2011-03-01 10:00:00'
  4. union all select 'a','2011-03-01 11:00:00'
  5. union all select 'a','2011-03-01 12:00:00'
  6. --union all select 'b','2011-03-01 13:10:00'
  7. --union all select 'b','2011-03-01 13:20:00'
  8. --union all select 'b','2011-03-01 14:30:00'
  9. --union all select 'b','2011-03-01 15:15:00'
  10. --union all select 'b','2011-03-01 16:00:00'
  11. --union all select 'b','2011-03-01 17:15:00'
  12. ;with a --按照name分区,同时按照createdate排序编号
  13. as
  14. (
  15. select name,
  16. createdate,
  17. ROW_NUMBER() over(partition by name
  18. order by createdate) as k1
  19. from @table1
  20. ),
  21. c
  22. as
  23. (
  24. select a1.name,
  25. a1.createdate,
  26. a1.k1,
  27. MIN(a2.createdate) as nextCreatedate,
  28. MIN(a2.k1) as nextK1
  29. from a a1
  30. inner join a a2
  31. on a1.name = a2.name
  32. and a2.createdate < DATEADD(hour,2,a1.createdate)
  33. and a2.createdate >= a1.createdate
  34. and a1.k1 <= a2.k1
  35. group by a1.name,
  36. a1.createdate,
  37. a1.k1
  38. ),
  39. w
  40. as
  41. (
  42. select name,
  43. createdate,
  44. k1
  45. --null,
  46. --null,
  47. --null
  48. from a
  49. where k1 = 1
  50. union all
  51. select c.name,
  52. c.nextCreatedate,
  53. c.nextK1
  54. from W
  55. inner join a
  56. on a.name = w.name
  57. and dateadd(hour,2,w.createdate) <= a.createdate
  58. and w.k1 <= a.k1
  59. and w.createdate <> '2011-03-01 12:00:00'
  60. inner join c
  61. on w.name = c.name
  62. and w.createdate = c.createdate
  63. and w.k1 = c.k1
  64. where w.k1 <=3
  65. )
  66. SELECT *
  67. FROM w

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


  1. declare @table1 table(name nvarchar(100),createdate smalldatetime)
  2. declare @table2 table(name nvarchar(100),createdate smalldatetime,rnum bigint)
  3. declare @temp table(name nvarchar(100),createdate smalldatetime,rnum bigint)
  4. declare @i int = 1;
  5. insert into @table1
  6. select 'a','2011-03-01 10:00:00'
  7. union all select 'a','2011-03-01 11:00:00'
  8. union all select 'a','2011-03-01 12:00:00'
  9. union all select 'b','2011-03-01 13:10:00'
  10. union all select 'b','2011-03-01 13:20:00'
  11. union all select 'b','2011-03-01 14:30:00'
  12. union all select 'b','2011-03-01 15:15:00'
  13. union all select 'b','2011-03-01 16:00:00'
  14. union all select 'b','2011-03-01 17:16:00'
  15. union all select 'b','2011-03-01 17:15:00'
  16. ;with a --按照name分区,同时按照createdate排序编号
  17. as
  18. (
  19. select name,
  20. createdate,
  21. ROW_NUMBER() over(partition by name
  22. order by createdate) as k1
  23. from @table1
  24. )
  25. insert into @table2
  26. select * from a
  27. insert into @temp
  28. select name,
  29. createdate,
  30. rnum
  31. from @table2
  32. where rnum = 1
  33. --select * from @temp
  34. while @i <= (select MAX(rnum) from @table2)
  35. begin
  36. insert into @temp
  37. select t2.name,
  38. min(t2.createdate),
  39. @i +1
  40. from @temp t1
  41. inner join @table2 t2
  42. on t1.name = t2.name
  43. and t2.createdate >= dateadd(hour,2,t1.createdate)
  44. where t1.rnum = @i
  45. group by t2.name
  46. set @i = @i + 1
  47. end
  48. ;with r
  49. as
  50. (
  51. select name,
  52. createdate
  53. from @temp
  54. group by name,
  55. createdate
  56. )
  57. select r.name,
  58. r.createdate,
  59. COUNT(1)
  60. from r
  61. inner join @table1 t
  62. on t.name = r.name
  63. and t.createdate >= r.createdate
  64. and t.createdate <DATEADD(HOUR,2,r.createdate)
  65. group by r.name,
  66. r.createdate

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


  1. declare @table1 table(name nvarchar,createdate smalldatetime)
  2. insert into @table1
  3. select 'a','2011-03-01 10:00:00'
  4. union all select 'a','2011-03-01 11:00:00'
  5. union all select 'a','2011-03-01 12:00:00'
  6. union all select 'a','2011-03-01 12:20:00'
  7. union all select 'b','2011-03-01 13:10:00'
  8. union all select 'b','2011-03-01 13:20:00'
  9. union all select 'b','2011-03-01 14:30:00'
  10. union all select 'b','2011-03-01 15:15:00'
  11. union all select 'b','2011-03-01 16:00:00'
  12. union all select 'b','2011-03-01 17:20:00'
  13. union all select 'b','2011-03-01 17:15:00'
  14. union all select 'b','2011-03-01 19:16:00'
  15. union all select 'b','2011-03-01 17:15:00'
  16. ;with a --按照name分区,同时按照createdate排序编号
  17. as
  18. (
  19. select name,
  20. createdate,
  21. ROW_NUMBER() over(partition by name
  22. order by createdate) as k1
  23. from @table1
  24. ),
  25. c --对于每个时间,找到大于这个时间2小时的时间中最小那个时间
  26. as
  27. (
  28. select a1.name,
  29. a1.createdate,
  30. a1.k1,
  31. MIN(a2.createdate) as nextCreatedate,
  32. MIN(a2.k1) as nextK1
  33. from a a1
  34. inner join a a2
  35. on a1.name = a2.name
  36. and a2.createdate >= DATEADD(hour,2,a1.createdate)
  37. group by a1.name,
  38. a1.createdate,
  39. a1.k1
  40. union all
  41. select a.name,null,null,a.createdate,1 --构造递归运行时需要的层级
  42. from a
  43. where k1 = 1
  44. ),
  45. w --递归查询
  46. as
  47. (
  48. select c.name,
  49. c.createdate,
  50. c.k1,
  51. c.nextCreatedate,
  52. c.nextK1,
  53. 1 as lev
  54. from c
  55. where createdate is null
  56. and k1 is null
  57. union all
  58. select c.name,
  59. c.createdate,
  60. c.k1,
  61. c.nextCreatedate,
  62. c.nextK1,
  63. lev + 1
  64. from W
  65. inner join c
  66. on w.name = c.name
  67. and w.nextCreatedate = c.createdate
  68. )
  69. SELECT distinct name,
  70. nextCreatedate,
  71. nextK1
  72. FROM w
发布了416 篇原创文章 · 获赞 135 · 访问量 94万+

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

  1. 利用Entity Framework修改指定字段中的值

    利用Entity Framework修改指定字段中的值一般我们编辑某些模型的时候会用到类似这样的代码: [HttpPost] public ActionResult Edit(Article mode ...

  2. sql语句把字段中的某个字符去掉

    sql语句把字段中的某个字符去掉 )),'http://demo.m-school.net','') 例如: )),'http://192.168.2.180','') )),'http://zpzx ...

  3. SQL Server为字段添加默认值

    SQL Server为字段添加默认值 if not exists ( select * from sys.columns as c join sys.objects as o on c.default ...

  4. sql查询某字段的相同值

    sql查询某字段的相同值: SELECT * FROM table WHERE col in (SELECT col FROM table GROUP BY col HAVING COUNT (col ...

  5. sql替换数据库字段中的字符

    UPDATE `table_name` SET `field_name` = replace (`field_name`,'from_str','to_str') WHERE ……说明:table_n ...

  6. 统计numpy数组中每个值出现的个数

    统计numpy数组中某一个值或某几个值出现的个数:sum(data==4) # 统计出现了几个cluster include0Cluster = sum(res == 0) include1Clust ...

  7. SQL(replace)替换字段中指定的字符

    语法:update 表名 set 字段名=REPLACE(字段名,'修改前的字符','修改后的字符') 例 Product商品表中Name 名字字段中描述中将'AAA' 修改成 'BBB' SQL语句 ...

  8. sql修改一个字段多个值

    UPDATE 表名 SET 修改的字段=REPLACE(修改的字段,'修改的值','新值');

  9. SQL Server提取字段中的所有数字

    今天公司项目中遇到了一个需求,要求提取用户电话号码字段中的所有电话信息. 由于该字段在项目最初设计中没有严格控制数据质量,导致用户在输入时包含了很多非电话的信息,如用户名字等(136 **** *** ...

随机推荐

  1. 23Flutter FloatingActionButton实现类似闲鱼App底部导航凸起按钮:

    /* 一.Flutter FloatingActionButton介绍 FloatingActionButton简称FAB,可以实现浮动按钮,也可以实现类型闲鱼app的底部凸起导航. child:子视 ...

  2. 25 Flutter仿京东商城项目 购物车页面布局

    加群452892873 下载对应25课文件,运行方法,建好项目,直接替换lib目录,在往pubspec.yaml添加上一下扩展. cupertino_icons: ^0.1.2 flutter_swi ...

  3. 不同操作系统上DNS客户端操作区别汇总

    结论:windows有DNS缓存,Linux默认无DNS缓存,只能依赖于安装其他软件. 一.不同操作系统的客户端的DNS缓存差别 1.windows 系统中dns 解析器会使用系统的dns缓存来提高d ...

  4. trylock方法

    synchronized 是不占用到手不罢休的,会一直试图占用下去. 与 synchronized 的钻牛角尖不一样,Lock接口还提供了一个trylock方法.trylock会在指定时间范围内试图占 ...

  5. PAT 甲级 1035 Password (20 分)(简单题)

    1035 Password (20 分)   To prepare for PAT, the judge sometimes has to generate random passwords for ...

  6. VL10B 采购订单转DN

    传入采购订单项目建交货单 FUNCTION zmmfmXXXX. *"------------------------------------------------------------ ...

  7. jQuery学习四——效果

    1.显示,隐藏: <!DOCTYPE html> <html> <head> <title>jquery事件</title> </he ...

  8. [CareerCup] Guards in a museum 博物馆的警卫

    A museum was represented by a square matrix that was filled with O, G, and W where O represented ope ...

  9. 基于Visual Studio Code搭建Vue开发环境

    安装node.js最新版 这里安装的是8.11.4版   image.png 更新npm至最新版 安装node.js后, npm默认版本为: 6.1.0   image.png 使用npm insta ...

  10. 实现一个Promise

    实现一个Promise promise特点 一个promise的当前状态只能是pending.fulfilled和rejected三种之一.状态改变只能是pending到fulfilled或者pend ...