Як було сказано (або, принаймні, на це наголошується) у багатьох чудових відповідях, вже заданих, ця проблема легко вирішується, коли у вас є набір номерів, з якими можна працювати.
Примітка. Далі йде T-SQL, але це просто моя конкретна реалізація загальних концепцій, про які вже говорилося тут, і в Інтернеті в цілому. Перетворити код на обраний вами діалект слід непросто.
Як? Розглянемо цей запит:
SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;
Вищезазначене дає діапазон дат 1/22/0001 - 27.01.0001 і надзвичайно тривіально. Є 2 ключових елементів інформації в наведеному вище запиті: дата початку з 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));
Наведений вище приклад - жахливий код, але демонструє, як все поєднується.
Більше веселощів
Мені потрібно багато чого робити, тому я вклав логіку у два ТВФ. Перший генерує діапазон чисел, а другий використовує цю функціональність для створення діапазону дат. Математика полягає в тому, щоб порядок введення не мав значення, і тому, що я хотів використовувати повний діапазон номерів, доступних в GenerateRangeSmallInt
.
Наступна функція займає ~ 16 мс часу процесора, щоб повернути максимальний діапазон 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
);