web-dev-qa-db-ja.com

2つの日付の間の日付のリストを取得する

標準のmysql関数を使用すると、2つの日付間の日のリストを返すクエリを作成する方法があります。

たとえば、2009-01-01と2009-01-13を指定すると、次の値を持つ1列のテーブルが返されます。

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

編集:はっきりしていないようです。このリストを生成したいです。私はデータベースに値を(日時によって)保存していますが、それらを上記のような日付のリストへの左外部結合で集約したいです(この結合のいくつかの右側から数日間ヌルを期待し、これを処理します) )。

85
Gilgad

このストアドプロシージャを使用して、必要な間隔をtime_intervalsという名前の一時テーブルに生成してから、JOINし、一時time_intervalsテーブル。

プロシージャは、指定されたすべての異なるタイプの間隔を生成できます。

call make_intervals('2009-01-01 00:00:00','2009-01-10 00:00:00',1,'DAY')
.
select * from time_intervals  
.
interval_start      interval_end        
------------------- ------------------- 
2009-01-01 00:00:00 2009-01-01 23:59:59 
2009-01-02 00:00:00 2009-01-02 23:59:59 
2009-01-03 00:00:00 2009-01-03 23:59:59 
2009-01-04 00:00:00 2009-01-04 23:59:59 
2009-01-05 00:00:00 2009-01-05 23:59:59 
2009-01-06 00:00:00 2009-01-06 23:59:59 
2009-01-07 00:00:00 2009-01-07 23:59:59 
2009-01-08 00:00:00 2009-01-08 23:59:59 
2009-01-09 00:00:00 2009-01-09 23:59:59 
.
call make_intervals('2009-01-01 00:00:00','2009-01-01 02:00:00',10,'MINUTE')
. 
select * from time_intervals
.  
interval_start      interval_end        
------------------- ------------------- 
2009-01-01 00:00:00 2009-01-01 00:09:59 
2009-01-01 00:10:00 2009-01-01 00:19:59 
2009-01-01 00:20:00 2009-01-01 00:29:59 
2009-01-01 00:30:00 2009-01-01 00:39:59 
2009-01-01 00:40:00 2009-01-01 00:49:59 
2009-01-01 00:50:00 2009-01-01 00:59:59 
2009-01-01 01:00:00 2009-01-01 01:09:59 
2009-01-01 01:10:00 2009-01-01 01:19:59 
2009-01-01 01:20:00 2009-01-01 01:29:59 
2009-01-01 01:30:00 2009-01-01 01:39:59 
2009-01-01 01:40:00 2009-01-01 01:49:59 
2009-01-01 01:50:00 2009-01-01 01:59:59 
.
I specified an interval_start and interval_end so you can aggregate the 
data timestamps with a "between interval_start and interval_end" type of JOIN.
.
Code for the proc:
.
-- drop procedure make_intervals
.
CREATE PROCEDURE make_intervals(startdate timestamp, enddate timestamp, intval integer, unitval varchar(10))
BEGIN
-- *************************************************************************
-- Procedure: make_intervals()
--    Author: Ron Savage
--      Date: 02/03/2009
--
-- Description:
-- This procedure creates a temporary table named time_intervals with the
-- interval_start and interval_end fields specifed from the startdate and
-- enddate arguments, at intervals of intval (unitval) size.
-- *************************************************************************
   declare thisDate timestamp;
   declare nextDate timestamp;
   set thisDate = startdate;

   -- *************************************************************************
   -- Drop / create the temp table
   -- *************************************************************************
   drop temporary table if exists time_intervals;
   create temporary table if not exists time_intervals
      (
      interval_start timestamp,
      interval_end timestamp
      );

   -- *************************************************************************
   -- Loop through the startdate adding each intval interval until enddate
   -- *************************************************************************
   repeat
      select
         case unitval
            when 'MICROSECOND' then timestampadd(MICROSECOND, intval, thisDate)
            when 'SECOND'      then timestampadd(SECOND, intval, thisDate)
            when 'MINUTE'      then timestampadd(MINUTE, intval, thisDate)
            when 'HOUR'        then timestampadd(HOUR, intval, thisDate)
            when 'DAY'         then timestampadd(DAY, intval, thisDate)
            when 'WEEK'        then timestampadd(WEEK, intval, thisDate)
            when 'MONTH'       then timestampadd(MONTH, intval, thisDate)
            when 'QUARTER'     then timestampadd(QUARTER, intval, thisDate)
            when 'YEAR'        then timestampadd(YEAR, intval, thisDate)
         end into nextDate;

      insert into time_intervals select thisDate, timestampadd(MICROSECOND, -1, nextDate);
      set thisDate = nextDate;
   until thisDate >= enddate
   end repeat;

 END;

