Как указывалось (или, по крайней мере, упоминалось) во многих замечательных ответах, которые уже даны, эта проблема легко решается, если у вас есть набор чисел для работы.* Ниже приведен 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 января 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
);