毎週木曜日またはその他の曜日から始まる週次結果でSQLステートメントを改善するにはどうすればよいですか?


8

私はまったくの初心者ですが、これを行う良い方法がどこにもありませんでした。週のさまざまな時間に記録される統計を含むデータベーステーブルがあります。報告週は木曜日に始まります。テーブルには、データがいつ記録されたかを格納する日付スタンプ列(日付)が含まれています。

特定の週のデータを取得する必要があります(レポート週は木曜日から始まります)。次のクエリを作成しました。

   SELECT * 
FROM `table` 
WHERE 1 = CASE 
  WHEN WEEKDAY(NOW()) = 0 THEN DATEDIFF(NOW(),`date`) BETWEEN -2 AND 4
  WHEN WEEKDAY(NOW()) = 1 THEN DATEDIFF(NOW(),`date`) BETWEEN -1 AND 5
  WHEN WEEKDAY(NOW()) = 2 THEN DATEDIFF(NOW(),`date`) BETWEEN -0 AND 6
  WHEN WEEKDAY(NOW()) = 3 THEN DATEDIFF(NOW(),`date`) BETWEEN -6 AND 0
  WHEN WEEKDAY(NOW()) = 4 THEN DATEDIFF(NOW(),`date`) BETWEEN -5 AND 1
  WHEN WEEKDAY(NOW()) = 5 THEN DATEDIFF(NOW(),`date`) BETWEEN -4 AND 2
  WHEN WEEKDAY(NOW()) = 6 THEN DATEDIFF(NOW(),`date`) BETWEEN -3 AND 3
END

これは初期テストで機能するようです。しかし、それが最善の方法かどうかはわかりません。MySQLのパフォーマンスについてはあまり知りませんが、フィルタリングするレコードは10万を超えます。チェックされる条件の数が原因で、このクエリは本当に遅くなりますか?

最新のレポートを取得するときにNOW()関数が使用されます。ただし、場合によっては他の週のレポートを作成する必要があるため、別の日付に置き換えます。

また、レポートの週が変更された場合(たとえば、開始日が水曜日に変更された場合)、この方法でクエリを書き直す必要があります。

WEEK()関数は、SunまたはMonで1週間しか開始できないため、使用できません。

このクエリを改善するためのアイデアは大歓迎です!

その他の注意事項:現在MariaDB 5.3を使用しています。

回答:


5

ここに私があなたに最近の木曜日と終わりの水曜日を与えるために書いたクエリがあります

SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;

こちらが今日の例です2011-09-21

mysql> SELECT
    -> thuwk_beg + INTERVAL 0 second thu_beg,
    -> thuwk_beg + INTERVAL 604799 second wed_end
    -> FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
    -> FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
    -> FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;
+---------------------+---------------------+
| thu_beg             | wed_end             |
+---------------------+---------------------+
| 2011-09-15 00:00:00 | 2011-09-21 23:59:59 |
+---------------------+---------------------+
1 row in set (0.00 sec)

NOW()関数呼び出しを任意の日時に置き換えるだけで、木曜日から始まる週が、選択した特定の日時にいつでも使用できます。

特定の日付「2011-01-01」を使用した別の例を次に示します

mysql> SELECT
    -> thuwk_beg + INTERVAL 0 second thu_beg,
    -> thuwk_beg + INTERVAL 604799 second wed_end
    -> FROM (SELECT (DATE('2011-01-01') - INTERVAL daysbacktothursday DAY) thuwk_beg
    -> FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
    -> FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE('2011-01-01') dt) AAAA) AAA) AA) A;
+---------------------+---------------------+
| thu_beg             | wed_end             |
+---------------------+---------------------+
| 2010-12-30 00:00:00 | 2011-01-05 23:59:59 |
+---------------------+---------------------+
1 row in set (0.00 sec)

table今日参照するクエリは次のようになります。

SELECT * from `table`,
(SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A) M
WHERE `date` >= thu_beg
AND `date` <= wed_end;

試してみる !!!

UPDATE 2011-09-22 16:27 EDT

これはThu-Wedをマークするために提案したクエリです。

SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;

