【问题标题】:Get data in groups of "Week of..." when dates may be missing当日期可能丢失时,以“...的周”组获取数据
【发布时间】:2018-03-04 21:39:07
【问题描述】:

我在一个带日期的表中有数据,并且想按“周”(例如“2017-05-01 周”)计算行数,其中结果具有周的日期(从星期一开始)和匹配行数——即使那周没有行。 (这都将在一个日期范围内。)

我可以通过在DATEPART(wk, D)(其中D 是日期列)上分组来轻松地将事情划分为几周,但我正在努力解决:

  1. 如何获取“星期”日期并填写,以及

  2. 如何在数据中没有匹配行的情况下连续一周

这里按周分组:

SET DATEFORMAT ymd;
SET DATEFIRST 1; -- Monday is first day of week

DECLARE @startDate DATETIME = '2017-05-01';
DECLARE @endDate DATETIME = '2017-07-01';

SELECT      DATEPART(wk, D) AS [Week Number], COUNT(*) AS [Count]
FROM        #temp
GROUP BY    DATEPART(wk, D)
ORDER BY    DATEPART(wk, D);

这给了我:

+−−−−−−−−−−−−−−+−−−−−−−−−+ |周数 |计数 | +−−−−−−−−−−−−−−+−−−−−−−−−+ | 19 | 5 | | 20 | 19 | | 22 | 8 | | 23 | 10 | | 24 | 5 | | 26 | 4 | +−−−−−−−−−−−−−−+−−−−−−−−−+

但理想情况下我想要:

+−−−−−−−−−−−−−+−−−−−−−−+ |周 |计数 | +−−−−−−−−−−−−−+−−−−−−−−+ | 2017-05-01 | 5 | | 2017-05-08 | 19 | | 2017-05-15 | 0 | | 2017-05-22 | 8 | | 2017-05-29 | 10 | | 2017-06-05 | 5 | | 2017-06-12 | 0 | | 2017-06-19 | 4 | | 2017-06-26 | 0 | +−−−−−−−−−−−−−+−−−−−−−−+

我该怎么做?


设置测试信息:

SET DATEFIRST 1;
SET DATEFORMAT ymd;

CREATE TABLE #temp (
    D DATETIME
);
GO

