Comme indiqué (ou du moins mentionné) dans de nombreuses réponses merveilleuses déjà données, ce problème est facilement résolu une fois que vous avez un ensemble de nombres avec lesquels travailler.
Remarque: Ce qui suit est T-SQL mais c'est simplement ma mise en œuvre particulière des concepts généraux déjà mentionnés ici et sur Internet en général. Il devrait être relativement simple de convertir le code dans le dialecte de votre choix.
Comment? Considérez cette requête:
SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;
Ce qui précède produit la plage de dates 1/22/0001 - 1/27/0001 et est extrêmement trivial. La requête ci-dessus contient 2 informations clés: la date de début0001-01-22
et le décalage de 5
. Si nous combinons ces deux informations, nous avons évidemment notre date de fin. Ainsi, étant donné deux dates, la génération d'une plage peut être décomposée comme suit:
Trouvez la différence entre deux dates données (le décalage), facile:
-- Returns 125
SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))
Utiliser ABS()
ici garantit que l'ordre des dates n'est pas pertinent.
Générez un ensemble limité de nombres, également 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')
Notez que nous ne nous soucions pas vraiment de ce que nous sélectionnons FROM
ici. Nous avons juste besoin d'un ensemble avec lequel travailler afin de compter le nombre de lignes qu'il contient. J'utilise personnellement un TVF, certains utilisent un CTE, d'autres utilisent une table de nombres à la place, vous voyez l'idée. Je préconise d'utiliser la solution la plus performante que vous comprenez également.
La combinaison de ces deux méthodes résoudra notre problème:
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'exemple ci-dessus est un code horrible mais montre comment tout se réunit.
Plus amusant
J'ai besoin de beaucoup faire ce genre de chose, alors j'ai encapsulé la logique dans deux TVF. Le premier génère une plage de nombres et le second utilise cette fonctionnalité pour générer une plage de dates. Le calcul est de s'assurer que l'ordre d'entrée n'a pas d'importance et parce que je voulais utiliser toute la gamme de nombres disponibles dans GenerateRangeSmallInt
.
La fonction suivante prend ~ 16 ms de temps CPU pour renvoyer la plage maximale de 65536 dates.
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
);