【问题标题】:group by day with missing days缺天按天分组
【发布时间】:2018-07-08 00:40:35
【问题描述】:

我有一个 sql server 表,结构和数据如下:

Created              ,  keyword
'2017-10-03 19:18:00',  'test7'
'2017-10-07 01:06:00',  'test3'
'2017-10-07 15:19:00',  'test2'
'2017-10-07 21:39:00',  'test10'
'2017-10-08 00:36:00',  'test3'
'2017-10-08 01:26:00',  'test13'
'2017-10-08 01:33:00',  'test9'
'2017-10-08 08:23:00',  'test13'
'2017-10-08 09:35:00',  'test9'
'2017-10-08 12:38:00',  'test9'
'2017-10-08 15:07:00',  'test2'
'2017-10-10 05:09:00',  'test4'

我想运行一个计算活动的查询并按天分组,还考虑没有记录任何内容的天数,并将这些天的活动显示为零。因此,我想要一个返回以下结果集的查询:

'2017-10-03', 1
'2017-10-04', 0
'2017-10-05', 0
'2017-10-06', 0
'2017-10-07', 3
'2017-10-08', 7
'2017-10-09', 0
'2017-10-03', 1

我知道如何运行查询并按天数对其进行分组,但不知道如何计算没有记录的天数。由于我是 Sql 新手,如果有人能提供一个工作示例,我将不胜感激。提前致谢

【问题讨论】:

  • 最好的解决方案是创建一个表格日历,将您的表格加入此日历(日历表格 lrft 在 dt = dt 和 dt 之间加入您的表格 ...)并将结果分组。如果您没有日历表,则可以“即时”生成它
  • 从以前使用另一个 DBMS 的经验来看,我认为没有一个简单的解决方案。也许最好的办法是遍历查询的输出并手动填补空白。
  • 谢谢,你能举个例子吗?

标签: sql sql-server


【解决方案1】:
 declare @date int
 WITH CTE_DatesTable
AS
(
  SELECT CAST('20171003' as date) AS [date]
  UNION ALL
  SELECT   DATEADD(dd, 1, [date])
  FROM CTE_DatesTable
  WHERE DATEADD(dd, 1, [date]) <= '20171010'
)
SELECT [CalendarDate]=[date] into #DimDate   FROM CTE_DatesTable

OPTION (MAXRECURSION 0);

select * from #DimDate

这将创建一个日历表以与您当前的表连接以填补空白

【讨论】:

    【解决方案2】:

    当您没有日历表时,这是一个解决方案:

    select row_number() over(order by getdate()) - 1 as n
    into #nums
    from sys.columns cross join sys.columns c2;
    
    declare @t table(Created datetime, keyword varchar(100));
    insert into @t values
    ('2017-10-03 19:18:00',  'test7'),
    ('2017-10-07 01:06:00',  'test3'),
    ('2017-10-07 15:19:00',  'test2'),
    ('2017-10-07 21:39:00',  'test10'),
    ('2017-10-08 00:36:00',  'test3'),
    ('2017-10-08 01:26:00',  'test13'),
    ('2017-10-08 01:33:00',  'test9'),
    ('2017-10-08 08:23:00',  'test13'),
    ('2017-10-08 09:35:00',  'test9'),
    ('2017-10-08 12:38:00',  'test9'),
    ('2017-10-08 15:07:00',  'test2'),
    ('2017-10-10 05:09:00',  'test4')
    
    declare @min_dt date, @max_dt date;
    select @min_dt = min(Created), @max_dt = max(Created)
    from @t;
    
    with calendar as
    (
    select dateadd(day, n, @min_dt) as dt
    from #nums
    where dateadd(day, n, @min_dt) <= @max_dt
    )
    
    select c.dt, isnull(count(t.keyword), 0) as cnt
    from calendar c left join @t t
            on c.dt = cast(t.Created as date)
    group by c.dt; 
    

    在我的情况下,我没有表格日历,但我有带有数字 (Nums) 的固定表格,但如果您没有数字表格,您可以像我在 #nums 中所做的那样生成它(您应该限制生成的数字到一个合理的数字)

    【讨论】:

      【解决方案3】:

      试试这个:

      declare @startDate date = '2017-10-01'
      declare @endDate date = '2017-10-31'
      
      ;with cte as (
      select cast(@startDate as date) [dayOfYear]
      union all
      select DATEADD(day, 1, [dayOfYear]) from cte
      where [dayOfYear] < @endDate
      )
      
      select dayOfYear, SUM(case when Created is null then 0 else 1 end) from cte
      left join MY_TABLE [T] on cte.dayOfYear = CAST(T.Created as date)
      group by dayOfYear
      

      逻辑如下:

      获取@startDate@endDate 之间所有日期的表格(CTE - 我指定了十月的第一天和最后一天)。然后我们离开加入您的表格,当天数不匹配时,我们将相应的值定义为 0,否则为 1。然后每天对这些值求和就足够了。

      【讨论】:

      • 如果超过 100 行,则必须添加“OPTION (MAXRECURSION 200)”。这将防止:“语句终止。在语句完成之前,最大递归 100 已用尽”。
      猜你喜欢
      • 1970-01-01
      • 2014-05-16
      • 1970-01-01
      • 1970-01-01
      • 2012-04-26
      • 2021-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多