this post の下部にある同様のデータシナリオの例では、SQL Server用の同様の関数を作成しました。

65
Ron Savage

MSSQLの場合、これを使用できます。とても速いです。

これをテーブル値関数またはストアドプロシージャでラップし、開始日と終了日を変数として解析できます。

DECLARE @startDate DATETIME
DECLARE @endDate DATETIME

SET @startDate = '2011-01-01'
SET @endDate = '2011-01-31';

WITH dates(Date) AS 
(
    SELECT @startdate as Date
    UNION ALL
    SELECT DATEADD(d,1,[Date])
    FROM dates 
    WHERE DATE < @enddate
)

SELECT Date
FROM dates
OPTION (MAXRECURSION 0)
GO
28
Richard

BIRTレポートでも同様の問題があり、データのない日にレポートしたかったのです。これらの日付のエントリがなかったため、私たちにとって最も簡単な解決策は、すべての日付を保存する単純なテーブルを作成し、それを使用して範囲を取得するか、結合してその日付のゼロ値を取得することでした。

毎月実行されるジョブを使用して、5年先までテーブルにデータが入力されるようにします。このようにしてテーブルが作成されます:

create table all_dates (
    dt date primary key
);

異なるDBMSでこれを行うには、魔法のトリッキーな方法があることは間違いありませんが、常に最も単純なソリューションを選択します。テーブルのストレージ要件は最小限であり、クエリが非常に簡単で移植可能になります。この種のソリューションは、データの行ごとの計算を必要としないため、パフォーマンスの観点からほとんど常に優れています。

もう1つのオプション(およびこれを以前に使用したこともあります)は、すべての日付についてテーブルにエントリがあることを確認することです。テーブルを定期的にスイープし、存在しない日付や時刻にゼロエントリを追加しました。これはオプションではないかもしれません、それは保存されたデータに依存します。

本当にall_datesテーブルにデータを格納するのは面倒だと思う場合、ストアドプロシージャを使用して、これらの日付を含むデータセットを返します。これは、事前に計算されたデータをテーブルから取得するだけでなく、呼び出されるたびに範囲を計算する必要があるため、ほぼ確実に遅くなります。

しかし、正直に言うと、深刻なデータストレージの問題なしにテーブルを1000年にわたって作成することができます-365,000(たとえば)16バイトの日付と、日付を複製するインデックスと安全のための20%のオーバーヘッド約14M [365,000 * 16 * 2 * 1.2 = 14,016,000バイト])、物事のスキームではごくわずかなテーブルです。

13
paxdiablo

MySQLの ユーザー変数 は次のように使用できます。

SET @num = -1;
SELECT DATE_ADD( '2009-01-01', interval @num := @num+1 day) AS date_sequence, 
your_table.* FROM your_table
WHERE your_table.other_column IS NOT NULL
HAVING DATE_ADD('2009-01-01', interval @num day) <= '2009-01-13'

@numは、初めて使用するときに追加するため、-1です。また、「HAVING date_sequence」を使用することはできません。これにより、行ごとにユーザー変数が2回インクリメントされます。

12
Andrew Vit

this answerからアイデアを借りて、0から9のテーブルをセットアップし、それを使用して日付のリストを生成できます。

CREATE TABLE num (i int);
INSERT INTO num (i) VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);

select adddate('2009-01-01', numlist.id) as `date` from
(SELECT n1.i + n10.i*10 + n100.i*100 AS id
   FROM num n1 cross join num as n10 cross join num as n100) as numlist
where adddate('2009-01-01', numlist.id) <= '2009-01-13';

これにより、最大1000の日付のリストを生成できます。大きくする必要がある場合は、内部クエリに別のクロス結合を追加できます。

8
Logan5

Access(または任意のSQL言語)

  1. 2つのフィールドを持つテーブルを1つ作成します。このテーブルをtempRunDatesと呼びます。
    -フィールドfromDateおよびtoDate
    -その後、開始日と終了日を持つ1つのレコードのみを挿入します。

  2. 別のテーブルを作成します:Time_Day_Ref
    -日付のリスト(Excelでリストを作成するのは簡単です)をこのテーブルにインポートします。
    -私の場合のフィールド名はGreg_Dt、グレゴリオ暦
    -2009年1月1日から2020年1月1日までリストを作成しました。

  3. クエリを実行します。

    SELECT Time_Day_Ref.GREG_DT
    FROM tempRunDates, Time_Day_Ref
    WHERE Time_Day_Ref.greg_dt>=tempRunDates.fromDate And greg_dt<=tempRunDates.toDate;
    

