web-dev-qa-db-ger.com

tage aus Datumsbereich generieren

Ich möchte gerne eine Anfrage ausführen

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

Und Daten wie: 

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

Diese Lösung verwendet keine Schleifen, Prozeduren oder temporäre Tabellen. Die Unterabfrage generiert Datumsangaben für die letzten 10.000 Tage und kann so erweitert werden, dass sie so weit wie möglich vor- oder zurückspringt.

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' 

Ausgabe:

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

Hinweise zur Leistung

Beim Testen hier ist die Leistung überraschend gut: Die obige Abfrage dauert 0,0009 Sekunden.

Wenn wir die Unterabfrage erweitern, um ca. 100.000 Zahlen (und damit etwa 274 Jahre), läuft sie in 0,0458 Sekunden.

Dies ist übrigens eine sehr tragbare Technik, die mit den meisten Datenbanken mit geringfügigen Anpassungen funktioniert.

Beispiel für SQL Fiddle mit 1.000 Tagen

294
RedFilter

Hier ist eine weitere Variante mit Ansichten:

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;

Und dann können Sie es einfach tun (sehen Sie, wie elegant es ist?):

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

Update

Es ist erwähnenswert, dass Sie nur vergangene Datumsangaben ab dem aktuellen Datum generieren können. Wenn Sie einen Datumsbereich (vergangene, zukünftige und dazwischenliegende Datumsbereiche) generieren möchten, müssen Sie stattdessen diese Ansicht verwenden:

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

Die akzeptierte Antwort funktionierte für PostgreSQL nicht (Syntaxfehler bei oder in der Nähe von "a").

Die Vorgehensweise in PostgreSQL ist die Verwendung der Funktion generate_series , d. H .:

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

Mit einem rekursiven Common Table Expression (CTE) können Sie eine Datumsliste generieren und daraus auswählen. Natürlich möchten Sie normalerweise keine drei Millionen Datumsangaben erstellen, daher werden hier nur die Möglichkeiten veranschaulicht. Sie können einfach den Datumsbereich innerhalb des CTE einschränken und die where-Klausel in der select-Anweisung mit dem CTE auslassen.

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)

In Microsoft SQL Server 2005 dauerte das Generieren der CTE-Liste aller möglichen Datumsangaben 1:08. Die Erzeugung von hundert Jahren hat weniger als eine Sekunde gedauert.

13
Joshua

MSSQL-Abfrage

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

Ausgabe

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

Die bisherige Lösung, um dies ohne Schleife/Cursor zu tun, besteht darin, eine NUMBERS-Tabelle zu erstellen, die eine einzelne Integer-Spalte mit Werten aufweist, die bei 1 beginnen. 

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

Sie müssen die Tabelle mit genügend Datensätzen füllen, um Ihre Anforderungen zu erfüllen:

INSERT INTO NUMBERS (id) VALUES (NULL);

Sobald Sie die NUMBERS-Tabelle haben, können Sie Folgendes verwenden:

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'

Die absolute Low-Tech-Lösung wäre:

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

Wofür würdest du es verwenden?


Um Listen mit Datumsangaben oder Zahlen zu erstellen, um auf LEFT JOIN zu gelangen. Sie möchten dies tun, um zu sehen, wo Lücken in den Daten vorhanden sind, da Sie sich LEFT JOINing in einer Liste sequenzieller Daten befinden - Nullwerte machen deutlich, wo Lücken bestehen.

4
OMG Ponies

Für Access 2010 - mehrere Schritte erforderlich; Ich folgte dem gleichen Muster wie oben, dachte aber, ich könnte jemandem in Access helfen. Für mich hat es super geklappt, ich musste keine gesetzten Termine haben.

Erstellen Sie eine Tabelle namens DUAL (ähnlich wie die Oracle DUAL-Tabelle funktioniert)

  • ID (AutoNumber)
  • DummyColumn (Text)
  • Fügen Sie einen Zeilenwert hinzu (1, "DummyRow")

Erstellen Sie eine Abfrage mit dem Namen "ZeroThru9Q". Geben Sie die folgende Syntax manuell ein:

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;

Erstellen Sie eine Abfrage mit dem Namen "TodayMinus1KQ" (für Datumsangaben vor heute). Geben Sie die folgende Syntax manuell ein:

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

Erstellen Sie eine Abfrage mit dem Namen "TodayPlus1KQ" (für Datumsangaben nach heute). Geben Sie die folgende Syntax manuell ein:

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;

Erstellen Sie eine Unionsabfrage mit dem Namen "TodayPlusMinus1KQ" (für Datumsangaben +/- 1000 Tage):

SELECT MyDate
FROM TodayMinus1KQ
UNION
SELECT MyDate
FROM TodayPlus1KQ;

Jetzt können Sie die Abfrage verwenden:

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

