Как указывалось (или, по крайней мере, упоминалось) во многих замечательных ответах, которые уже даны, эта проблема легко решается, когда у вас есть набор чисел для работы.
Примечание: ниже приведен 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 - 1/27/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));
Приведенный выше пример - ужасный код, но демонстрирует, как все объединяется.
Смешнее
Мне нужно много заниматься такими вещами, поэтому я инкапсулировал логику в два TVF. Первый генерирует диапазон чисел, а второй использует эту функцию для генерации диапазона дат. Математика состоит в том, чтобы гарантировать, что порядок ввода не имеет значения, и потому что я хотел использовать полный диапазон чисел, доступных в 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
);