プログラミング

MySQLのSQL文で間違った書き方。修正後は28倍速くなった![日付編]

MySQLのSQL文で間違った書き方。修正後は28倍速くなった![日付編]
目次
  1. 検証条件&サンプルデータ
  2. 例:アクセスログから24時間以内のデータを取得する
  3. 間違ったSQL文
  4. 修正したSQL文
  5. MySQLのEXPLAINでインデックス状況

先日仕事で重いSQLを調べていたら、うっかりミス的なSQLの書き方を見つけたので紹介します。

得られた結果は同じですが、日付の扱い方によって28倍も速くなった事例です。

本番環境ではもう少し複雑なSQL文でしたが修正したら桁違いに速くなりました。

検証条件&サンプルデータ

今回の検証は下記の条件です。

  • 約21万件のアクセスログ。
  • ストレージエンジンは、「InnoDB」
  • クエリキャッシュは、「無効」
  • インデックスは、「created_at」

本番環境は、数百万件、クエリキャッシュは有効になっています。

// サンプルデータ件数
mysql> SELECT COUNT(*) FROM `logs`;
+----------+
| COUNT(*) |
+----------+
|   217196 |
+----------+

例:アクセスログから24時間以内のデータを取得する

今回は、アクセスログから24時間以内のデータを取得する例で紹介します。

ログデータから24時間以内のデータを取得する場合、INTERVAL関数を使い「INTERVAL 1 DAY」や「INTERVAL 24 HOUR」と指定して24時間以内のデータを取得すると思いますが、この条件をどう書くかによって処理時間に大きな違いが出ました。

間違ったSQL文

今回のうっかりSQL文です。

// 修正前
mysql> SELECT COUNT(*) FROM `logs` WHERE DATE_ADD(created_at, INTERVAL 1 DAY) >= NOW();
+----------+
| COUNT(*) |
+----------+
|     2818 |
+----------+
1 row in set (0.28 sec)

ここで気がついたら素晴らしい。

修正したSQL文

// 修正後
mysql> SELECT COUNT(*) FROM `logs` WHERE `created_at`>= NOW() - INTERVAL 1 DAY;
+----------+
| COUNT(*) |
+----------+
|     2818 |
+----------+
1 row in set (0.00 sec)

うっかりSQLと同じ件数を得られていますが、処理時間は、「0.28 sec → 0.00 sec」と格段に速くなっています。

※ 0.28 sec → 0.00 secだとショボい感じで申し訳ないですが・・・。

ポイントは、カラム側の日付にINTERVAL関数を使うか、値側にINTERVAL関数を使うかでインデックスの使われ方に違いがあります。

2026年11月時点の補足:MariaDB 12.2 で同じ条件を試した結果

2026年11月5日に、手元の Windows の MariaDB 12.2.2 で記事と同じ条件の表を作って試しました(InnoDB・created_at にインデックス・217,196件・一時テーブル)。件数は記事とほぼ同じになるよう、30.7秒おきの日時を入れています。

MariaDB> SELECT COUNT(*) FROM logs WHERE DATE_ADD(created_at, INTERVAL 1 DAY) >= NOW();
|     2814 |
1 row in set (0.051 sec)

MariaDB> SELECT COUNT(*) FROM logs WHERE created_at >= NOW() - INTERVAL 1 DAY;
|     2814 |
1 row in set (0.001 sec)
| type  | possible_keys | key        | rows   | Extra                    |
| index | NULL          | created_at | 217878 | Using where; Using index |   <- 修正前
| range | created_at    | created_at |   2814 | Using where; Using index |   <- 修正後

NOW(6) で前後の時刻を取って測ると、修正前が 66.3ms、修正後が 1.5ms で、約44倍の差でした。8年たって MariaDB の版が変わっても、列を関数で包むとインデックスの範囲検索が使えない、という結論は記事のときと同じです。

項目この記事(2018年)2026年11月(MariaDB 12.2.2)
件数217,196件217,196件
24時間以内の件数2,818件2,814件
修正前0.28秒・type=index・rows=186,9800.051秒・type=index・rows=217,878
修正後0.00秒・type=range・rows=2,8150.001秒・type=range・rows=2,814
速さの差約28倍約44倍(66.3ms → 1.5ms)

MySQLのEXPLAINでインデックス状況

今回のSQLは最小限の状態ですが、たったこれだけの事で処理速度に違いが出ました。

それはなぜか?

インデックスの使われ方に違いがあります。

// 修正前
mysql> EXPLAIN SELECT COUNT(*) FROM `logs` WHERE DATE_ADD(created_at, INTERVAL 1 DAY) >= NOW();
+------+-------------+-------+-------+---------------+------------+---------+------+--------+--------------------------+
| id   | select_type | table | type  | possible_keys | key        | key_len | ref  | rows   | Extra                    |
+------+-------------+-------+-------+---------------+------------+---------+------+--------+--------------------------+
|    1 | SIMPLE      | logs  | index | NULL          | created_at | 5       | NULL | 186980 | Using where; Using index |
+------+-------------+-------+-------+---------------+------------+---------+------+--------+--------------------------+
1 row in set (0.00 sec)


// 修正後
mysql> EXPLAIN SELECT COUNT(*) FROM `logs` WHERE `created_at`>= NOW() - INTERVAL 1 DAY;
+------+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| id   | select_type | table | type  | possible_keys | key        | key_len | ref  | rows | Extra                    |
+------+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
|    1 | SIMPLE      | logs  | range | created_at    | created_at | 5       | NULL | 2815 | Using where; Using index |
+------+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

修正前のSQL文は、
「type=index、rows=186980」

修正後のSQL文は、
「type=range、rows=2815」

結果は一目瞭然ですが、修正前のSQLはフルインデックススキャン(type=index)が行われています。

一方、修正後は「type=range」となっており、インデックスが上手く使われているのが分かります。

たったこれだけの違いですが、インデックスの存在を意識してSQLの条件を書かないとこうなります。

今回は、結果が得られれば良いと思ってSQLを組んでいると、、

結果的に、サイト利用者の時間を28倍も無題にするかもしれない事例の紹介でした。

ちょっと大げさに書きましたが、もしサイトが100万PVあったら1秒速くなるだけで、100万秒の無駄がなくなります。

  • 分にすると16,666分
  • 時間にすると694時間

一人1PVだと1秒しかと思うかもしれませんが、サーバーの立場で考えると100万秒ものリソースが無駄になっています。

データベースを扱う上で、インデックスの存在はすごく大事なので常に意識してSQLを書きましょう。

もう少しSQLを勉強したいなら、「達人に学ぶ SQL徹底指南書」とか参考になると思います。

[amazonjs asin=”4798157821″ locale=”JP” title=”達人に学ぶSQL徹底指南書 第2版 初級者で終わりたくないあなたへ (CodeZine BOOKS)”]