当日期可能丢失时,以“周......”为一组获取数据

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

我有一个包含日期的表中的数据,并希望按“周”计算行数(例如,“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);

这给了我:

+−−−−−−−−−−−−−+−−−−−−−+
| Week Number | Count |
+−−−−−−−−−−−−−+−−−−−−−+
| 19          |     5 |
| 20          |    19 |
| 22          |     8 |
| 23          |    10 |
| 24          |     5 |
| 26          |     4 |
+−−−−−−−−−−−−−+−−−−−−−+

但理想情况下我想:

+−−−−−−−−−−−−+−−−−−−−+
| 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 |
+−−−−−−−−−−−−+−−−−−−−+

我怎样才能做到这一点?


设置测试信息:

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
3个回答
3
投票

要做到这一点,你必须生成一个表格或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))相结合。

1
投票

你可以用它。

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

0
投票

这是另一种方法。我把它包括在内,因为它会产生比递归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
© www.soinside.com 2019 - 2024. All rights reserved.