日付範囲から日数を生成する


135

次のようなクエリを実行したい

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

そして、次のようなデータを返します:

日々
----------
2010-01-20
2010-01-21
2010-01-22
2010-01-23
2010-01-24

10
この質問に添付されている他の問題はありません。上記の質問は、SQLコースを習得する際の問題です。
Pentium10 2010年

選択した日付範囲に基づく日付の配列が必要ですか?
デレク・アデア

1
問題を見つけるための使用法を考えています...テーブル内の欠落しているレコードを埋めるタスクが発生した場合。そして、あなたは私が何かを考えている毎日クエリを実行しなければなりませんinsert into table select ... as days date between '' and ''
Pentium10

13
その使用例は、統計を生成し、データがない日付の行を含めることです。ある種のgroup-byを実行している場合、データをそのまま言語にダンプする代わりに、SQLですべての情報を実際に生成して必要な形式に追加する方がはるかに速く、ループと追加を開始できます。空。
ナンヌ2013

1
@Nanneこそが、私がこの質問を保存した理由です。特定の日付では存在しない可能性のあるデータに結合するには、上記が必要です。
Josh Diehl 2016年

回答:


318

このソリューションは、ループ、プロシージャ、または一時テーブルを使用しません。サブクエリは過去10,000日間の日付を生成し、必要に応じて前後に拡張することができます。

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' 

出力:

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

パフォーマンスに関する注意

ここでテストすると、パフォーマンスは驚くほど良好です。上記のクエリには0.0009秒かかります。

サブクエリを拡張して約を生成すると、100,000の数値(したがって、約274年分の日付)、0.0458秒で実行されます。

ちなみに、これは非常に移植可能な手法であり、ほとんどのデータベースでわずかな調整を行うだけで機能します。

1,000日を返すSQL Fiddleの例


6
に変更UNIONすると、パフォーマンスが向上しますUNION ALL-存在しない重複を削除するための重複チェックに時間を費やすことになります。ただし、IMOは複雑すぎます。UNIONを使用して結果セットを作成する場合は、日付を指定してそれで終了しないのはなぜですか。
OMGポニー

7
なぜちょうど日付を指定し、それを行うことがありません -上記の方法は、あなたが示唆されている方法で、ハードコードに痛みを伴うだろう何のテーブルの作成を必要としない数字(および日付)の任意の大きなセットを作成することができますので。明らかに5つの日付については、やり過ぎです。しかし、それでも、事前に日付がわからず、潜在的な最小値と最大値だけがわかっているテーブルに対して結合する場合は、理にかなっています。
RedFilter 2010年

2
作成済みのUNIONステートメントの代わりにDATETIME関数を使用するのは「面倒」です。これにより、追加する必要があったロジックの必要性が軽減されます。したがって、クエリが複雑すぎます。どちらの方法でも、UNIONステートメントはスケーラブルではありません。たとえば、20または30の日付に対応できるように更新したい日付または数値を指定していますか?
OMGポニー2010年

23
質問への答えを見るのは本当にいいことです。無限のコメントではなく、それがどのようにして実行できないか、または実行してはならないかはわかりません。ほとんどのことを行うことができますが、「すべき」というのは文脈においてのみ意味があり、誰にとっても異なります。ほとんどの状況でより良い方法があることをよく知っていますが、この答えは私を助けました。
joe

7
このクエリを機能させることができない方:顔を叩いてから、1000の日付を生成するこのクエリに関するOPのコメントをもう一度読んでください。2010年は1000日以上前なので、それに応じてクエリを調整する必要があります。
Noel Baron、

32

ビューを使用した別のバリエーションを次に示します。

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;

そして、あなたは単にそれを行うことができます(それがどれほどエレガントであるかを見てください?):

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

更新

現在の日付から始まる過去の日付のみを生成できることに注意してください。任意の種類の日付範囲(過去、将来、およびその中間)を生成する場合は、代わりにこのビューを使用する必要があります。

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;

1
これはすべてのケースで機能するわけではありません。「2014-12-01」と「2014-12-28」の間の日付WHERE BY日付から日付を選択するORDER BY日付
vasanth

3
@ user927258にお電話ください。これは、上記の最初のビューdatesが現在の日付から始まる日付を計算するためです。そのため、将来設定された日付を取得することはできません。@RedFilterからの回答にも同じ設計上の欠陥があります。私は私の答えに回避策を追加しました。
ステファン・

一部のビューを使用すると、クエリが確実に簡略化され、再利用できるようになります。本質的に同じことを行っていますが、これらのUNION句はすべて1つのSQLステートメントでは奇妙に見えます。
Stewart

24

受け入れられた回答がPostgreSQLで機能しませんでした(「a」またはその近くの構文エラー)。

PostgreSQLでこれを行う方法は、次のgenerate_series関数を使用することです。

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)

14

再帰的な共通テーブル式(CTE)を使用して、日付のリストを生成し、それから選択できます。当然、通常は300万の日付を作成する必要はないので、これは可能性を示しているにすぎません。CTE内の日付範囲を制限し、CTEを使用してselectステートメントからwhere句を省略できます。

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)