他の週はどうですか???

  • (SELECT SUBSTR('6012345',wkndx,1) 月曜日から日曜日までの週を行います
  • (SELECT SUBSTR('5601234',wkndx,1) 火曜日から月曜日までの週
  • (SELECT SUBSTR('4560123',wkndx,1) 水曜日から火曜日まで
  • (SELECT SUBSTR('3456012',wkndx,1) 木で始まり水で終わる週
  • (SELECT SUBSTR('2345601',wkndx,1) 金曜日から木曜日まで
  • (SELECT SUBSTR('1234560',wkndx,1) 土曜日から金曜日まで
  • (SELECT SUBSTR('0123456',wkndx,1) 日曜始まり土曜終わりの週

1

私が正しく理解していることを確認するために、あなたが何を求めているかをもう一度言いましょう。

指定した日付の過去7日間のすべてのレコードをプルしますか?

次のようになります。

select * from table where `date` between $date - interval 7 day and $date

$ dateはリテラルのmysql構文ではなく、希望する開始日のプレースホルダーの例にすぎないことに注意することが重要です。これがレポート用である場合、クエリは最終的にいくつかのスクリプトから生成されると思いますか?それが本当である場合、その言語ではより単純で、作成されたクエリの一部としてリテラル値を渡すことができます。

私はクエリをできるだけシンプルに保つのが好きなので、このままにしておきます。単一のSQL-fuクエリで必要なことを達成するために、他の人が答えを提供する余地を残しておきます。

編集:投稿を再読した後、おそらく日付タイプを使用しているようです。その場合、次のイタリック体のブロックは冗長になる可能性があります。他の人に役立つイベントに残しておきます(それを書くのに時間がかかったので:-)

「日付スタンプ」を使用していると言いましたか?これは厳密にはMysqlデータ型ではありません。それは日時、タイムスタンプ、または日付ですか(時間と年も存在しますが、あなたの文脈からはそれらは当てはまらないと思います)?あなたはので、私は尋ねることがあり、それの代わりに他の人のちょうど日付列にしたいです。これが正しい選択であるかどうかは、実際にcolumnnがどのように使用されているか、およびすべてに対してテーブルがクエリされる詳細に依存します。それが唯一の目的が、時間に関係なく日付範囲のレコードを取得することだけである場合、日付は間違いなく進むべき道です。1つには、4または8ではなく3バイトしか必要ありません。日付が使用要件に一致する場合(つまり、時間部分は気にしない場合)、日付を使用する他の理由について詳しく説明します。さまざまなタイプの詳細については、 http://dev.mysql.com/doc/refman/5.0/en/datetime.html

http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html

特に日付を使用している場合に限り、次のことを検討できます。

先頭に「説明」を付けてクエリを実行します。出力の解釈方法と最適な方法の詳細については、http://dev.mysql.com/doc/refman/5.0/en/explain-output.htmlを参照してください。

それを説明したら、クエリを次のように変更します

select * from table where date in ("N", "N+1"..."N+7")

ここで、興味のある個々の日付をすべて列挙します。MySQLが範囲クエリ(xとyの間)を効率的に使用するほど賢くなく、小さなセットの特定の値を列挙する状況に遭遇したことがあります。

値に基づいて定期的なレポートクエリを作成する場合、列にインデックスが付けられていることを確認する必要があるユースケースはどれですか。


1

@Rolando氏は明らかに質問に答えますが、別のタイムゾーンで定義されている週ごとにタイムゾーンと最も重要なグループの間でカレンダーを操作およびカスタマイズできるようにする別の決定を提案します

したがって、mySQLがUTC構成のサーバーで実行されていて、7時間先のカスタムカレンダーが必要であり、したがって、週は土曜日の午前7時に開始する必要があると仮定します。

CREATE TABLE `wh_blur_calendar` (
  `date` timestamp NOT NULL ,
  `y` smallint(6) DEFAULT NULL,
  `q` tinyint(4) DEFAULT NULL,
  `m` tinyint(4) DEFAULT NULL,
  `d` tinyint(4) DEFAULT NULL,
  `w` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `wh_ints` (
  `i` tinyint(4) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into wh_ints values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);

テーブルにデータを入力する人気のあるデカルト結合:

INSERT INTO wh_blur_calendar (date)
SELECT DATE('2010-01-01 00:00:00 ') + INTERVAL a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i DAY
FROM wh_ints a JOIN wh_ints b JOIN wh_ints c JOIN wh_ints d JOIN wh_ints e
WHERE (a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i) < 10245
ORDER BY 1;

時間を更新しましょう:

update  db_wh_repo.wh_blur_calendar set date = date_add(date, interval 7 hour);

そして最後に、カスタムの方法でカレンダーの週を配置します

UPDATE wh_blur_calendar
SET 
    y = YEAR(date),
    q = quarter(date),
    m = MONTH(date),
    d = dayofmonth(date),
    w = week(date_add((date), interval 1 day));

私がこの決定に至るまでに数時間を費やすと信じていますが、カスタムのタイムゾーンと週の定義に基づいて結果をグループ化する場合に備えて、非常に自由になります。

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