簡単!

3
Nathan Wood

通常、この目的のために通常保持する補助番号テーブルを使用しますが、これにはいくつかのバリエーションがあります。

SELECT *
FROM (
    SELECT DATEADD(d, number - 1, '2009-01-01') AS dt
    FROM Numbers
    WHERE number BETWEEN 1 AND DATEDIFF(d, '2009-01-01', '2009-01-13') + 1
) AS DateRange
LEFT JOIN YourStuff
    ON DateRange.dt = YourStuff.DateColumn

テーブル値関数などのバリエーションを見てきました。

日付の永続的なリストを保持することもできます。データウェアハウスにも時刻のリストもあります。

2
Cade Roux
CREATE FUNCTION [dbo].[_DATES]
(
    @startDate DATETIME,
    @endDate DATETIME
)
RETURNS 
@DATES TABLE(
    DATE1 DATETIME
)
AS
BEGIN
    WHILE @startDate <= @endDate
    BEGIN 
        INSERT INTO @DATES (DATE1)
            SELECT @startDate   
    SELECT @startDate = DATEADD(d,1,@startDate) 
    END
RETURN
END
2
Todd Dickerson

SQLサーバーで指定された2つの日付の間の日付を見つける方法については、 http://ektaraval.blogspot.com/2010/09/writing-recursive-query-to-find-out-all.html で説明しています。 =

2
Ekta

このソリューションはMySQL 5.0で動作しています
テーブルを作成します-mytable
スキーマは重要ではありません。重要なのは、その中の行の数です。
したがって、10行、値-1〜10のINT型の列を1つだけ保持できます。

SQL:

set @tempDate=date('2011-07-01') - interval 1 day;
select
date(@tempDate := (date(@tempDate) + interval 1 day)) as theDate
from mytable x,mytable y
group by theDate
having theDate <= '2011-07-31';

制限:上記のクエリで返される日付の最大数は
(rows in mytable)*(rows in mytable) = 10*10 = 100.

SQLのフォーム部分を変更することにより、この範囲を増やすことができます。
mytable x、mytable y、mytable zから
したがって、範囲は10*10*10 =1000 等々。

1
Vihang Patil

HRMSシステムでこれを使用しました。

SELECT CAST(DAYNAME(daydate) as CHAR) as dayname,daydate
    FROM
    (select CAST((date_add('20110101', interval H.i*100 + T.i*10 + U.i day) )as DATE) as daydate
      from erp_integers as H
    cross
      join erp_integers as T
    cross
      join erp_integers as U
     where date_add('20110101', interval H.i*100 + T.i*10 + U.i day ) <= '20110228'
    order
        by daydate ASC
        )Days
1
Anas
select * from table_name where col_Date between '2011/02/25' AND DATEADD(s,-1,DATEADD(d,1,'2011/02/27'))

ここでは、まず現在のendDateに日を追加します。これは2011-02-28 00:00:00になります。次に、1秒を減算して終了日2011-02-27 23:59:59を作成します。これを行うことにより、指定した間隔の間のすべての日付を取得できます。

出力:
2011/02/25
2011/02/26
2011/02/27

0
Chandra Prakash

私はこれとかなり長い間戦ってきました。これは、ソリューションを検索したときにGoogleで最初にヒットしたものなので、これまでに取得した場所に投稿してみましょう。

SET @d := '2011-09-01';
SELECT @d AS d, cast( @d := DATE_ADD( @d , INTERVAL 1 DAY ) AS DATE ) AS new_d
  FROM [yourTable]
  WHERE @d <= '2012-05-01';

置換[yourTable]データベースのテーブルを使用します。秘Theは、選択するテーブルの行数は、返される日付の数以上でなければならないということです。テーブルプレースホルダーDUALを使用してみましたが、1行しか返されませんでした。

0
champ

統計のために2つの日付の間のすべての月のリストが必要でした。 2つの日付は、サブスクリプションの開始日と終了日です。 つまり、リストにはすべての月と月ごとのサブスクリプションの量が表示されます。

MYSQL

