69 votes

Obtenir une liste de dates entre deux dates à l'aide d'une fonction

Ma question est similaire à este Question sur MySQL, mais destinée à SQL Server :

Existe-t-il une fonction ou une requête qui renvoie une liste de jours entre deux dates ? Par exemple, disons qu'il existe une fonction appelée ExplodeDates :

SELECT ExplodeDates('2010-01-01', '2010-01-13');

Cela renvoie un tableau à une seule colonne contenant les valeurs :

2010-01-01
2010-01-02
2010-01-03
2010-01-04
2010-01-05
2010-01-06
2010-01-07
2010-01-08
2010-01-09
2010-01-10
2010-01-11
2010-01-12
2010-01-13

Je pense qu'un calendrier ou un tableau de chiffres pourrait m'aider.


Mise à jour

J'ai décidé de jeter un coup d'œil sur les trois réponses de code fournies, et les résultats de l'exécution - en % du lot total - sont les suivants :

Plus bas, c'est mieux

J'ai accepté la réponse de Rob Farley, car elle était la plus rapide, même si les solutions de tables de chiffres (utilisées par KM et StingyJack dans leurs réponses) sont un peu mes préférées. La réponse de Rob Farley était deux tiers plus rapide.

Mise à jour 2

Alivia's réponse est beaucoup plus succinct. J'ai modifié la réponse acceptée.

101voto

Alivia Points 270

Ces quelques lignes sont la réponse simple à cette question dans sql server.

WITH mycte AS
(
  SELECT CAST('2011-01-01' AS DATETIME) DateValue
  UNION ALL
  SELECT  DateValue + 1
  FROM    mycte   
  WHERE   DateValue + 1 < '2021-12-31'
)

SELECT  DateValue
FROM    mycte
OPTION (MAXRECURSION 0)

67voto

Rob Farley Points 9042

Essayez quelque chose comme ça :

CREATE FUNCTION dbo.ExplodeDates(@startdate datetime, @enddate datetime)
returns table as
return (
with 
 N0 as (SELECT 1 as n UNION ALL SELECT 1)
,N1 as (SELECT 1 as n FROM N0 t1, N0 t2)
,N2 as (SELECT 1 as n FROM N1 t1, N1 t2)
,N3 as (SELECT 1 as n FROM N2 t1, N2 t2)
,N4 as (SELECT 1 as n FROM N3 t1, N3 t2)
,N5 as (SELECT 1 as n FROM N4 t1, N4 t2)
,N6 as (SELECT 1 as n FROM N5 t1, N5 t2)
,nums as (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as num FROM N6)
SELECT DATEADD(day,num-1,@startdate) as thedate
FROM nums
WHERE num <= DATEDIFF(day,@startdate,@enddate) + 1
);

Vous utilisez alors :

SELECT *
FROM dbo.ExplodeDates('20090401','20090531') as d;

Modifié (après l'acceptation) :

Attention... si vous avez déjà une table nums suffisamment grande, vous devriez utiliser :

CREATE FUNCTION dbo.ExplodeDates(@startdate datetime, @enddate datetime)
returns table as
return (
SELECT DATEADD(day,num-1,@startdate) as thedate
FROM nums
WHERE num <= DATEDIFF(day,@startdate,@enddate) + 1
);

Et vous pouvez créer un tel tableau en utilisant :

CREATE TABLE dbo.nums (num int PRIMARY KEY);
INSERT dbo.nums values (1);
GO
INSERT dbo.nums SELECT num + (SELECT COUNT(*) FROM nums) FROM nums
GO 20

Ces lignes créeront un tableau de nombres contenant 1M de lignes... et bien plus rapidement qu'en les insérant une par une.

Vous ne devez PAS créer votre fonction ExplodeDates à l'aide d'une fonction qui implique BEGIN et END, car l'optimiseur de requêtes ne pourra plus du tout simplifier la requête.

17voto

Jason Points 131

Ceci fait exactement ce que vous voulez, modifié à partir du post précédent de Will. Pas besoin de tables d'aide ou de boucles.

WITH date_range (calc_date) AS (
    SELECT DATEADD(DAY, DATEDIFF(DAY, 0, '2010-01-13') - DATEDIFF(DAY, '2010-01-01', '2010-01-13'), 0)
        UNION ALL SELECT DATEADD(DAY, 1, calc_date)
            FROM date_range
            WHERE DATEADD(DAY, 1, calc_date) <= '2010-01-13')
SELECT calc_date
FROM date_range;

3voto

Brian Points 7072

Je suis un spécialiste d'Oracle, mais je crois que MS SQL Server prend en charge la clause connect by :

select  sysdate + level
from    dual
connect by level <= 10 ;

La sortie est :

SYSDATE+LEVEL
05-SEP-09
06-SEP-09
07-SEP-09
08-SEP-09
09-SEP-09
10-SEP-09
11-SEP-09
12-SEP-09
13-SEP-09
14-SEP-09

Dual est simplement une table "factice" fournie avec oracle (elle contient une ligne et le mot "factice" comme valeur de la seule colonne).

1voto

Mark Redman Points 10816

Quelques idées :

Si vous avez besoin de lister les dates afin de les parcourir en boucle, vous pourriez avoir des paramètres de date de début et de nombre de jours et faire une boucle tout en créant la date et en l'utilisant ?

Utiliser les procédures stockées C# CLR et écrire le code en C#.

Faites-le en dehors de la base de données dans le code

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X