Microsoft SQL Server 2005では、すべての可能な日付のCTEリストの生成に1:08かかりました。100年の生成には1秒もかかりませんでした。


7

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

出力

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

2
もう少し下にスクロールしただけだったら...ため息。とにかくありがとうございます。CAST(<expression> AS DATE)を追加して、バージョンの時間を削除しました。GETDATE()-365とGETDATE()の間のa.Dateでも使用されます...今日クエリを実行した場合、WHERE = P
Ricardo C

4

ループやカーソルを使用せずにこれを行う従来の解決策NUMBERSは、1から始まる値を持つ単一のInteger列を持つテーブルを作成することです。

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

ニーズに対応するのに十分なレコードをテーブルに入力する必要があります。

INSERT INTO NUMBERS (id) VALUES (NULL);

NUMBERSテーブルを取得したら、以下を使用できます。

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'

絶対的なローテクソリューションは次のとおりです。

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

何に使用しますか?


左に参加するために日付または数値のリストを生成します。これは、シーケンシャルデータのリストに左結合しているため、データにギャップがある場所を確認するために行います。null値を指定すると、ギャップが存在する場所が明らかになります。


1
このDUALテーブルは、OracleとMySQLでサポートされており、このFROM句で代替テーブルとして使用されます。それは存在しません。そこから値を選択すると、値が何であっても返されます。SELECTクエリにはFROM少なくとも1つのテーブルを指定する句が必要であるため、このアイデアは代用になりました。
OMGポニー2010年

1
+1は、クエリが必要になるたびにRDBMSで作成するのではなく、永続的な数値テーブルを実際に作成するためのものです。補助テーブルは悪ではありません、人々!
ベーコンビット

4

Access 2010の場合 -複数の手順が必要です。私は上記と同じパターンに従いましたが、Accessの誰かを助けることができると思いました。うまくいったので、シードされた日付のテーブルを保持する必要はありませんでした。

DUALと呼ばれるテーブルを作成します(Oracle DUALテーブルの機能と同様)。

  • ID(オートナンバー)
  • DummyColumn(テキスト)
  • 1つの行の値を追加します(1、 "DummyRow")

「ZeroThru9Q」という名前のクエリを作成します。次の構文を手動で入力します。

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;

「TodayMinus1KQ」という名前のクエリを作成します(今日より前の日付の場合)。次の構文を手動で入力します。

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

"TodayPlus1KQ"という名前のクエリを作成します(今日以降の日付の場合)。次の構文を手動で入力します。

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;

"TodayPlusMinus1KQ"という名前のユニオンクエリを作成します(日付+/- 1000日の場合):

SELECT MyDate
FROM TodayMinus1KQ
UNION
SELECT MyDate
FROM TodayPlus1KQ;

これでクエリを使用できます。

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

3

手順+一時テーブル:

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

3

thx Pentium10-あなたは私にstackoverflowに参加させました:)-これは私のmsaccessへの移植です-どのバージョンでも機能すると思います:

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は、from句で、「アクセスにはテーブルカウントが必要なので」少なくとも1つのレコードを使用します。少なくとも1つのレコードを持つテーブルであれば十分です。


2

すでに与えられたすばらしい答えの多くで述べられている(または少なくともほのめかされている)ので、この問題は、使用する一連の数値を取得すると簡単に解決されます。

注:以下はT-SQLですが、これは既にここで、またインターネット全体で言及されている一般的な概念の特定の実装にすぎません。コードを選択した方言に変換するのは比較的簡単なはずです。

どうやって?次のクエリを検討してください。

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

上記は、日付範囲1/22/0001-1/27/0001を生成し、非常に簡単です。:上記のクエリ内の情報のキー2枚あります開始日0001-01-22オフセットのは5。これら2つの情報を組み合わせると、明らかに終了日がわかります。したがって、2つの日付を指定すると、範囲の生成は次のように分解できます。

  • 2つの指定された日付の差(オフセット)を簡単に見つける:

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

    ABS()ここを使用すると、日付の順序が不適切になります。

  • 限られた数のセットを生成します。これも簡単です。

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

    FROMここで何を選択するかは実際には気にしません。セット内の行数をカウントするために使用するセットが必要です。私は個人的にTVFを使用しており、CTEを使用している人もいれば、代わりに数値表を使用している人もいます。私もあなたが理解している最も高性能なソリューションを使用することを提唱します。

これら2つの方法を組み合わせると、問題が解決します。

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

上記の例は恐ろしいコードですが、すべてが一体になる方法を示しています。

もっと楽しく

この種のことをたくさんする必要があるので、ロジックを2つのTVFにカプセル化しました。1つ目は数値の範囲を生成し、2つ目はこの機能を使用して日付の範囲を生成します。計算は、入力順序が問題にならないことを保証することであり、私はで利用可能なすべての範囲の数値を使用したかったためですGenerateRangeSmallInt

次の関数では、最大範囲の65536の日付を返すために、最大16ミリ秒のCPU時間がかかります。

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

2

これを試して。

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

日付範囲を取得したいと考えています。

あなたの例では、「2010-01-20」と「2010-01-24」の間の日付を取得したいとします

可能な解決策:

 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'