CREATE PROCEDURE `get_amount_subscription_per_month`()
BEGIN
   -- Select the ultimate start and enddate from subscribers
   select @startdate := min(DATE_FORMAT(a.startdate, "%Y-%m-01")), 
          @enddate := max(DATE_FORMAT(a.enddate, "%Y-%m-01")) + interval 1 MONTH
   from subscription a;

   -- Tmp table with all months (dates), you can always format them with DATE_FORMAT) 
   DROP TABLE IF EXISTS tmp_months;
   create temporary table tmp_months (
      year_month date,
      PRIMARY KEY (year_month)
   );


   set @tempDate=@startdate;  #- interval 1 MONTH;

   -- Insert every month in tmp table
   WHILE @tempDate <= @enddate DO
     insert into tmp_months (year_month) values (@tempDate);
     set @tempDate = (date(@tempDate) + interval 1 MONTH);
   END WHILE;

   -- All months
   select year_month from tmp_months;

   -- If you want the amount of subscription per month else leave it out
   select mnd.year_month, sum(subscription.amount) as subscription_amount
   from tmp_months mnd
   LEFT JOIN subscription ON mnd.year_month >= DATE_FORMAT(subscription.startdate, "%Y-%m-01") and mnd.year_month <= DATE_FORMAT(subscription.enddate, "%Y-%m-01")
   GROUP BY mnd.year_month;

 END
0
Wow

私はServer version: 5.7.11-log MySQL Community Server (GPL)を使用しています

これを簡単な方法で解決します。

"datetable"という名前のテーブルを作成しました

mysql> describe datetable;
+---------+---------+------+-----+---------+-------+
| Field   | Type    | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+-------+
| colid   | int(11) | NO   | PRI | NULL    |       |
| coldate | date    | YES  |     | NULL    |       |
+---------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

これで、挿入されたレコードが内部に表示されます。

mysql> select * from datetable;
+-------+------------+
| colid | coldate    |
+-------+------------+
|   101 | 2015-01-01 |
|   102 | 2015-05-01 |
|   103 | 2016-01-01 |
+-------+------------+
3 rows in set (0.00 sec)

そして、ここでは、これらの日付ではなく2つの日付内のレコードを取得するクエリを示します。

mysql> select * from datetable where coldate > '2015-01-01' and coldate < '2016-01-01';
+-------+------------+
| colid | coldate    |
+-------+------------+
|   102 | 2015-05-01 |
+-------+------------+
1 row in set (0.00 sec)

これが多くの人を助けることを願っています。

0
ArifMustafa

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」の間の日付のテーブルを返します。

0
Brad

2つのパラメーターa_beginとa_endを受け取るストアドプロシージャを作成します。その中にtという一時テーブルを作成し、変数dを宣言し、a_beginをdに割り当て、WHILE loop INSERTing dをtに実行し、ADDDATE関数を呼び出して値d。最後に SELECT * FROM t

0
Eugene Yokota
DELIMITER $$  
CREATE PROCEDURE popula_calendario_controle()
   BEGIN
      DECLARE a INT Default 0;
      DECLARE first_day_of_year DATE;
      set first_day_of_year = CONCAT(DATE_FORMAT(curdate(),'%Y'),'-01-01');
      one_by_one: LOOP
         IF dayofweek(adddate(first_day_of_year,a)) <> 1 THEN
            INSERT INTO calendario.controle VALUES(null,150,adddate(first_day_of_year,a),adddate(first_day_of_year,a),1);
         END IF;
         SET a=a+1;
         IF a=365 THEN
            LEAVE one_by_one;
         END IF;
      END LOOP one_by_one;
END $$

この手順は、年の初めから現在までのすべての日付を挿入し、「開始」と「終了」の日を置き換えるだけで準備完了です。

0
Julio Marins

私はこれに似たものを使用します:

DECLARE @DATEFROM AS DATETIME
DECLARE @DATETO AS DATETIME
DECLARE @HOLDER TABLE(DATE DATETIME)

SET @DATEFROM = '2010-08-10'
SET @DATETO = '2010-09-11'

INSERT INTO
    @HOLDER
        (DATE)
VALUES
    (@DATEFROM)

WHILE @DATEFROM < @DATETO
BEGIN

    SELECT @DATEFROM = DATEADD(D, 1, @DATEFROM)
    INSERT 
    INTO
        @HOLDER
            (DATE)
    VALUES
        (@DATEFROM)
END

SELECT 
    DATE
FROM
    @HOLDER

そうして @HOLDER変数テーブルには、これら2つの日付の間で日ごとにインクリメントされるすべての日付が保持され、心のコンテンツで参加する準備ができています。

0