標準の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
編集:はっきりしていないようです。このリストを生成したいです。私はデータベースに値を(日時によって)保存していますが、それらを上記のような日付のリストへの左外部結合で集約したいです(この結合のいくつかの右側から数日間ヌルを期待し、これを処理します) )。
このストアドプロシージャを使用して、必要な間隔を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用の同様の関数を作成しました。
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
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バイト])、物事のスキームではごくわずかなテーブルです。
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回インクリメントされます。
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の日付のリストを生成できます。大きくする必要がある場合は、内部クエリに別のクロス結合を追加できます。
Access(または任意のSQL言語)
2つのフィールドを持つテーブルを1つ作成します。このテーブルをtempRunDates
と呼びます。
-フィールドfromDate
およびtoDate
-その後、開始日と終了日を持つ1つのレコードのみを挿入します。
別のテーブルを作成します:Time_Day_Ref
-日付のリスト(Excelでリストを作成するのは簡単です)をこのテーブルにインポートします。
-私の場合のフィールド名はGreg_Dt
、グレゴリオ暦
-2009年1月1日から2020年1月1日までリストを作成しました。
クエリを実行します。
SELECT Time_Day_Ref.GREG_DT
FROM tempRunDates, Time_Day_Ref
WHERE Time_Day_Ref.greg_dt>=tempRunDates.fromDate And greg_dt<=tempRunDates.toDate;
簡単!
通常、この目的のために通常保持する補助番号テーブルを使用しますが、これにはいくつかのバリエーションがあります。
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
テーブル値関数などのバリエーションを見てきました。
日付の永続的なリストを保持することもできます。データウェアハウスにも時刻のリストもあります。
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
SQLサーバーで指定された2つの日付の間の日付を見つける方法については、 http://ektaraval.blogspot.com/2010/09/writing-recursive-query-to-find-out-all.html で説明しています。 =
このソリューションは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
等々。
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
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
私はこれとかなり長い間戦ってきました。これは、ソリューションを検索したときに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行しか返されませんでした。
統計のために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
私は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)
これが多くの人を助けることを願っています。
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」の間の日付のテーブルを返します。
2つのパラメーターa_beginとa_endを受け取るストアドプロシージャを作成します。その中にtという一時テーブルを作成し、変数dを宣言し、a_beginをdに割り当て、WHILE
loop INSERT
ing dをtに実行し、ADDDATE
関数を呼び出して値d。最後に SELECT * FROM t
。
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 $$
この手順は、年の初めから現在までのすべての日付を挿入し、「開始」と「終了」の日を置き換えるだけで準備完了です。
私はこれに似たものを使用します:
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つの日付の間で日ごとにインクリメントされるすべての日付が保持され、心のコンテンツで参加する準備ができています。