正如已经给出的许多精彩答案中所述(或至少暗示),一旦你有一组数字可供使用,这个问题就很容易解决。
注意:以下是 T-SQL,但它只是我对这里和整个 Internet 上已经提到的一般概念的特定实现。将代码转换为您选择的方言应该相对简单。
如何?考虑这个查询:
SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;
上面产生的日期范围是 1/22/0001 - 1/27/0001,非常简单。上述查询中有两条关键信息:0001-01-22 的开始日期和5 的偏移量。如果我们结合这两条信息,那么我们显然有我们的结束日期。因此,给定两个日期,生成一个范围可以这样分解:
-
找出两个给定日期之间的差异(偏移量),很简单:
-- Returns 125
SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))
在此处使用ABS() 可确保日期顺序无关紧要。
-
生成一组有限的数字,也很简单:
-- Returns the numbers 0-2
SELECT N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1
FROM(SELECT 'A' AS S UNION ALL SELECT 'A' UNION ALL SELECT 'A')
请注意,我们实际上并不关心我们在此处选择 FROM 的内容。我们只需要一个集合来计算其中的行数。我个人使用 TVF,有些使用 CTE,有些使用数字表,你明白了。我提倡使用您也理解的最高效的解决方案。
结合这两种方法将解决我们的问题:
DECLARE @date1 DATE = '9001-11-21';
DECLARE @date2 DATE = '9001-11-23';
SELECT D = DATEADD(d, N, @date1)
FROM (
SELECT N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1
FROM (SELECT 'A' AS S UNION ALL SELECT 'A' UNION ALL SELECT 'A') S
) Numbers
WHERE N <= ABS(DATEDIFF(d, @date1, @date2));
上面的例子是糟糕的代码,但演示了一切是如何结合在一起的。
更多乐趣
我经常需要做这种事情,所以我将逻辑封装到两个 TVF 中。第一个生成一个数字范围,第二个使用此功能生成一个日期范围。数学是为了确保输入顺序无关紧要,因为我想使用GenerateRangeSmallInt 中可用的全部数字。
以下函数需要大约 16 毫秒的 CPU 时间来返回 65536 个日期的最大范围。
CREATE FUNCTION dbo.GenerateRangeDate (
@date1 DATE,
@date2 DATE
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN (
SELECT D = DATEADD(d, N + 32768, CASE WHEN @date1 <= @date2 THEN @date1 ELSE @date2 END)
FROM dbo.GenerateRangeSmallInt(-32768, ABS(DATEDIFF(d, @date1, @date2)) - 32768)
);
GO
CREATE FUNCTION dbo.GenerateRangeSmallInt (
@num1 SMALLINT = -32768
, @num2 SMALLINT = 32767
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN (
WITH Numbers(N) AS (
SELECT N FROM(VALUES
(1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 16
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 32
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 48
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 64
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 80
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 96
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 112
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 128
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 144
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 160
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 176
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 192
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 208
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 224
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 240
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 256
) V (N)
)
SELECT TOP(ABS(CAST(@num1 AS INT) - CAST(@num2 AS INT)) + 1)
N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + CASE WHEN @num1 <= @num2 THEN @num1 ELSE @num2 END - 1
FROM Numbers A
, Numbers B
);