web-dev-qa-db-fra.com

générer des jours à partir de la plage de dates

Je voudrais lancer une requête comme

select ... as days where `date` is between '2010-01-20' and '2010-01-24'

Et renvoyer des données comme: 

 jours 
 ---------- 
 2010-01-20 
 2010-01-21 
 2010-01-22 
 2010-01-23
 2010-01-24 
123
Pentium10

Cette solution utilise no boucles, procédures ou tables temporaires. La sous-requête génère des dates pour les 10 000 derniers jours et peut être étendue pour aller aussi loin que vous le souhaitez.

select a.Date 
from (
    select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a) ) DAY as Date
    from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as d
) a
where a.Date between '2010-01-20' and '2010-01-24' 

Sortie:

Date
----------
2010-01-24
2010-01-23
2010-01-22
2010-01-21
2010-01-20

Notes sur les performances

Testez-le ici , la performance est étonnamment bonne: la requête ci-dessus prend 0,0009 s.

Si nous étendons la sous-requête pour générer env. 100 000 numéros (et donc environ 274 années de dates), cela fonctionne en 0.0458 sec.

Incidemment, il s’agit d’une technique très portable qui fonctionne avec la plupart des bases de données avec des ajustements mineurs.

Exemple SQL Fiddle renvoyant 1 000 jours

294
RedFilter

Voici une autre variante utilisant des vues:

CREATE VIEW digits AS
  SELECT 0 AS digit UNION ALL
  SELECT 1 UNION ALL
  SELECT 2 UNION ALL
  SELECT 3 UNION ALL
  SELECT 4 UNION ALL
  SELECT 5 UNION ALL
  SELECT 6 UNION ALL
  SELECT 7 UNION ALL
  SELECT 8 UNION ALL
  SELECT 9;

CREATE VIEW numbers AS
  SELECT
    ones.digit + tens.digit * 10 + hundreds.digit * 100 + thousands.digit * 1000 AS number
  FROM
    digits as ones,
    digits as tens,
    digits as hundreds,
    digits as thousands;

CREATE VIEW dates AS
  SELECT
    SUBDATE(CURRENT_DATE(), number) AS date
  FROM
    numbers;

