アップデート:
「欠落している」行については気にしないので、クエリが午前7時から午前12時までではない期間の行を返す可能性があることを心配していないことも(おそらく間違って)想定します。このクエリは、指定された結果セットを返します。
SELECT (HOUR(r.time)-7)*2+(MINUTE(r.time) DIV 30) AS i
, SUM(r.subtotal) AS sum_subtotal
FROM Receipts r
GROUP BY i
ORDER BY i
これは、列を参照する式から派生した期間インデックス(i)を返しtime
ます。このクエリのパフォーマンスを最高にするには、次のような「カバー」インデックスを使用できるようにする必要があります。
ON Receipts(`time`,`subtotal`)
列に等式述語を含める場合date
(これはソリューションには表示されませんが、「選択された」回答のソリューションには表示されます)、その列を先頭のインデックスとして持つとよいでしょう。 「カバーする」インデックス。
ON Receipts(`date`,`time`,`subtotal`)
午前7時より前の期間に行が返されないようにする場合はHAVING i >= 0
、クエリに句を追加するだけです。(午前7時より前の期間の行は、iに対して負の数を生成します。)
SELECT (HOUR(r.time)-7)*2+(MINUTE(r.time) DIV 30) AS i
, SUM(r.subtotal) AS sum_subtotal
FROM Receipts r
GROUP BY i
HAVING i >= 0
ORDER BY i
以前:
現在返しているものと同様の結果セットが必要だと思いますが、一挙に。このクエリは、現在取得しているのと同じ33行を返しますが、ピリオド(0〜33)を識別する追加の列があります。これは私が得ることができるあなたの現在の解決策にできるだけ近いです:
SELECT t.i
, IFNULL(SUM(r.subtotal),0) AS sum_subtotal
FROM (SELECT (d1.i + d2.i + d4.i + d8.i + d16.i + d32.i) AS i
, ADDTIME('07:00:00',SEC_TO_TIME((d1.i+d2.i+d4.i+d8.i+d16.i+d32.i)*1800)) AS b_time
, ADDTIME('07:30:00',SEC_TO_TIME((d1.i+d2.i+d4.i+d8.i+d16.i+d32.i)*1800)) AS e_time
FROM (SELECT 0 i UNION ALL SELECT 1) d1 CROSS
JOIN (SELECT 0 i UNION ALL SELECT 2) d2 CROSS
JOIN (SELECT 0 i UNION ALL SELECT 4) d4 CROSS
JOIN (SELECT 0 i UNION ALL SELECT 8) d8 CROSS
JOIN (SELECT 0 i UNION ALL SELECT 16) d16 CROSS
JOIN (SELECT 0 i UNION ALL SELECT 32) d32
HAVING i <= 33
) t
LEFT
JOIN Receipts r ON r.time >= t.b_time AND r.time < t.e_time
GROUP BY t.i
ORDER BY t.i
いくつかの重要な注意事項:
秒が「59」または「00」に正確に等しい場合は常に、現在のソリューションがレシートから行を「欠落」している可能性があるようです。
また、日付コンポーネントには関係がないように見えます。すべての日付に対して単一の値を取得しているだけです。(誤解している可能性があります。)そうであれば、クエリで裸のTIME列を参照できるため、DATE列とTIME列を分離するとこれが役立ちます。
date
列にWHERE句を追加するのは簡単です。たとえば、1日の小計ロールアップを取得するには、たとえば、の前にWHERE句を追加しGROUP BY
ます。
WHERE r.date = '2011-09-10'
カバーリングインデックスON Receipts(time,subtotal)
(カバーリングインデックスがまだない場合)は、パフォーマンスに役立つ場合があります。(日付列に等式述語を含める場合(上記のWHERE句のように、最も適切なカバーインデックスはおそらくON Receipts(date,time,subtotal)
。です。
time
列のデータ型はTIMEであると想定しました。t
(そうでない場合は、(派生した)b_time列とe_time列のデータ型をReceiptsの列のデータ型と一致させるために、(インラインビューでのエイリアスとして)クエリを少し調整する必要がありますtime
。
他の回答で提案されているソリューションの一部は、特定の期間内にレシートに行がない場合、33行を返すことが保証されていません。「行の欠落」は問題ではないかもしれませんが、時系列および期間データで頻繁に発生する問題です。
私は、33行が返されることを保証したいと仮定しました。上記のクエリは、期間に一致する行が見つからない場合、小計ゼロを返します。(その場合、現在のソリューションはNULLを返すことに注意してください。SUMがNULLの場合に0を返すように、そのSUM集計をIFNULL関数でラップしました。)
したがって、インラインクエリt
は醜い混乱のようにエイリアスされますが、高速に動作します。実行しているのは、0から33までの個別の整数値を持つ33行を生成することです。同時に、各期間をテーブルのtime
列に「一致」させるために使用される「開始時間」と「終了時間」を導出します。 Receipts
。
time
Receiptsテーブルの列を関数でラップしないように注意しますが、裸の列のみを参照します。また、暗黙的な変換が行われないようにする必要があります(これが、b_timeとe__timeのデータ型を一致させる理由です。andADDTIME
関数SEC_TO_TIME
は両方ともTIME
データ型を返します(一致とGROUPの実行を回避することはできません)。 BY操作。)
その最後の期間の「終了時間」の値は「24:00:00」として返され、次のテストを実行して、これが照合に有効な時間であることを確認します。
SELECT MAKETIME(23,59,59) < MAKETIME(24,0,0)
これは成功している(1を返す)ので、問題ありません。
派生列(t.b_time
およびt.e_time
)も結果セットに含めることができますが、配列を作成するために必要ではなく、それらを含めない方が(おそらく)より効率的です。
最後に、パフォーマンスを最適化するには、エイリアス化されたインラインビューをt
実際のテーブルにロードすると(一時テーブルで十分です)、インラインビューの代わりにテーブルを参照できると便利な場合があります。これを行う利点は、そのテーブルにインデックスを作成できることです。