在 SQL 中选择连续数字

问题描述 投票:0回答:7

这感觉很简单,但我在任何地方都找不到答案。
我正在尝试按一天中的每个小时运行查询。 所以我在小时部分做了一个

Group By
,但并不是所有小时都有数据,所以有一些间隙。 我想每小时显示一次,无论是否有数据。

这是一个示例查询:

SELECT DATEPART(HOUR, DATEADD(HH,-5, CreationDate)) As Hour,
       COUNT(*) AS Count
FROM Comments
WHERE UserId = ##UserId##
GROUP BY DATEPART(HOUR, DATEADD(HH,-5, CreationDate))

我的想法是加入一个已经有数字 1 到 24 的表,以便传入的数据可以放在它的位置。

我可以用 CTE 做到这一点吗?

WITH Hours AS (
   SELECT i As Hour    --Not Sure on this
   FROM [1,2,3...24]), --Not Sure on this
CommentTimes AS (
   SELECT DATEPART(HOUR, DATEADD(HH,-5, CreationDate)) AS Hour,
          COUNT(*) AS Count
   FROM Comments
   WHERE UserId = ##UserId##
   GROUP BY DATEPART(HOUR, DATEADD(HH,-5, CreationDate))
)
SELECT h.Hour, c.Count
FROM Hours h
JOIN CommentTimes c ON h.Hour = c.Hour

###这是一个示例 从 Stack Exchange 数据资源管理器查询

sql sql-server common-table-expression recursive-query
7个回答
3
投票

您可以使用递归查询来构建包含您想要的任何数字的表。我们在 24 点停止。然后将其添加到您的评论中,以确保每个小时都得到体现。如果您愿意,您可以轻松地将这些变成时间。我还更改了您对

hour
作为列名称的使用,因为它是关键字。

;with dayHours as (
    select 1 as HourValue
    union all select hourvalue + 1
    from dayHours
    where hourValue < 24
)
,
CommentTimes As (
       SELECT DATEPART(HOUR, DATEADD(HH,-5, CreationDate)) As HourValue,
              COUNT(*) AS Count
       FROM Comments
       WHERE UserId = ##UserId##
       GROUP BY DATEPART(HOUR, DATEADD(HH,-5, CreationDate)))
SELECT h.Hour, c.Count
FROM dayHours h
left JOIN CommentTimes c ON h.HourValue = c.HourValue

1
投票

您可以使用表值构造函数:

with hours as (
    SELECT hr
    FROM  (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)) AS b(hr) 
)
etc..

您还可以使用永久辅助号码表。

http://dataeducation.com/you-require-a-numbers-table/


1
投票

使用递归 CTE 生成小时数:

with hours as (
      select 1 as hour
      union all
      select hour + 1
      from hours
      where hour < 24
     )
. . .

那么您的完整查询需要一个

left outer join
:

with hours as (
      select 1 as hour
      union all
      select hour + 1
      from hours
      where hour < 24
     )
     CommentTimes As (
      SELECT DATEPART(HOUR, DATEADD(HH,-5, CreationDate)) As Hour,
             COUNT(*) AS Count
      FROM Comments
      WHERE UserId = ##UserId##
      GROUP BY DATEPART(HOUR, DATEADD(HH,-5, CreationDate))
     )
SELECT h.Hour, c.Count
FROM Hours h LEFT OUTER JOIN
     CommentTimes c
     ON h.Hour = c.Hour;

1
投票

作为此问题的更一般抽象,您可以使用 Brad 和 Gordon 建议的递归 CTE 创建连续的数字,如下所示:

WITH Numbers AS (
    SELECT 1 AS Number
    UNION ALL SELECT Number + 1
    FROM Numbers
    WHERE Number < 1000
)
SELECT * FROM Numbers
OPTION (MaxRecursion 0)

请注意,如果您计划超过 100 个数字,则需要在查询末尾添加

OPTION (MaxRecursion 0)
以防止出现错误 最大递归 100 在语句完成之前已用尽

在 TSQL 中填充或使用 Tally 表时,通常可以看到这种技术


1
投票

下面是不使用

recursive CTE
进行 sql-server 的演示

select h.hour ,c.count
from (
    select top 24 number + 1 as hour from master..spt_values 
    where type = 'P'
) h 
left join (
    select datepart(hour, creationdate) as hour,count(1) count
    from comments
    where userid = '9131476'
    group by datepart(hour, creationdate)
) c on h.hour = c.hour
order by h.hour;

在线演示链接:连续号码查询演示 - Stack Exchange Data Explorer


0
投票

基本思想是正确的,但您需要执行左连接而不是标准连接。 左连接的原因是因为您想要从左侧得到答案。

关于如何创建原始小时表,您可以直接使用以下内容创建:

SELECT 1 as hour
UNION ALL
SELECT 2 as hour
  ...
UNION ALL
SELECT 24 as hour

或者,您可以创建一个填充这些值的永久表。 (我不记得在 SqlServer 上是否有更好的方法来执行此操作,或者是否允许选择一个值但不能从表中选择。在 Oracle 上,您可以从内置表“dual”中选择,该表是一个表包含单行)。


0
投票

更新 SQL Server 2022+

您现在可以在 SQL Server 2022+ 中使用

GENERATE_SERIES
执行此操作,语法如下:
GENERATE_SERIES(<start>, <stop> [, <step>])

SELECT value FROM GENERATE_SERIES(1, 24);

输出

value
-----
1
2
...
23
24

SQL Fiddle 中的演示

进一步阅读SQL Server 2022 中我最喜欢的 T-SQL 增强功能

© www.soinside.com 2019 - 2024. All rights reserved.