我有一张带有日期记录的表格。我想要一个查询,显示每个日期的记录数。
不难,只是一个GROUP BY,对吗?肯定。
但我还要显示没有记录的日子。在这种情况下,我需要将连接对连续日期的单列表进行连接,因为GROUP BY不是将显示一个不存在的日期。
有更好的方法吗?或者我是否必须创建一个填充日期表,以便我可以加入它?
答案 0 :(得分:3)
构建日期表可能最简单
例如,如果您想要从2009年1月1日到今天的所有日期,您可以执行以下操作:
DECLARE @start AS DATETIME,
@end AS DATETIME
SELECT @start = '2009-01-01',
@end = getdate()
DECLARE @dates TABLE (
dt DATETIME
)
WHILE (@start < @end)
BEGIN
INSERT @dates
SELECT @start
SELECT @start = DATEADD(day, 1, @start)
END
SELECT *
FROM @dates
@dates将从@start到@end每天都有一条记录。
答案 1 :(得分:2)
如果你真的不想要一张桌子。
在Oracle上,您可以执行以下操作:
select D.D, count(T.CompareDate)
from (select to_date('2009-07-01', 'YYYY-MM-DD') + LEVEL - 1 as D
from dual
connect by LEVEL <= 30) D
left outer join T ON T.CompareDate = D.D
group by D.D
如果to_date中的日期是开始日期,那么您可以针对结果中所需天数的级别进行测试。
在SQL Server 2005/2008上:
; with X1 (X) as
(select 1 union all select 1)
, X2 (X) as
(select 1 from X1 a cross join X1 b)
, X4 (X) as
(select 1 from X2 a cross join X2 b)
, X8 (X) as
(select 1 from X4 a cross join X4 b)
, X16 (X) as
(select 1 from X8 a cross join X8 b)
, NUM (N) as
(select row_number() over (order by X) from X16)
, D (D) as
(select dateadd(day, N-1, '20090701')
from NUM
where NUM.N <= 30)
select D.D, count(T.CompareDate)
from D
left outer join T on T.CompareDate = D.D
group by D.D
with
子句用于构建日期。 dateadd
中指定的日期是开始日期,天数在NUM.N <=30
进行测试。您还可以针对结束日期where dateadd(day, N-1, StartDate) <= EndDate
进行测试。
我建议封装with子句以将范围创建为内联表值函数。
数字生成基于我从Itzik Ben-gan看到的代码。每个X给出的行数等于X数的2的幂(X1 = 2行,X8 = 256行)。如果您需要更多65,536,则需要添加更多交叉连接。如果你永远不需要超过256,那么你可以消除X16。
此外,如果你有一个数字表,你可以使用它和日期arithemetic来生成你需要的日期。
答案 2 :(得分:1)
这将是最简单的解决方案。一旦你有了这张桌子,我想你会发现它有很多用处。关键是要有一个年度工作,它会为它添加新的日期,否则它将与系统中的当前日期不同步。并且不要忘记将其编入索引,因为您将加入它。
或者您可以在运行时将其填充到临时表或表变量中,但在运行时可能会耗费更多时间,具体取决于每次运行所需的日期范围。如果您只想寻找上个月的日期,这可能是要走的路,如果您需要最近5年的价值,我会选择预先填好的日期表。
答案 3 :(得分:1)
使用SQL Server 2005可以轻松实现 - 您可以使用此日期/时间间隔功能:
的工作方式如下:
-- Hourly blocks
select
dr.startdate
from
fn_DateRange('12/14/2008 08:00:00', '12/14/2008 12:00:00', '01:00:00') dr
go
startdate
-----------------------
2008-12-14 08:00:00.000
2008-12-14 09:00:00.000
2008-12-14 10:00:00.000
2008-12-14 11:00:00.000
2008-12-14 12:00:00.000
-- Daily blocks
select
dr.startdate
from
fn_DateRange('12/14/2008', '12/18/2008', 1) dr
go
startdate
-----------------------
2008-12-14 00:00:00.000
2008-12-15 00:00:00.000
2008-12-16 00:00:00.000
2008-12-17 00:00:00.000
2008-12-18 00:00:00.000
-- Weekly blocks
select
dr.startdate
from
fn_DateRange('12/14/2008', '01/14/2009', 7) dr
go
startdate
-----------------------
2008-12-14 00:00:00.000
2008-12-21 00:00:00.000
2008-12-28 00:00:00.000
2009-01-04 00:00:00.000
2009-01-11 00:00:00.000
非常方便加入您的查询...
答案 4 :(得分:0)
Jon使用交叉连接的解决方案的一个有趣变体:
DECLARE @BaseDate DATETIME
Select @BaseDate = getdate() - 365
DECLARE @digits TABLE (d char(1))
DECLARE @i int
SET @i = 0
WHILE (@i < 10) BEGIN
INSERT @digits SELECT cast(@i as char(1))
Select @i = @i +1
END
SELECT DATEADD(d, cast(d1.d + d2.d + d3.d as int), @BaseDate)
FROM @digits d1 CROSS JOIN @digits d2 CROSS JOIN @digits d3
WHERE cast(d1.d + d2.d + d3.d as int) < 365
ORDER BY d1.d, d2.d, d3.d