テーブルの各行には日付のタイムスタンプがあり、これからデータベースにクエリを実行して、過去30日間、その30日前などの行数をカウントしたいと思います。テーブルの先頭に戻る30日間のビンができるまで。
Pythonを使用し、いくつかのクエリを実行することで、このクエリを正常に実行できました。しかし、1つのMySQLクエリで実行できることはほぼ確実です。
少なくとも1つの行がある間隔をカウントする必要がある場合は、次のように使用できます。
select
datediff(curdate(), `date`) div 30 as block,
count(*) as rows_per_block
from
your_table
group by
block
また、これには開始日と終了日も表示されます。
select
datediff(curdate(), d) div 30 as block,
date_sub(curdate(),
INTERVAL (datediff(curdate(), `date`) div 30)*30 DAY) as start_block,
date_sub(curdate(),
INTERVAL (1+datediff(curdate(), `date`) div 30)*30-1 DAY) as end_block,
count(*)
from your_table
group by block
ただし、すべての間隔も表示する必要がある場合は、次のようなソリューションを使用できます。
select
num,
date_sub(curdate(),
INTERVAL (num+1)*30-1 DAY) as start_block,
date_sub(curdate(),
INTERVAL num*30 DAY) as end_block,
count(`date`)
from
numbers left join your_table
on `date` between date_sub(curdate(),
INTERVAL (num+1)*30-1 DAY) and
date_sub(curdate(),
INTERVAL num*30 DAY)
where num<=(datediff(curdate(), (select min(`date`) from your_table) ) div 30)
group by num
numbers
ただし、これにはテーブルがすでに準備されている必要があります。または、数値テーブルのないソリューションについては、ここでフィドルを参照してください。
ストアドプロシージャはなく、一時テーブルは1つだけで、日付列にインデックスが指定された効率的な実行プランは次のとおりです。
select
subdate(
'2012-12-31',
floor(dateDiff('2012-12-31', dateStampColumn) / 30) * 30 + 30 - 1
) as "period starting",
subdate(
'2012-12-31',
floor(dateDiff('2012-12-31', dateStampColumn) / 30) * 30
) as "period ending",
count(*)
from
YOURTABLE
group by floor(dateDiff('2012-12-31', dateStampColumn) / 30);
この呪文を除いて、ここで何が起こっているのかはかなり明白なはずです。
floor(dateDiff('2012-12-31', dateStampColumn) / 30)
その式は数回出現し、30日前の期間数と評価されdateStampColumn
ます。dateDiff
差を日数で返し、30で割って30日の期間で取得し、すべてをフィードしfloor()
て整数に丸めます。この数値を取得したら、それを実行できGROUP BY
ます。さらに、この数値を期間の開始日と終了日に変換するために、少し計算を行います。
必要に応じて交換'2012-12-31'
しnow()
てください。サンプルデータは次のとおりです。
CREATE TABLE YOURTABLE
(`Id` int, `dateStampColumn` datetime);
INSERT INTO YOURTABLE
(`Id`, `dateStampColumn`)
VALUES
(1, '2012-10-15 02:00:00'),
(1, '2012-10-17 02:00:00'),
(1, '2012-10-30 02:00:00'),
(1, '2012-10-31 02:00:00'),
(1, '2012-11-01 02:00:00'),
(1, '2012-11-02 02:00:00'),
(1, '2012-11-18 02:00:00'),
(1, '2012-11-19 02:00:00'),
(1, '2012-11-21 02:00:00'),
(1, '2012-11-25 02:00:00'),
(1, '2012-11-25 02:00:00'),
(1, '2012-11-26 02:00:00'),
(1, '2012-11-26 02:00:00'),
(1, '2012-11-24 02:00:00'),
(1, '2012-11-23 02:00:00'),
(1, '2012-11-28 02:00:00'),
(1, '2012-11-29 02:00:00'),
(1, '2012-11-30 02:00:00'),
(1, '2012-12-01 02:00:00'),
(1, '2012-12-02 02:00:00'),
(1, '2012-12-15 02:00:00'),
(1, '2012-12-17 02:00:00'),
(1, '2012-12-18 02:00:00'),
(1, '2012-12-19 02:00:00'),
(1, '2012-12-21 02:00:00'),
(1, '2012-12-25 02:00:00'),
(1, '2012-12-25 02:00:00'),
(1, '2012-12-26 02:00:00'),
(1, '2012-12-26 02:00:00'),
(1, '2012-12-24 02:00:00'),
(1, '2012-12-23 02:00:00'),
(1, '2012-12-31 02:00:00'),
(1, '2012-12-30 02:00:00'),
(1, '2012-12-28 02:00:00'),
(1, '2012-12-28 02:00:00'),
(1, '2012-12-30 02:00:00');
そして結果:
period starting period ending count(*)
2012-12-02 2012-12-31 17
2012-11-02 2012-12-01 14
2012-10-03 2012-11-01 5
期間のエンドポイントは包括的です。
SQLフィドルでこれを試してみてください。
一致する行がゼロの30日間は結果に含まれないという点で、少し間抜けな可能性があります。期間のテーブルに対してこれを結合できれば、それを排除できます。ただし、MySQLにはPostgreSQLのgenerate_series()のようなものがないため、アプリケーションで処理するか、この巧妙なハックを試す必要があります。
これを試して:
SELECT
DATE_FORMAT(t1.`Date`, '%Y-%m-%d'),
COUNT(t2.Id)
FROM
(
SELECT SUBDATE(CURDATE(), ID) `Date`
FROM
(
SELECT t2.digit * 10 + t1.digit + 1 AS id
FROM TEMP AS t1
CROSS JOIN TEMP AS t2
) t
WHERE Id <= 30
) t1
LEFT JOIN YOURTABLE t2 ON DATE(t1.`Date`) = DATE(t2.dateStampColumn)
GROUP BY t1.`Date`;
Temp
ただし、次のような一時テーブルを作成する必要があります。
CREATE TABLE TEMP
(Digit int);
INSERT INTO Temp VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
次のことを試してください。
SELECT Count(*)
FROM
yourtable
where
dateColumn between Now() and Now() - Interval 30 Day
30日間隔でアイソレートするためのより良い答えを得るには、ループが必要です。また、テーブルのmin(Date)と最後のループ日付の間に30日の間隔が必要なので、:)または、各30日の間隔の日付を保持する少なくとも別のテーブルに移動してから、参加します。
これは、暦月ごとにカウントされています。正確には必要なものではありません。
SELECT
extract(month from datecolumn),
count(*)
FROM
yourtable
GROUP BY
extract(month from datecolumn);
私の後者のコメントとStefanのコメントを考えて、ここに長いコードがありますが、適切な結果が得られています。私自身のサンプルデータに基づいており、MYSQLと互換性がありinterval
ます。SQL Serverで使用する必要がある場合は、DateADD
または同等の機能を使用してください。
サンプルデータ:
ID_MAIN FIELD1 FILTER
----------------------------------------
1 red August, 05 2012 00:00:00+0000
2 blue September, 15 2012 00:00:00+0000
3 pink September, 20 2012 00:00:00+0000
4 blue September, 27 2012 00:00:00+0000
5 blue October, 02 2012 00:00:00+0000
6 blue October, 16 2012 00:00:00+0000
7 blue October, 22 2012 00:00:00+0000
8 pink November, 12 2012 00:00:00+0000
9 pink November, 28 2012 00:00:00+0000
10 pink December, 01 2012 00:00:00+0000
11 pink December, 08 2012 00:00:00+0000
12 pink December, 22 2012 00:00:00+0000
クエリ:
set @i:= 0;
SELECT MIN(filter) INTO @mindt
FROM MAIN
;
select
count(a.id_main),
y.dateInterval,
(y.dateInterval - interval 29 day) as lowerBound
from
main a join (
SELECT date_format(Now(),'%Y-%m-%d') as dateInterval
from dual
union all
select x.dateInterval
from (
SELECT
date_format(
DATE(DATE_ADD(Now(),
INTERVAL @i:=@i-29 DAY)),'%Y-%m-%d') AS dateInterval
FROM Main, (SELECT @i:=0) r
HAVING datediff(dateInterval,@mindt) >= 30
order by dateInterval desc) as x) as y
on a.filter <= y.dateInterval
and a.filter > (y.dateInterval - interval 29 day)
group by y.dateInterval
order by y.dateInterval desc
;
結果:
COUNT(A.ID_MAIN) DATEINTERVAL LOWERBOUND
----------------------------------------------
2 2012-12-30 2012-12-01
3 2012-12-01 2012-11-02
2 2012-11-02 2012-10-04
4 2012-10-04 2012-09-05
行数を30日までにカウントするストアドプロシージャを作成します。
最初にこのプロシージャを実行してから、データを生成するときに同じプロシージャを呼び出します。
DELIMITER $$
DROP PROCEDURE IF EXISTS `sp_CountDataByDays`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_CountDataByDays`()
BEGIN
CREATE TEMPORARY TABLE daterange (
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
fromDate DATE,
toDate DATE,
PRIMARY KEY (`id`)
);
SELECT DATEDIFF(CURRENT_DATE(), dteCol) INTO @noOfDays
FROM yourTable ORDER BY dteCol LIMIT 1;
SET @counter = -1;
WHILE (@noOfDays > @counter) DO
INSERT daterange (toDate, fromDate)
VALUES (DATE_SUB(CURRENT_DATE(), INTERVAL @counter DAY), DATE_SUB(CURRENT_DATE(), INTERVAL @counter:=@counter + 30 DAY));
END WHILE;
SELECT d.id, d.fromdate, d.todate, COUNT(d.id) rowcnt
FROM daterange d
INNER JOIN yourTable a ON a.dteCol BETWEEN d.fromdate AND d.todate
GROUP BY d.id;
DROP TABLE daterange;
END$$
DELIMITER ;
次に、手順を呼び出します。
CALL sp_CountDataByDays();
次のような出力が得られます。
ID From Date To Date Row Count
1 2012-12-06 2013-01-05 17668
2 2012-11-06 2012-12-06 2845
3 2012-10-07 2012-11-06 2276
4 2012-09-07 2012-10-07 4561
5 2012-08-08 2012-09-07 5415
6 2012-07-09 2012-08-08 8954
7 2012-06-09 2012-07-09 4387
8 2012-05-10 2012-06-09 7911
9 2012-04-10 2012-05-10 7935
10 2012-03-11 2012-04-10 2566