Come affermato (o almeno accennato) in molte delle meravigliose risposte già fornite, questo problema è facilmente risolvibile una volta che hai una serie di numeri con cui lavorare.
Nota: Quanto segue è T-SQL, ma è semplicemente la mia particolare implementazione di concetti generali già menzionati qui e su Internet in generale. Dovrebbe essere relativamente semplice convertire il codice nel dialetto che preferisci.
Come? Considera questa query:
SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;
Quanto sopra produce l'intervallo di date 1/22/0001 - 1/27/0001 ed è estremamente banale. Ci sono 2 informazioni chiave nella query sopra: la data di inizio di 0001-01-22
e l' offset di 5
. Se combiniamo queste due informazioni, ovviamente abbiamo la nostra data di fine. Pertanto, date due date, la generazione di un intervallo può essere suddivisa in questo modo:
Trova la differenza tra due date (l'offset), facile:
-- Returns 125
SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))
L'uso ABS()
qui garantisce che l'ordine della data sia irrilevante.
Genera un numero limitato di numeri, anche facile:
-- 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')
Si noti che in realtà non ci interessa quello che stiamo selezionando FROM
qui. Abbiamo solo bisogno di un set con cui lavorare in modo da contare il numero di righe al suo interno. Personalmente uso un TVF, alcuni usano un CTE, altri usano invece una tabella numerica, hai capito. Sostengo per l'utilizzo della soluzione più performante che capisci anche tu.
La combinazione di questi due metodi risolverà il nostro 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));
L'esempio sopra è un codice orribile ma dimostra come tutto si combina.
Più divertimento
Devo fare molto questo genere di cose, quindi ho incapsulato la logica in due TVF. Il primo genera un intervallo di numeri e il secondo utilizza questa funzionalità per generare un intervallo di date. La matematica è garantire che l'ordine di input non abbia importanza e perché volevo utilizzare l'intera gamma di numeri disponibili inGenerateRangeSmallInt
.
La seguente funzione richiede ~ 16ms di tempo CPU per restituire l'intervallo massimo di 65536 date.
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
);