p.c*_*ell 53 sql t-sql database sql-server sql-server-2005
考虑需要创建日期的结果集.我们有开始和结束日期,我们想要生成两者之间的日期列表.
DECLARE @Start datetime
,@End datetime
DECLARE @AllDates table
(@Date datetime)
SELECT @Start = 'Mar 1 2009', @End = 'Aug 1 2009'
--need to fill @AllDates. Trying to avoid looping.
-- Surely if a better solution exists.
Run Code Online (Sandbox Code Playgroud)
考虑使用WHILE循环的当前实现:
DECLARE @dCounter datetime
SELECT @dCounter = @Start
WHILE @dCounter <= @End
BEGIN
INSERT INTO @AllDates VALUES (@dCounter)
SELECT @dCounter=@dCounter+1
END
Run Code Online (Sandbox Code Playgroud)
问题:如何使用T-SQL创建一组在用户定义范围内的日期?假设SQL 2005+.如果您的答案是使用SQL 2008功能,请标记为.
dev*_*vio 50
如果您的日期不超过2047天:
declare @dt datetime, @dtEnd datetime
set @dt = getdate()
set @dtEnd = dateadd(day, 100, @dt)
select dateadd(day, number, @dt)
from
(select number from master.dbo.spt_values
where [type] = 'P'
) n
where dateadd(day, number, @dt) < @dtEnd
Run Code Online (Sandbox Code Playgroud)
在多次提出要求后,我更新了我的答案.为什么?
原始答案包含子查询
select distinct number from master.dbo.spt_values
where name is null
Run Code Online (Sandbox Code Playgroud)
这与我在SQL Server 2008,2012和2016上测试它们提供了相同的结果.
但是,当我在查询时尝试分析MSSQL内部的代码时spt_values,我发现这些SELECT语句总是包含该子句WHERE [type]='[magic code]'.
因此我决定虽然查询返回正确的结果,但它会出于错误的原因提供正确的结果:
有可能是SQL Server的未来版本,其定义了不同的[type]值,其还具有NULL作为值[name],的0-2047的范围内,或甚至不连续的,外面在这种情况下,结果将是完全错误的.
OMG*_*ies 42
以下使用递归CTE(SQL Server 2005+):
WITH dates AS (
SELECT CAST('2009-01-01' AS DATETIME) 'date'
UNION ALL
SELECT DATEADD(dd, 1, t.date)
FROM dates t
WHERE DATEADD(dd, 1, t.date) <= '2009-02-01')
SELECT ...
FROM TABLE t
JOIN dates d ON d.date = t.date --etc.
Run Code Online (Sandbox Code Playgroud)
@KM 的回答首先创建一个数字表,并使用它来选择日期范围。要在没有临时数字表的情况下执行相同操作:
DECLARE @Start datetime
,@End datetime
DECLARE @AllDates table
(Date datetime)
SELECT @Start = 'Mar 1 2009', @End = 'Aug 1 2009';
WITH Nbrs_3( n ) AS ( SELECT 1 UNION SELECT 0 ),
Nbrs_2( n ) AS ( SELECT 1 FROM Nbrs_3 n1 CROSS JOIN Nbrs_3 n2 ),
Nbrs_1( n ) AS ( SELECT 1 FROM Nbrs_2 n1 CROSS JOIN Nbrs_2 n2 ),
Nbrs_0( n ) AS ( SELECT 1 FROM Nbrs_1 n1 CROSS JOIN Nbrs_1 n2 ),
Nbrs ( n ) AS ( SELECT 1 FROM Nbrs_0 n1 CROSS JOIN Nbrs_0 n2 )
SELECT @Start+n-1 as Date
FROM ( SELECT ROW_NUMBER() OVER (ORDER BY n)
FROM Nbrs ) D ( n )
WHERE n <= DATEDIFF(day,@Start,@End)+1 ;
Run Code Online (Sandbox Code Playgroud)
当然,测试一下,如果您经常这样做,永久表的性能可能会更好。
上面的查询是从修改后的版本本文,其中讨论了生成序列,并给出许多可能的方法。我喜欢这个,因为它不创建临时表,并且不受sys.objects表中元素数量的限制。
该解决方案基于 MySQL 相同问题的精彩答案。它在 MSSQL 上的性能也非常好。/sf/answers/151044351/
select DateGenerator.DateValue from (
select DATEADD(day, - (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a)), CONVERT(DATE, GETDATE()) ) as DateValue
from (select a.a from (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) as a(a)) as a
cross join (select b.a from (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) as b(a)) as b
cross join (select c.a from (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) as c(a)) as c
cross join (select d.a from (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) as d(a)) as d
) DateGenerator
WHERE DateGenerator.DateValue BETWEEN 'Mar 1 2009' AND 'Aug 1 2009'
ORDER BY DateGenerator.DateValue ASC
Run Code Online (Sandbox Code Playgroud)
仅适用于过去的日期,适用于未来的日期更改 DATEADD 函数中的减号。查询仅适用于 SQL Server 2008+,但也可以通过用联合替换“从值中选择”结构来重写 2005。
要使此方法起作用,您需要执行以下一次性表设置:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Run Code Online (Sandbox Code Playgroud)
设置Numbers表后,使用以下查询:
SELECT
@Start+Number-1
FROM Numbers
WHERE Number<=DATEDIFF(day,@Start,@End)+1
Run Code Online (Sandbox Code Playgroud)
抓住他们做:
DECLARE @Start datetime
,@End datetime
DECLARE @AllDates table
(Date datetime)
SELECT @Start = 'Mar 1 2009', @End = 'Aug 1 2009'
INSERT INTO @AllDates
(Date)
SELECT
@Start+Number-1
FROM Numbers
WHERE Number<=DATEDIFF(day,@Start,@End)+1
SELECT * FROM @AllDates
Run Code Online (Sandbox Code Playgroud)
输出:
Date
-----------------------
2009-03-01 00:00:00.000
2009-03-02 00:00:00.000
2009-03-03 00:00:00.000
2009-03-04 00:00:00.000
2009-03-05 00:00:00.000
2009-03-06 00:00:00.000
2009-03-07 00:00:00.000
2009-03-08 00:00:00.000
2009-03-09 00:00:00.000
2009-03-10 00:00:00.000
....
2009-07-25 00:00:00.000
2009-07-26 00:00:00.000
2009-07-27 00:00:00.000
2009-07-28 00:00:00.000
2009-07-29 00:00:00.000
2009-07-30 00:00:00.000
2009-07-31 00:00:00.000
2009-08-01 00:00:00.000
(154 row(s) affected)
Run Code Online (Sandbox Code Playgroud)