日付 (年/月/日) の個々の要素を格納するために 3 つの個別の列があると、挿入のパフォーマンスとディスク容量に関して、データベースに不要なオーバーヘッドが追加されます。
やりたいことはDATETIME
、日付と時刻を格納する単一の列を用意し、複合インデックスを設定すること(countryId, datetime_col)
です。
特定の日または月に基づいてすべての行をクエリしたい場合でも、クエリを正しい方法で記述し、DATETIME 列をラップしないようにしていれば、MySQL は DATETIME フィールドのインデックスを利用できます。条件チェックを実行するときの関数内。
インデックスを利用できるようにクエリを作成する方法は次のとおりです。
-- Get the sum of totalNumber of all rows based on current day
-- where countryId = 1
SELECT SUM(totalNumber) AS totalsum
FROM tbl
WHERE countryId = 1 AND
datetime_col >= CAST(CURDATE() AS DATETIME) AND
datetime_col < CAST(CURDATE() + INTERVAL 1 DAY AS DATETIME)
そのままの DATETIME 列で比較を行うことにより、クエリは sargable (つまり、インデックス範囲スキャンを利用できる) のままになり、MySQL はインデックスを使用して行をすばやく検索できるようになります。
一方、比較を行うために DATETIME 列を関数内にラップしようとすると、次のようになります。
-- Get the sum of totalNumber of all rows based on current day
-- where countryId = 1
SELECT SUM(totalNumber) AS totalsum
FROM tbl
WHERE countryId = 1 AND
DATE(datetime_col) = CURDATE()
...DATE()
列をラップする関数はクエリを効果的に非サーガブルとしてレンダリングし、DATETIME 列を含む設定したあらゆる種類のインデックスが使用されないため、非常に非効率的です。
また、当月のすべての行の合計を効率的に照会することもできます。
-- Get the sum of totalNumber of all rows based on current month
-- where countryId = 1
SELECT SUM(totalNumber) AS monthsum
FROM tbl
WHERE countryId = 1 AND
datetime_col >= CAST(CONCAT(YEAR(NOW()), '-', MONTH(NOW()), '-01') AS DATETIME) AND
datetime_col < CAST(CONCAT(YEAR(NOW()), '-', MONTH(NOW()), '-01') AS DATETIME) + INTERVAL 1 MONTH
そして、現在の年内:
-- Get the sum of totalNumber of all rows based on current year
-- where countryId = 1
SELECT SUM(totalNumber) AS yearsum
FROM tbl
WHERE countryId = 1 AND
datetime_col >= CAST(CONCAT(YEAR(NOW()), '-01-01') AS DATETIME) AND
datetime_col < CAST(CONCAT(YEAR(NOW()), '-01-01') AS DATETIME) + INTERVAL 1 YEAR