Como afirmado (ou pelo menos mencionado) em muitas das maravilhosas respostas já dadas, esse problema é facilmente resolvido quando você tem um conjunto de números para trabalhar.
Nota: O seguinte é o T-SQL, mas é simplesmente minha implementação específica de conceitos gerais já mencionados aqui e na Internet em geral. Deve ser relativamente simples converter o código para o seu dialeto de sua escolha.
Quão? Considere esta consulta:
SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;
O texto acima produz o intervalo de datas 22/1/0001 a 27/1/0001 e é extremamente trivial. Há duas informações importantes na consulta acima: a data de início0001-01-22
e o deslocamento de 5
. Se combinarmos essas duas informações, obviamente teremos nossa data final. Assim, dadas duas datas, a geração de um intervalo pode ser dividida da seguinte forma:
Encontre a diferença entre duas datas (o deslocamento), fácil:
-- Returns 125
SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))
O uso ABS()
aqui garante que a ordem da data seja irrelevante.
Gere um conjunto limitado de números, também fácil:
-- 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')
Observe que não nos importamos com o que estamos selecionando FROM
aqui. Só precisamos de um conjunto para trabalhar, para que contemos o número de linhas nele. Eu pessoalmente uso um TVF, alguns usam CTE, outros usam uma tabela de números, você entendeu. Defendo o uso da solução de melhor desempenho que você também entende.
A combinação desses dois métodos resolverá nosso problema:
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));
O exemplo acima é um código horrível, mas demonstra como tudo acontece.
Mais divertido
Eu preciso fazer muito esse tipo de coisa, então encapsulei a lógica em dois TVFs. O primeiro gera um intervalo de números e o segundo usa essa funcionalidade para gerar um intervalo de datas. A matemática é garantir que a ordem de entrada não importa e porque eu queria usar toda a gama de números disponíveis no GenerateRangeSmallInt
.
A função a seguir leva ~ 16ms do tempo da CPU para retornar o intervalo máximo de 65536 datas.
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
);