Et alors vous pouvez simplement faire (voir à quel point c'est élégant?):

SELECT
  date
FROM
  dates
WHERE
  date BETWEEN '2010-01-20' AND '2010-01-24'
ORDER BY
  date

Mettre à jour

Il est à noter que vous ne pourrez générer que des dates passées à partir de la date du jour. Si vous souhaitez générer un type de plage de dates (passé, futur et intermédiaire), vous devrez utiliser cette vue à la place:

CREATE VIEW dates AS
  SELECT
    SUBDATE(CURRENT_DATE(), number) AS date
  FROM
    numbers
  UNION ALL
  SELECT
    ADDDATE(CURRENT_DATE(), number + 1) AS date
  FROM
    numbers;
31
Stéphane

La réponse acceptée ne fonctionnait pas pour PostgreSQL (erreur de syntaxe égale ou proche de "a").

Pour ce faire, utilisez PostgreSQL: generate_series function, c.-à-d.

SELECT day::date
FROM generate_series('2010-01-20', '2010-01-24', INTERVAL '1 day') day;

    day
------------
 2010-01-20
 2010-01-21
 2010-01-22
 2010-01-23
 2010-01-24
(5 rows)
19
Dmitry Gusev

À l'aide d'une expression de table commune récursive (CTE), vous pouvez générer une liste de dates, puis en sélectionner une. Évidemment, vous ne voudriez normalement pas créer trois millions de dates, ce qui illustre bien les possibilités. Vous pouvez simplement limiter la plage de dates à l'intérieur du CTE et omettre la clause where de l'instruction select à l'aide du CTE.

with [dates] as (
    select convert(datetime, '1753-01-01') as [date] --start
    union all
    select dateadd(day, 1, [date])
    from [dates]
    where [date] < '9999-12-31' --end
)
select [date]
from [dates]
where [date] between '2013-01-01' and '2013-12-31'
option (maxrecursion 0)

Sur Microsoft SQL Server 2005, la liste CTE de toutes les dates possibles prenait 1:08. Générer cent ans a pris moins d'une seconde.

13
Joshua

Requête MSSQL

select datetable.Date 
from (
    select DATEADD(day,-(a.a + (10 * b.a) + (100 * c.a)),getdate()) AS Date
    from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
     union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a

    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
     union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b

    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
     union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) datetable
where datetable.Date between '2014-01-20' and '2014-01-24' 
order by datetable.Date DESC

Sortie

Date
-----
2014-01-23 12:35:25.250
2014-01-22 12:35:25.250
2014-01-21 12:35:25.250
2014-01-20 12:35:25.250
7
SUHAIL AG

L'ancienne solution pour cela sans boucle/curseur consiste à créer une table NUMBERS, qui comporte une seule colonne Integer avec des valeurs commençant à 1. 

CREATE TABLE  `example`.`numbers` (
  `id` int(10) unsigned NOT NULL auto_increment,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Vous devez remplir la table avec suffisamment d'enregistrements pour répondre à vos besoins:

INSERT INTO NUMBERS (id) VALUES (NULL);

Une fois que vous avez la table NUMBERS, vous pouvez utiliser:

SELECT x.start_date + INTERVAL n.id-1 DAY
  FROM NUMBERS n
  JOIN (SELECT STR_TO_DATE('2010-01-20', '%Y-%m-%d') AS start_date 
          FROM DUAL) x
 WHERE x.start_date + INTERVAL n.id-1 DAY <= '2010-01-24'

La solution low-tech absolue serait:

SELECT STR_TO_DATE('2010-01-20', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-21', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-22', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-23', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-24', '%Y-%m-%d')
 FROM DUAL

Pour quoi l'utiliseriez-vous?


Pour générer des listes de dates ou de nombres afin de LEFT JOIN sur. Vous y parviendrez afin de voir s'il y a des lacunes dans les données, car vous êtes en train de joindre à gauche dans une liste de données séquentielles - des valeurs nulles indiqueront clairement où existent des lacunes.

4
OMG Ponies

Pour Access 2010 - plusieurs étapes sont nécessaires; J'ai suivi le même schéma que ci-dessus, mais je pensais pouvoir aider quelqu'un dans Access. Travaillait très bien pour moi, je n'avais pas à garder une table de classement semée.

Créez une table appelée DUAL (similaire au fonctionnement de la table Oracle DUAL)

  • ID (Numéro automatique)
  • Colonne factice (texte)
  • Ajouter une valeur de ligne (1, "DummyRow")

Créez une requête nommée "ZeroThru9Q"; entrez manuellement la syntaxe suivante:

SELECT 0 AS a
FROM dual
UNION ALL
SELECT 1
FROM dual
UNION ALL
SELECT 2
FROM dual
UNION ALL
SELECT 3
FROM dual
UNION ALL
SELECT 4
FROM dual
UNION ALL
SELECT 5
FROM dual
UNION ALL
SELECT 6
FROM dual
UNION ALL
SELECT 7
FROM dual
UNION ALL
SELECT 8
FROM dual
UNION ALL
SELECT 9
FROM dual;

Créez une requête nommée "TodayMinus1KQ" (pour les dates antérieures à aujourd'hui); entrez manuellement la syntaxe suivante:

SELECT date() - (a.a + (10 * b.a) + (100 * c.a)) AS MyDate
FROM
  (SELECT *
   FROM ZeroThru9Q) AS a,

  (SELECT *
   FROM ZeroThru9Q) AS b,

  (SELECT *
   FROM ZeroThru9Q) AS c

Créez une requête nommée "TodayPlus1KQ" (pour les dates postérieures à aujourd'hui); entrez manuellement la syntaxe suivante:

SELECT date() + (a.a + (10 * b.a) + (100 * c.a)) AS MyDate
FROM
  (SELECT *
   FROM ZeroThru9Q) AS a,

  (SELECT *
   FROM ZeroThru9Q) AS b,

  (SELECT *
   FROM ZeroThru9Q) AS c;

Créez une requête d'union nommée "TodayPlusMinus1KQ" (pour les dates de plus ou moins 1000 jours):

SELECT MyDate
FROM TodayMinus1KQ
UNION
SELECT MyDate
FROM TodayPlus1KQ;

Maintenant, vous pouvez utiliser la requête:

SELECT MyDate
FROM TodayPlusMinus1KQ
WHERE MyDate BETWEEN #05/01/2014# and #05/30/2014#
4
Travis

thx Pentium10 - vous m'avez fait rejoindre stackoverflow :) - c'est mon portage sur msaccess - pensez que cela fonctionnera sur n'importe quelle version

SELECT date_value
FROM (SELECT a.espr1+(10*b.espr1)+(100*c.espr1) AS integer_value,
dateadd("d",integer_value,dateserial([start_year], [start_month], [start_day])) as date_value
FROM (select * from 
    (
    select top 1 "0" as espr1 from MSysObjects
    union all
    select top 1 "1" as espr2 from MSysObjects
    union all
    select top 1 "2" as espr3 from MSysObjects
    union all
    select top 1 "3" as espr4 from MSysObjects
    union all
    select top 1 "4" as espr5 from MSysObjects
    union all
    select top 1 "5" as espr6 from MSysObjects
    union all
    select top 1 "6" as espr7 from MSysObjects
    union all
    select top 1 "7" as espr8 from MSysObjects
    union all
    select top 1 "8" as espr9 from MSysObjects
    union all
    select top 1 "9" as espr9 from MSysObjects
    ) as a,
    (
    select top 1 "0" as espr1 from MSysObjects
    union all
    select top 1 "1" as espr2 from MSysObjects
    union all
    select top 1 "2" as espr3 from MSysObjects
    union all
    select top 1 "3" as espr4 from MSysObjects
    union all
    select top 1 "4" as espr5 from MSysObjects
    union all
    select top 1 "5" as espr6 from MSysObjects
    union all
    select top 1 "6" as espr7 from MSysObjects
    union all
    select top 1 "7" as espr8 from MSysObjects
    union all
    select top 1 "8" as espr9 from MSysObjects
    union all
    select top 1 "9" as espr9 from MSysObjects
    ) as b,
    (
    select top 1 "0" as espr1 from MSysObjects
    union all
    select top 1 "1" as espr2 from MSysObjects
    union all
    select top 1 "2" as espr3 from MSysObjects
    union all
    select top 1 "3" as espr4 from MSysObjects
    union all
    select top 1 "4" as espr5 from MSysObjects
    union all
    select top 1 "5" as espr6 from MSysObjects
    union all
    select top 1 "6" as espr7 from MSysObjects
    union all
    select top 1 "7" as espr8 from MSysObjects
    union all
    select top 1 "8" as espr9 from MSysObjects
    union all
    select top 1 "9" as espr9 from MSysObjects
    ) as c   
)  as d) 
WHERE date_value 
between dateserial([start_year], [start_month], [start_day]) 
and dateserial([end_year], [end_month], [end_day]);

mSysObjects référencé juste parce que l'accès nécessite une table comptant au moins 1 enregistrement, dans une clause from - toute table avec au moins 1 enregistrement ferait l'affaire.

3
user3780177

essaye ça.

SELECT TO_DATE('20160210','yyyymmdd') - 1 + LEVEL AS start_day 
from DUAL
connect by level <= (TO_DATE('20160228','yyyymmdd') + 1) - TO_DATE('20160210','yyyymmdd') ;
2
loalexzzzz

Procédure + table temporaire:

DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `days`(IN dateStart DATE, IN dateEnd DATE)
BEGIN

    CREATE TEMPORARY TABLE IF NOT EXISTS date_range (day DATE);

    WHILE dateStart <= dateEnd DO
      INSERT INTO date_range VALUES (dateStart);
      SET dateStart = DATE_ADD(dateStart, INTERVAL 1 DAY);
    END WHILE;

    SELECT * FROM date_range;
    DROP TEMPORARY TABLE IF EXISTS date_range;

END
2

Réponse plus courte que celle acceptée, même idée:

(SELECT TRIM('2016-01-05' + INTERVAL a + b DAY) date
FROM
(SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
UNION SELECT 8 UNION SELECT 9 ) d,
(SELECT 0 b UNION SELECT 10 UNION SELECT 20
UNION SELECT 30 UNION SELECT 40) m
WHERE '2016-01-05' + INTERVAL a + b DAY  <=  '2016-01-21')
1
daniherculano

si vous avez besoin de plus de quelques jours, vous avez besoin d’une table.

Créer une plage de dates dans mysql

puis, 

select from days.day, count(mytable.field) as fields from days left join mytable on day=date where date between x and y;
1
gcb

Pour ceux qui le souhaitent sous forme de vue enregistrée (MySQL ne prend pas en charge les instructions select imbriquées dans les vues):

create view zero_to_nine as
    select 0 as n union all 
    select 1 union all 
    select 2 union all 
    select 3 union all 
    select 4 union all 
    select 5 union all 
    select 6 union all 
    select 7 union all 
    select 8 union all 
    select 9;

create view date_range as
    select curdate() - INTERVAL (a.n + (10 * b.n) + (100 * c.n)) DAY as date
    from zero_to_nine as a
    cross join zero_to_nine as b
    cross join zero_to_nine as c;

Vous pouvez alors faire

select * from date_range

obtenir

date
---
2017-06-06
2017-06-05
2017-06-04
2017-06-03
2017-06-02
...
1
Tom G

Générer des dates entre deux champs de date

Si vous connaissez la requête SQL CTE, cette solution vous aidera à résoudre votre question.

Voici l'exemple

Nous avons les dates dans une table

Nom de la table: “testdate”

STARTDATE   ENDDATE
10/24/2012  10/24/2012
10/27/2012  10/29/2012
10/30/2012  10/30/2012

Résultat requis:

STARTDATE
10/24/2012
10/27/2012
10/28/2012
10/29/2012
10/30/2012

Solution:

WITH CTE AS
  (SELECT DISTINCT convert(varchar(10),StartTime, 101) AS StartTime,
                   datediff(dd,StartTime, endTime) AS diff
   FROM dbo.testdate
   UNION ALL SELECT StartTime,
                    diff - 1 AS diff
   FROM CTE
   WHERE diff<> 0)
SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime
FROM CTE

Explication: Explication de requête récursive CTE

  • Première partie de la requête:

    SELECT DISTINCT convert(varchar(10), StartTime, 101) AS StartTime, datediff(dd, StartTime, endTime) AS diff FROM dbo.testdate

    Explication: la première colonne est "date de début", la deuxième colonne indique la différence entre les dates de début et de fin en jours et sera considérée comme une colonne "diff".

  • Deuxième partie de la requête:

    UNION ALL SELECT StartTime, diff-1 AS diff FROM CTE WHERE diff<>0

    Explication: Union all héritera du résultat de la requête ci-dessus jusqu'à ce que le résultat devienne nul. Ainsi, le résultat "StartTime" est hérité de la requête CTE générée et de diff, diminue - 1, de sorte qu'il ressemble à 3, 2 et 1. jusqu'à 0

Par exemple

STARTDATE   DIFF
10/24/2012  0
10/27/2012  0
10/27/2012  1
10/27/2012  2
10/30/2012  0

Résultat Spécification

STARTDATE       Specification
10/24/2012  --> From Record 1
10/27/2012  --> From Record 2
10/27/2012  --> From Record 2
10/27/2012  --> From Record 2
10/30/2012  --> From Record 3
  • 3ème partie de la requête

    SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime FROM CTE

    Il ajoutera le jour "diff" dans "startdate", le résultat devrait donc être comme ci-dessous

Résultat

STARTDATE
10/24/2012
10/27/2012
10/28/2012
10/29/2012
10/30/2012
1
Tarun Harkinia

Comme indiqué (ou du moins mentionné) dans bon nombre des merveilleuses réponses déjà données, ce problème est facilement résolu une fois que vous avez une série de chiffres à utiliser.

Remarque: Ce qui suit est T-SQL, mais c’est tout simplement ma mise en oeuvre des concepts généraux déjà mentionnés ici et sur Internet au sens large. Il devrait être relativement simple de convertir le code dans le dialecte de votre choix.

Comment? Considérons 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 essentielles: le date de début de 0001-01-22 et le offset de 5. Si nous combinons ces deux informations, nous avons évidemment notre date de fin. Ainsi, pour 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 est sans importance.

  • Générez un nombre 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')

    Remarquez que nous ne nous soucions pas vraiment de ce que nous sélectionnons ici FROM. Nous avons simplement besoin d'un ensemble pour pouvoir compter le nombre de lignes qu'il contient. Personnellement, j'utilise un fichier TVF, certains utilisent un CTE, d'autres utilisent un tableau de chiffres à la place, vous voyez l'idée. Je préconise l’utilisation de 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 est réuni.

Plus amusant

Je dois souvent faire ce genre de choses, alors j’ai résumé la logique en 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 faire en sorte que l'ordre d'entrée ne compte pas et parce que je voulais utiliser toute la gamme de nombres disponibles dans GenerateRangeSmallInt.

La fonction suivante prend environ 16 ms de temps CPU pour renvoyer la plage maximale de 65 536 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
);
1
Kittoes0124

C'est une bonne idée de générer ces dates à la volée. Cependant, je ne me sens pas à l'aise pour faire cela avec une gamme assez large et j'ai donc fini avec la solution suivante:

  1. Création d'un tableau "DatesNumbers" contenant les numéros utilisés pour le calcul des dates:

CREATE TABLE DatesNumbers ( i MEDIUMINT NOT NULL, PRIMARY KEY (i) ) COMMENT='Used by Dates view' ;

  1. Rempli le tableau en utilisant les techniques ci-dessus avec des nombres de -59999 à 40000. Cette plage me donnera des dates allant de 59999 jours (~ 164 ans) à 40000 jours (109 ans):

INSERT INTO DatesNumbers SELECT a.i + (10 * b.i) + (100 * c.i) + (1000 * d.i) + (10000 * e.i) - 59999 AS i FROM (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS a , (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS b , (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS c , (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS d , (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS e ;

  1. Créé une vue "Dates":

SELECT i , CURRENT_DATE() + INTERVAL i DAY AS Date FROM DatesNumbers

C'est tout.

  • (+) Requêtes faciles à lire
  • (+) Non à la volée des générations 
  • (+) Donne des dates dans le passé et dans le futur et AUCUN UNION n'est en vue pour cela, comme dans cet article .
  • (+) Les dates "dans le passé seulement" ou "dans le futur seulement" peuvent être filtrées avec WHERE i < 0 ou WHERE i > 0 (PK)
  • (-) 'table' temporaire 'est utilisé
1
fifonik

Une solution supplémentaire pour mysql 8.0.1 et mariadb 10.2.2 utilisant des expressions de table communes récursives:

with recursive dates as (
    select '2010-01-20' as date
    union all
    select date + interval 1 day from dates where date < '2010-01-24'
)
select * from dates;
WITH
  Digits AS (SELECT 0 D UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9),
  Dates AS (SELECT adddate('1970-01-01',t4.d*10000 + t3.d*1000 + t2.d*100 + t1.d*10 +t0.d) AS date FROM Digits AS t0, Digits AS t1, Digits AS t2, Digits AS t3, Digits AS t4)
SELECT * FROM Dates WHERE date BETWEEN '2017-01-01' AND '2017-12-31'
0
Norbert Madarász

Vous souhaitez obtenir la plage de dates.

Dans votre exemple, vous souhaitez obtenir les dates comprises entre '2010-01-20' et '2010-01-24'

solution possible:

 select date_add('2010-01-20', interval row day) from
 ( 
    SELECT @row := @row + 1 as row FROM 
    (select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
    (select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2, 
    (select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3, 
    (select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t4, 
    (SELECT @row:=-1) r
 ) sequence
 where date_add('2010-01-20', interval row day) <= '2010-01-24'

Explication

MySQL a une fonction date_add donc

select date_add('2010-01-20', interval 1 day)

te donnera

2010-01-21

La fonction datiff vous indiquera souvent que vous devez répéter cette opération.

select datediff('2010-01-24', '2010-01-20')

qui retourne

 4

Obtenir une liste de dates dans une plage de dates revient à créer une séquence de nombres entiers, voir générer une séquence de nombres entiers dans MySQL

La réponse la plus votée a adopté une approche similaire à celle de https://stackoverflow.com/a/2652051/1497139 :

SELECT @row := @row + 1 as row FROM 
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2, 
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3, 
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t4, 
(SELECT @row:=0) r
limit 4

ce qui entraînera

row
1.0
2.0
3.0
4.0

Les lignes peuvent maintenant être utilisées pour créer une liste de dates à partir de la date de début donnée. Pour inclure la date de début, nous commençons par la ligne -1;

select date_add('2010-01-20', interval row day) from
 ( 
    SELECT @row := @row + 1 as row FROM 
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2, 
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3, 
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t4, 
    (SELECT @row:=-1) r
 ) sequence
 where date_add('2010-01-20', interval row day) <= '2010-01-24'
0
Wolfgang Fahl

SQLite version de la solution top RedFilters

select d.Date
from (
    select 
    date(julianday('2010-01-20') + (a.a + (10 * b.a) + (100 * c.a))) as Date
    from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) d
where 
d.Date between '2010-01-20' and '2010-01-24' 
order by d.Date
0
martin

Bon .. Essayez ceci: http://www.devshed.com/c/a/MySQL/Delving-Deeper-into-MySQL-50/
http://dev.mysql.com/doc/refman/5.0/en/loop-statement.html
http://www.roseindia.net/sql/mysql-example/mysql-loop.shtml

Utilisez-le pour, par exemple, générer une table temporaire, puis effectuez une sélection * sur la table temporaire. Ou affichez les résultats un à un.
Ce que vous dites vouloir ne peut pas être fait avec une instruction SELECT, mais cela peut être faisable avec des choses spécifiques à MySQL.
Là encore, vous avez peut-être besoin de curseurs: http://dev.mysql.com/doc/refman/5.0/en/cursors.html

0
Trevoke

Solution élégante utilisant la nouvelle fonctionnalité récursive (Common Table Expressions) dans MariaDB> = 10.3 et MySQL> = 8.0.

WITH RECURSIVE t as (
    select '2019-01-01' as dt
  UNION
    SELECT DATE_ADD(t.dt, INTERVAL 1 DAY) FROM t WHERE DATE_ADD(t.dt, INTERVAL 1 DAY) <= '2019-04-30'
)
select * FROM t;

Ce qui précède renvoie un tableau des dates comprises entre '2019-01-01' et '2019-04-30'. C'est aussi décemment rapide. Le retour de 1000 ans de dates (~ 365 000 jours) prend environ 400 ms sur ma machine.

0
Brad

si vous voulez la liste des dates entre deux dates:

create table #dates ([date] smalldatetime)
while @since < @to
begin
     insert into #dates(dateadd(day,1,@since))
     set @since = dateadd(day,1,@since)
end
select [date] from #dates

* violon ici: http://sqlfiddle.com/#!6/9eecb/3469

0
celerno
DELIMITER $$
CREATE PROCEDURE GenerateRangeDates(IN dateStart DATE, IN dateEnd DATE)
BEGIN

    CREATE TEMPORARY TABLE IF NOT EXISTS dates (day DATE);

    loopDate: LOOP
        INSERT INTO dates(day) VALUES (dateStart); 
        SET dateStart = DATE_ADD(dateStart, INTERVAL 1 DAY);

        IF dateStart <= dateEnd 
            THEN ITERATE loopDate;
            ELSE LEAVE loopDate;
        END IF;
    END LOOP loopDate;

    SELECT day FROM dates;
    DROP TEMPORARY TABLE IF EXISTS dates;

END 
$$

-- Call procedure
call GenerateRangeDates( 
        now() - INTERVAL 40 DAY,
        now()
    );
0
Victor Silva

Pour Oracle, ma solution est la suivante:

select trunc(sysdate-dayincrement, 'DD') 
  from dual, (select level as dayincrement 
                from dual connect by level <= 30)

Sysdate peut être modifié pour une date spécifique et le numéro de niveau peut être modifié pour donner plus de dates.

0
mousetwentytwo

Peut créer une procédure également pour créer table de calendrier avec timestmap différente de jour Si vous voulez une table pour chaque trimestre 

par exemple.

2019-01-22 08:45:00
2019-01-22 09:00:00
2019-01-22 09:15:00
2019-01-22 09:30:00
2019-01-22 09:45:00
2019-01-22 10:00:00

vous pouvez utiliser 

CREATE DEFINER=`root`@`localhost` PROCEDURE `generate_calendar_table`()
BEGIN

select unix_timestamp('2014-01-01 00:00:00') into @startts;
select unix_timestamp('2025-01-01 00:00:00') into @endts;

if ( @startts < @endts ) then

    DROP TEMPORARY TABLE IF EXISTS calendar_table_tmp;

    CREATE TEMPORARY TABLE calendar_table_tmp (ts int, dt datetime); 

    WHILE ( @startts < @endts)
        DO 
        SET @startts = @startts + 900;
        INSERT calendar_table_tmp VALUES (@startts, from_unixtime(@startts));
    END WHILE;

END if;

END


puis manipuler à travers

select ts, dt from calendar_table_tmp;

ça te donne aussi ts

'1548143100', '2019-01-22 08:45:00'
'1548144000', '2019-01-22 09:00:00'
'1548144900', '2019-01-22 09:15:00'
'1548145800', '2019-01-22 09:30:00'
'1548146700', '2019-01-22 09:45:00'
'1548147600', '2019-01-22 10:00:00'

à partir de là, vous pouvez commencer à ajouter d'autres informations telles que 

select ts, dt, weekday(dt) as wd from calendar_table_tmp;

ou créer une vraie table avec créer une déclaration de table

0
Gianluca D'Ardia

amélioré avec le jour de la semaine joignant une table de vacances personnalisée Microsoft MSSQL 2012 pour la table de date powerpivot https://Gist.github.com/josy1024/cb1487d66d9e0ccbd420bc4a23b6e90e

with [dates] as (
    select convert(datetime, '2016-01-01') as [date] --start
    union all
    select dateadd(day, 1, [date])
    from [dates]
    where [date] < '2018-01-01' --end
)
select [date]
, DATEPART (dw,[date]) as Wochentag
, (select holidayname from holidaytable 
where holidaytable.hdate = [date]) 
as Feiertag
from [dates]
where [date] between '2016-01-01' and '2016-31-12'
option (maxrecursion 0)
0
josy1024
set language  'SPANISH'
DECLARE @table table(fechaDesde datetime , fechaHasta datetime ) 
INSERT @table VALUES('20151231' , '20161231');
WITH x AS 
    (
        SELECT   DATEADD( m , 1 ,fechaDesde ) as fecha  FROM @table
        UNION ALL
        SELECT  DATEADD( m , 1 ,fecha )
        FROM @table t INNER JOIN x ON  DATEADD( m , 1 ,x.fecha ) <= t.fechaHasta
    )
SELECT LEFT( CONVERT( VARCHAR, fecha , 112 ) , 6 ) as Periodo_Id 
,DATEPART ( dd, DATEADD(dd,-(DAY(fecha)-1),fecha)) Num_Dia_Inicio
,DATEADD(dd,-(DAY(fecha)-1),fecha) Fecha_Inicio
,DATEPART ( mm , fecha ) Mes_Id
,DATEPART ( yy , fecha ) Anio
,DATEPART ( dd, DATEADD(dd,-(DAY(DATEADD(mm,1,fecha))),DATEADD(mm,1,fecha))) Num_Dia_Fin
,DATEADD(dd,-(DAY(DATEADD(mm,1,fecha))),DATEADD(mm,1,fecha)) ultimoDia
,datename(MONTH, fecha) mes
,'Q' + convert(varchar(10),  DATEPART(QUARTER, fecha)) Trimestre_Name
FROM x 
OPTION(MAXRECURSION 0)
0
Benigno Geronimo