説明

MySQLにはdate_add関数があるので

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

あなたにあげます

2010-01-21

DATEDIFFの機能を使用すると、多くの場合、あなたはこれを繰り返す必要があるだろう知っているでしょう

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

戻る

 4

日付範囲の日付のリストを取得すると、整数のシーケンス作成することになります。MySQLで整数シーケンス生成するを参照してください。

ここで最も支持されている答えは、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

その結果

row
1.0
2.0
3.0
4.0

行を使用して、指定した開始日からの日付のリストを作成できるようになりました。開始日を含めるには、行-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'

1

数日以上必要になる場合は、テーブルが必要です。

MySQLで日付範囲を作成する

その後、

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

3
上記の返信には表は必要なく、解決策を提供するので、なぜこれを投稿したのですか?
Pentium10、2010年

1

2つの日付フィールドの間に日付を生成する

SQL CTEクエリに精通している場合、このソリューションは質問の解決に役立ちます

ここに例があります

1つのテーブルに日付があります

テーブル名:「testdate」

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

結果が必要:

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

解決:

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

説明:CTE再帰クエリの説明

  • クエリの最初の部分:

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

    説明:最初の列は「開始日」、2番目の列は開始日と終了日の日数の差であり、「差」列と見なされます

  • クエリの2番目の部分:

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

    説明:ユニオンはすべて、結果がnullになるまで上記のクエリの結果を継承します。したがって、「StartTime」の結果は生成されたCTEクエリから継承され、diff、reduce-1から継承されるため、3、2、1のようになります。 0まで

例えば

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

結果仕様

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番目の部分

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

    「startdate」に「diff」の日が追加されるため、結果は次のようになります。

結果

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

1

受け入れられた回答よりも短い、同じ考え:

(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

これを保存されたビューとして使用したい人のために(MySQLはビュー内のネストされたselectステートメントをサポートしていません):

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;

その後、行うことができます

select * from date_range

取得するため

date
---
2017-06-06
2017-06-05
2017-06-04
2017-06-03
2017-06-02
...

1

MariaDB> = 10.3および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;

上記は、「2019-01-01」から「2019-04-30」までの日付のテーブルを返します。また、かなり高速です。私のマシンでは、1000年分の日付(約365,000日)を返すのに約400msかかります。


1

これらの日付をその場で生成することをお勧めします。しかし、私はこれを非常に広い範囲で行うのは快適ではないので、次の解決策に終わりました:

  1. 日付の計算に使用される数値を保持するテーブル「DatesNumbers」を作成しました:
CREATE TABLE DatesNumbers (
    i MEDIUMINT NOT NULL,
    PRIMARY KEY (i)
)
COMMENT='Used by Dates view'
;
  1. 上記の手法を使用して、表に-59999〜40000の数値を入力します。この範囲は、59999日(〜164年)遅れてから40000日(109年)先の日付を与えます。
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. ビュー「日付」を作成しました:
SELECT
      i,
      CURRENT_DATE() + INTERVAL i DAY AS Date
FROM
    DatesNumbers

それでおしまい。

  • (+)読みやすいクエリ
  • (+)オンザフライ番号世代
  • (+)過去と未来の日付を示し、この投稿のようにこれを考慮したUNIONはありません。
  • (+)「過去のみ」または「将来のみ」の日付は、WHERE i < 0またはWHERE i > 0(PK)を使用してフィルタリングできます。
  • (-)「一時的な」テーブルとビューが使用されます

0

了解しました。これを試してください: 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

これを使用して、たとえば一時テーブルを生成してから、一時テーブルでselect *を実行します。または、結果を1つずつ出力します。
あなたがやりたいことはSELECTステートメントはできませんが、MySQLに固有のことでできるかもしれません。
次に、カーソルが必要な場合があります:http : //dev.mysql.com/doc/refman/5.0/en/cursors.html


0

Oracleの場合、私の解決策は次のとおりです。

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

Sysdateは特定の日付に変更でき、レベル番号を変更してより多くの日付を指定できます。


0

2つの日付の間の日付のリストが必要な場合:

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

*ここでフィドル:http ://sqlfiddle.com/#!6/ 9eecb / 3469


0
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
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

SQLiteバージョンの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

平日、powerpivot日付テーブル用のカスタム休日テーブル microsoft MSSQL 2012に 参加することで改善されました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
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

日とは異なる timestmapでカレンダーテーブルを作成するプロシージャも作成できます 四半期ごとのテーブルが必要な場合

例えば

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

あなたは使うことができます

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

次に操作します

select ts, dt from calendar_table_tmp;

それはまたあなたに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'

ここから、次のような他の情報を追加できます

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

またはcreate tableステートメントで実際のテーブルを作成します


0

AWS MySQLで機能するより一般的な答え。

select datetable.Date
from (
    select date_format(adddate(now(),-(a.a + (10 * b.a) + (100 * c.a))),'%Y-%m-%d') 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 now() - INTERVAL 14 Day and Now()
order by datetable.Date DESC

-1

再帰的な共通テーブル式を使用したmysql 8.0.1およびmariadb 10.2.2のもう1つのソリューション:

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;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.