我有一个包含日期的表中的数据,并希望按“周”计算行数(例如,“2017-05-01周”),其中结果包含周的日期(从星期一开始)和计数匹配行 - 即使该周没有行。 (这都将在日期范围内。)
我可以通过分组DATEPART(wk, D)
(其中D
是日期列)将事情分成几周,但我正在努力:
这是按周分组:
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
要做到这一点,你必须生成一个表格或CTE,其中包含星期一日期及其周数(如this answer所示,稍为我们需要在下面进行修改),然后是LEFT JOIN
或OUTER 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;
关于此的两点说明:
@startDate
是星期一,这很容易在查询之外完成,或者如果需要可以在T-SQL中使用简单的循环完成(备份直到WEEKPART(WEEKDAY, @startDate)
是1
)。 (或者最坏的情况我们可以生成所有日期,然后用WEEKPART(WEEKDAY, ...)
过滤它们。)DATEPART(YEAR, D) * 100 + DATEPART(wk, D)
)相结合。你可以用它。
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
这是另一种方法。我把它包括在内,因为它会产生比递归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