INSERT INTO #temp (D)
VALUES      -- Week of 2017-05-01 (#19)
            ('2017-05-01'),('2017-05-01'),('2017-05-01'),
            ('2017-05-06'),('2017-05-06'),
            -- Week of 2017-05-08 (#20) - note no data actually on the 8th
            ('2017-05-10'),
            ('2017-05-11'),('2017-05-11'),('2017-05-11'),('2017-05-11'),('2017-05-11'),('2017-05-11'),
            ('2017-05-12'),('2017-05-12'),('2017-05-12'),('2017-05-12'),
            ('2017-05-13'),('2017-05-13'),('2017-05-13'),('2017-05-13'),('2017-05-13'),('2017-05-13'),('2017-05-13'),
            ('2017-05-14'),
            -- Week of 2017-05-15 (#21)
            -- (note we have no data for this week)
            -- Week of 2017-05-22 (#22)
            ('2017-05-22'),('2017-05-22'),('2017-05-22'),
            ('2017-05-23'),('2017-05-23'),('2017-05-23'),('2017-05-23'),('2017-05-23'),
            -- Week of 2017-05-29 (#23)
            ('2017-05-29'),('2017-05-29'),('2017-05-29'),
            ('2017-06-02'),('2017-06-02'),
            ('2017-06-03'),
            ('2017-06-04'),('2017-06-04'),('2017-06-04'),('2017-06-04'),
            -- Week of 2017-06-05 (#24) - note no data actually on the 5th
            ('2017-06-08'),('2017-06-08'),('2017-06-08'),
            ('2017-06-11'),('2017-06-11'),
            -- Week of 2017-06-12 (#25)
            -- (note we have no data for this week)
            -- Week of 2017-06-19 (#26)
            ('2017-06-19'),('2017-06-19'),('2017-06-19'),
            ('2017-06-20');
GO

【问题讨论】:

    标签: sql-server tsql


    【解决方案1】:

    为此,您必须生成一个包含星期一日期及其周数的表格或 CTE(如 this answer 所示,为我们需要在下面执行的操作稍作修改),然后是 LEFT JOINOUTER APPLY使用周数将您的数据按周分组:

    SET DATEFORMAT ymd;
    SET DATEFIRST 1;
    
    DECLARE @startDate DATETIME = '2017-05-01';
    DECLARE @endDate DATETIME = '2017-07-01';
    
    ;WITH Mondays AS (
        SELECT  @startDate AS D, DATEPART(WK, @startDate) AS W
        UNION ALL
        SELECT  DATEADD(DAY, 7, D), DATEPART(WK, DATEADD(DAY, 7, D))
        FROM    Mondays m
        WHERE   DATEADD(DAY, 7, D) < @endDate
    )
    SELECT      LEFT(CONVERT(NVARCHAR(MAX), Mondays.D, 120), 10) AS [Week Of], d.Count
    FROM        Mondays
    OUTER APPLY (
                SELECT  COUNT(*) AS [Count]
                FROM    #temp
                WHERE   DATEPART(WK, D) = W
                AND     D >= @startDate
                AND     D < @endDate
    ) d
    ORDER BY    Mondays.D;
    

    对此有两个注意事项:

    1. 我假设我们可以确保 @startDate 是星期一,这很容易在查询之外完成,或者如果需要可以通过 T-SQL 中的简单循环完成(备份到 WEEKPART(WEEKDAY, @startDate)1)。 (或者最坏的情况,我们可以生成所有日期,然后使用 WEEKPART(WEEKDAY, ...) 过滤它们。)

    2. 我假设日期范围总是一年或更短;否则,我们会有重复的周数。如果日期范围可能超过一年,请将周数与我们仅使用上述周数的任何地方的年份结合起来(例如,DATEPART(YEAR, D) * 100 + DATEPART(wk, D))。

    【讨论】:

      【解决方案2】:

      你可以用这个。

      SET DATEFORMAT ymd;
      SET DATEFIRST 1; -- Monday is first day of week
      
      DECLARE @startDate DATETIME = '2017-05-01';
      DECLARE @endDate DATETIME = '2017-07-01';
      
      ;WITH OrgResult AS ( -- Grouping result with missing week. Answer of the first question
          SELECT 
              DATEADD(DAY, 1 - DATEPART (WEEKDAY, D), D) [Week] -- Fist Day Of the Week
              , COUNT(*) [Count]
          FROM #temp
              WHERE D BETWEEN @startDate AND @endDate
          GROUP BY 
              DATEADD(DAY, 1 - DATEPART (WEEKDAY, D), D)
      )
      , Result AS -- Adds only missing weeks. Answer of the second question
      (
          SELECT * FROM OrgResult
          UNION ALL
          SELECT DATEADD( DAY, 7, R.[Week] ), 0 [Count] 
          FROM Result R 
          WHERE NOT EXISTS( SELECT * FROM OrgResult O WHERE [Week] = DATEADD( DAY, 7, R.[Week] ) )
                  AND DATEADD( DAY, 7, R.[Week] ) <= @endDate
      )
      SELECT * FROM Result
      ORDER BY [Week]
      

      结果:

      Week        Count
      ----------- -----------
      2017-05-01  5
      2017-05-08  19
      2017-05-15  0
      2017-05-22  8
      2017-05-29  10
      2017-06-05  5
      2017-06-12  0
      2017-06-19  4
      2017-06-26  0
      

      【讨论】:

        【解决方案3】:

        这是另一种方法。我将其包含在内,因为它比递归 CTE 解决方案生成的读取更少,而且速度会快很多

        WITH E(N) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))x(x)),
        iTally(N) AS 
        (
          SELECT TOP (((DATEDIFF(day,@startdate, @endDate))/7)+1)
            (ROW_NUMBER() OVER (ORDER BY (SELECT 1))-1)
          FROM E a, E b, E c
        )
        SELECT WeekOf = DATEADD(WEEK,N,@startDate), [count] = COUNT(t.D)
        FROM iTally i
        LEFT JOIN #temp t ON t.D >= DATEADD(WEEK,N,@startDate) AND t.D < DATEADD(WEEK,N+1,@startDate)
        GROUP BY DATEADD(WEEK,N,@startDate)
        ORDER BY DATEADD(WEEK,N,@startDate); -- not required
        

        结果:

        WeekOf     count
        ---------- -----------
        2017-05-01 5
        2017-05-08 19
        2017-05-15 0
        2017-05-22 8
        2017-05-29 10
        2017-06-05 5
        2017-06-12 0
        2017-06-19 4
        2017-06-26 0
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-07-21
          • 2022-06-29
          • 2021-10-29
          • 1970-01-01
          • 1970-01-01
          • 2021-04-04
          • 1970-01-01
          • 2021-03-30
          相关资源
          最近更新 更多