thx Pentium10 - Sie haben mich dazu gebracht, stackoverflow beizutreten :) - Dies ist meine Portierung auf msaccess. Ich denke, das funktioniert mit jeder 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, auf das verwiesen wird, bewirkt, dass für den Zugriff mindestens ein Datensatz in einer from-Klausel benötigt wird - jede Tabelle mit mindestens einem Datensatz würde dies tun.

3
user3780177

versuche dies.

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

Prozedur + temporäre Tabelle:

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

Kürzer als angenommene Antwort, gleiche Idee:

(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

wenn Sie mehr als ein paar Tage brauchen, brauchen Sie einen Tisch.

Erstelle einen Datumsbereich in mysql

dann, 

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

Für alle, die dies als gespeicherte Ansicht wünschen (MySQL unterstützt keine verschachtelten Select-Anweisungen in Ansichten):

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;

Sie können es dann tun

select * from date_range

bekommen

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

Generiere Datumsangaben zwischen zwei Datumsfeldern

Wenn Sie mit der SQL CTE-Abfrage vertraut sind, hilft Ihnen diese Lösung bei der Lösung Ihrer Frage

Hier ist ein Beispiel

Wir haben Daten in einer Tabelle

Tabellenname: "Testdatum"

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

Ergebnis anfordern:

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

Lösung:

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

Erläuterung: Erklärung der rekursiven CTE-Abfrage

  • Erster Teil der Anfrage:

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

    Erläuterung: Die erste Spalte ist "Startdatum", die zweite Spalte ist die Differenz zwischen Start- und Enddatum in Tagen und wird als "Diff" -Spalte betrachtet

  • Zweiter Teil der Abfrage:

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

    Erläuterung: Union all erbt das Ergebnis der obigen Abfrage, bis das Ergebnis null wird. Das Ergebnis "StartTime" wird also von der generierten CTE-Abfrage und von diff geerbt, verringern Sie - 1, sodass es wie 3, 2 und 1 aussieht bis 0

Zum Beispiel

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

Ergebnis Spezifikation

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. Teil der Abfrage

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

    Es wird Tag "diff" in "startdate" hinzugefügt, daher sollte das Ergebnis wie folgt aussehen

Ergebnis

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

Wie bereits erwähnt (oder zumindest in vielen der wunderbaren Antworten erwähnt), lässt sich dieses Problem leicht lösen, wenn Sie mit einer Reihe von Zahlen arbeiten müssen.

Anmerkung: Das Folgende ist T-SQL, aber es ist einfach meine spezielle Implementierung allgemeiner Konzepte, die hier und im Internet allgemein erwähnt wurden. Es sollte relativ einfach sein, den Code in den gewünschten Dialekt zu konvertieren.

Wie? Betrachten Sie diese Abfrage:

SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;

Das obige ergibt den Datumsbereich 1/22/0001 - 1/27/0001 und ist äußerst trivial. Die obige Abfrage enthält zwei wichtige Informationen: das Startdatum von 0001-01-22 und das offset von 5. Wenn wir diese beiden Informationen kombinieren, haben wir offensichtlich unser Enddatum. Daher kann die Erzeugung eines Bereichs bei zwei Daten wie folgt aufgeteilt werden:

  • Finden Sie den Unterschied zwischen zwei angegebenen Daten (dem Offset), ganz einfach:

    -- Returns 125 SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))

    Die Verwendung von ABS() stellt sicher, dass die Datumsreihenfolge nicht relevant ist.

  • Erzeugen Sie eine begrenzte Anzahl von Zahlen, auch einfach:

    -- 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')

    Beachten Sie, dass es uns eigentlich egal ist, was wir hier FROM auswählen. Wir brauchen nur ein Set, mit dem wir arbeiten können, damit wir die Anzahl der Zeilen zählen. Ich persönlich benutze eine TVF, einige verwenden einen CTE, andere verwenden stattdessen eine Zahlentabelle. Ich befürworte die Verwendung der leistungsfähigsten Lösung, die Sie auch verstehen.

Die Kombination dieser beiden Methoden löst unser Problem:

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));

Das obige Beispiel ist schrecklicher Code, zeigt aber, wie alles zusammenkommt.

Mehr Spaß

Ich muss so viel tun, also habe ich die Logik in zwei TVFs gekapselt. Der erste generiert einen Zahlenbereich und der zweite verwendet diese Funktion, um einen Datumsbereich zu generieren. Die Mathematik soll sicherstellen, dass die Eingabereihenfolge keine Rolle spielt, und ich wollte den gesamten in GenerateRangeSmallInt verfügbaren Zahlenbereich verwenden.

Die folgende Funktion benötigt ~ 16ms CPU-Zeit, um den maximalen Bereich von 65536 Datums zurückzugeben.

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

