简体   繁体   English

在 SQL 中选择连续数字

[英]Select Consecutive Numbers in SQL

This feels simple, but I can't find an answer anywhere.这感觉很简单,但我在任何地方都找不到答案。
I'm trying to run a query by time of day for each hour.我正在尝试按一天中的每个小时运行一个查询。 So I'm doing a Group By on the hour part, but not all hours have data, so there are some gaps.所以我在小时部分进行Group By ,但并非所有小时都有数据,因此存在一些差距。 I'd like to display every hour, regardless of whether or not there's data.我想每小时显示一次,无论是否有数据。

Here's a sample query:这是一个示例查询:

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))

My thought was to Join to a table that already had numbers 1 through 24 so that the incoming data would get put in it's place.我的想法是加入一个已经有数字 1 到 24 的表,以便传入的数据放在它的位置。

Can I do this with a CTE?我可以用 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

###Here's a sample Query From Stack Exchange Data Explorer ###Here 是来自 Stack Exchange 数据资源管理器的示例查询

You can use a recursive query to build up a table of whatever numbers you want.您可以使用递归查询来构建您想要的任何数字的表。 Here we stop at 24. Then left join that to your comments to ensure every hour is represented.在这里,我们在 24 点停止。然后将其加入您的评论中,以确保每个小时都有代表。 You can turn these into times easily if you wanted.如果您愿意,您可以轻松地将这些时间转换为时间。 I also changed your use of hour as a column name as it is a keyword.我还更改了您使用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

You can use a table value constructor:您可以使用表值构造函数:

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

You can also use a permanent auxilliary numbers table.您还可以使用永久辅助号码表。

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

Use a recursive CTE to generate the hours:使用递归 CTE 生成小时数:

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

Then your full query needs a left outer join :然后你的完整查询需要一个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;

Below is demo without using recursive CTE for sql-server下面是 sql-server 不使用recursive CTE演示

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;

online demo link : consecutive number query demo - Stack Exchange Data Explorer 在线演示链接:连续号码查询演示

As a more general abstraction of this issue, you can create consecutive numbers Brad and Gordon have suggested with a recursive CTE like this:作为此问题的更一般抽象,您可以创建 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)

As a note, if you plan to go over 100 numbers, you'll need to add OPTION (MaxRecursion 0) to the end of your query to prevent the error The maximum recursion 100 has been exhausted before statement completion请注意,如果您计划超过 100 个数字,则需要在查询末尾添加OPTION (MaxRecursion 0)以防止出现错误语句完成前最大递归 100 已用尽

This technique can commonly be seen when populating or using a Tally Table in TSQL在 TSQL 中填充或使用Tally 表时,通常可以看到这种技术

The basic idea is correct, but you will want to perform a left join instead of a standard join.基本思想是正确的,但您需要执行左连接而不是标准连接。 The reason for the left join is because you want the answers from the left-hand side.左连接的原因是因为您想要来自左侧的答案。

With respect to how to create the original hours table, you can either directly create it with something like:关于如何创建原始小时表,您可以直接使用以下内容创建它:

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

or, you can create a permanent table populated with these values.或者,您可以创建一个填充了这些值的永久表。 (I do not recall immediately on SqlServer if there is a better way to do this, or if selecting a value but not from a table is allowed. On Oracle, you could select from the built-in table 'dual' which is a table containing a single row). (我不记得在 SqlServer 上是否有更好的方法来做到这一点,或者是否允许选择一个值而不是从表中选择。在 Oracle 上,您可以从内置表“dual”中进行选择,它是一个表包含单行)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM