Sql server 当日期可能缺失时,以“周…”分组获取数据
我在一个带有日期的表格中有数据,希望按周计算行数,例如2017-05-01的一周,其中结果是星期一开始的一周日期和匹配行数,即使该周没有行。这些都将在一个日期范围内 通过对DATEPARTwk进行分组,我可以很容易地将事情划分为几周,其中D是日期列,但我很难做到: 如何获取日期和填充的周,以及 如何在数据中没有匹配行的情况下保留一周的行 以下是按周分组:Sql server 当日期可能缺失时,以“周…”分组获取数据,sql-server,tsql,Sql Server,Tsql,我在一个带有日期的表格中有数据,希望按周计算行数,例如2017-05-01的一周,其中结果是星期一开始的一周日期和匹配行数,即使该周没有行。这些都将在一个日期范围内 通过对DATEPARTwk进行分组,我可以很容易地将事情划分为几周,其中D是日期列,但我很难做到: 如何获取日期和填充的周,以及 如何在数据中没有匹配行的情况下保留一周的行 以下是按周分组: SET DATEFORMAT ymd; SET DATEFIRST 1; -- Monday is first day of week DE
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 |
+−−−−−−−−−−−−−+−−−−−−−+
为此,您必须生成一个表或CTE,其中包含星期一日期及其周数,如中所示,根据下面需要进行的操作进行了轻微修改,然后使用周数将数据按周分组,左连接或外应用该表或CTE:
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是星期一,这在查询之外很容易完成,或者如果需要备份到WEEKPARTWEEKDAY,@startDate是1,可以使用T-SQL中的简单循环完成。或者最坏的情况是,我们可以生成所有日期,然后用WEEKPARTWEEKDAY过滤它们
我假设日期范围总是一年或更短;否则,我们会有重复的周数。如果日期范围可能超过一年,请将周数与年数组合在一起,我们只使用上面的周数,例如DATEPARTYEAR,D*100+DATEPARTwk,D
为此,您必须生成一个表或CTE,其中包含星期一日期及其周数,如中所示,根据下面需要进行的操作进行了轻微修改,然后使用周数将数据按周分组,左连接或外应用该表或CTE:
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是星期一,这在查询之外很容易完成,或者如果需要备份到WEEKPARTWEEKDAY,@startDate是1,可以使用T-SQL中的简单循环完成。或者最坏的情况是,我们可以生成所有日期,然后用WEEKPARTWEEKDAY过滤它们
我假设日期范围总是一年或更短;否则,我们会有重复的周数。如果日期范围可能超过一年,请将周数与年数组合在一起,我们只使用上面的周数,例如DATEPARTYEAR,D*100+DATEPARTwk,D
这是另一种方法。我包括这一点,因为它将生成比递归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
这是另一种方法。我包括这一点,因为它将生成比递归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
你可以用这个
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]
你可以用这个
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