Es ist eine gute Idee, diese Daten schnell zu generieren. Ich fühle mich jedoch nicht wohl dabei, dies mit einer ziemlich großen Reichweite zu tun, also habe ich die folgende Lösung gefunden:

  1. Eine Tabelle "DatesNumbers" erstellt, die Zahlen enthält, die für die Datumsberechnung verwendet werden:

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

  1. Die Tabelle wurde unter Verwendung der obigen Techniken mit Zahlen von -59999 bis 40000 ausgefüllt. Dieser Bereich gibt mir Daten von 59999 Tagen (~ 164 Jahre) bis 40000 Tage (109 Jahre) voraus:

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. Eine Ansicht "Datumsangaben" erstellt:

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

Das ist es.

  • (+) Leicht lesbare Abfragen
  • (+) Nein, fliegende Zahlengenerationen 
  • (+) Gibt Datumsangaben in der Vergangenheit und in der Zukunft an und es ist KEINE UNION in Sicht, wie in diesem Beitrag .
  • (+) "Nur in der Vergangenheit" oder "Nur in der Zukunft" könnten Datumsangaben mit WHERE i < 0 oder WHERE i > 0 (PK) gefiltert werden.
  • (-) 'temporäre' Tabelle und Ansicht wird verwendet
1
fifonik

Eine weitere Lösung für mysql 8.0.1 und mariadb 10.2.2 mit rekursiven allgemeinen Tabellenausdrücken:

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

Sie möchten einen Datumsbereich erhalten.

In Ihrem Beispiel möchten Sie die Daten zwischen '2010-01-20' und '2010-01-24' erhalten.

mögliche Lösung:

 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'

Erklärung

MySQL hat also eine date_add - Funktion

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

werde dir geben

2010-01-21

Die Funktion datediff weist Sie häufig darauf hin, dass Sie dies wiederholen müssen

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

die zurückkehrt

 4

Das Abrufen einer Datumsliste in einem Datumsbereich läuft darauf hinaus, eine Folge von Ganzzahlen zu erstellen. Siehe Generieren einer Ganzzahlfolge in MySQL

Die am häufigsten vorgelegte Antwort hier hat einen ähnlichen Ansatz wie https://stackoverflow.com/a/2652051/1497139 als Grundlage genommen:

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

was dazu führen wird

row
1.0
2.0
3.0
4.0

Die Zeilen können jetzt verwendet werden, um eine Datumsliste ab dem angegebenen Startdatum zu erstellen. Um das Startdatum anzugeben, beginnen wir mit Zeile -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 von RedFilters Top-Lösung

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

Okay .. Versuchen Sie Folgendes: http://www.devshed.com/c/a/MySQL/Delving-Deeper-into-MySQL-50/
http://dev.mysql.com/doc/refman/5.0/de/loop-statement.html
http://www.roseindia.net/sql/mysql-example/mysql-loop.shtml

Verwenden Sie dies, um beispielsweise eine Temp-Tabelle zu generieren, und wählen Sie dann * in der Temp-Tabelle aus. Oder geben Sie die Ergebnisse einzeln aus.
Was Sie sagen, was Sie tun möchten, kann nicht mit einer SELECT-Anweisung ausgeführt werden, aber es kann mit den für MySQL spezifischen Dingen machbar sein.
Dann brauchen Sie vielleicht noch einen Cursor: http://dev.mysql.com/doc/refman/5.0/de/cursors.html

0
Trevoke

Elegante Lösung mit neuer rekursiver Funktionalität (Common Table Expressions) in MariaDB> = 10.3 und 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;

Das Obige gibt eine Datumstabelle zwischen '2019-01-01' und '2019-04-30' zurück. Es ist auch anständig schnell. Die Rückgabe von Daten aus einem Zeitraum von 1000 Jahren (~ 365.000 Tage) dauert auf meiner Maschine etwa 400 ms.

0
Brad

wenn Sie die Liste der Daten zwischen zwei Daten wünschen:

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

* Geige hier: 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

Für Oracle lautet meine Lösung:

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

Sysdate kann auf ein bestimmtes Datum geändert werden, und die Stufennummer kann geändert werden, um mehr Daten zu erhalten.

0
mousetwentytwo

Kann eine Prozedur erstellen, um auch Kalendertabelle mit Timestmap vom Tag abweichend zu erstellen. Wenn Sie für jedes Quartal eine Tabelle wünschen 

z.B.

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

sie können verwenden 

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


und dann durch manipulieren

select ts, dt from calendar_table_tmp;

das gibt dir auch 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'

von hier aus können Sie weitere Informationen hinzufügen, wie z 

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

oder erstellen Sie eine echte Tabelle mit Tabellenanweisung erstellen

0

verbessert mit Wochentag und Hinzufügen einer benutzerdefinierten Feiertagstabelle Microsoft MSSQL 2012 für Powerpivot-Datentabelle 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