SQL記述者全員が理解すべきSELECT文の論理的な処理順序のお話


2020/9/30追記
本記事は元々、「SQL記述者全員が理解すべきSELECT文の実行順序のお話」というタイトルで投稿しておりました。
しかし、知見のある方からのコメントと自分でも調べてみた結果、今回紹介している順序はあくまで論理的な処理順序であり、実行順序とは別物ということがわかりました。
誤った知識を布教してしまい申し訳ございません。
2020/9/30のタイミングで、本記事のタイトルを「SQL記述者全員が理解すべきSELECT文の論理的な処理順序のお話」に変更させていただきました。

はじめに

「SQLといえば、エンジニアが扱うスキル」と思われがちですが、最近はマーケターや営業など、非エンジニアの方もSQLを使って、自らデータを抽出し分析する方が増えてきています。

またエンジニアの方でも、ORM任せでなんとなく理解している状態の方もいるのではないでしょうか?

今回は、そんな方々にこそぜひ理解していただきたい SELECT文の実行順序 についてお話したいと思います。

問いかけ

SELECT文を書いたことがある方であれば、一度はこんな疑問を抱いたことがあるのではないでしょうか?

「なんでWHERE句でSUM関数(集約関数)は使えないの?」
「なんでSELECT句のASはORDER BY句では使えるのに、WHERE句では使えないの?」
「なんでLIMITで出力行数を絞っているのに、処理時間は変わらないの?」

これらすべて、今回お話する SELECT文の実行順序 を理解すれば誰でも説明できるようになれます!

SELECT文の実行順序とは?

データベースがSQLを実行する際、上から(SELECT句から)実行するイメージを持っている方が多いのではないでしょうか?

実はこれ間違いで、データベースは下記順序でSQLを実行していきます。

FROM句
↓
JOIN句
↓
WHERE句
↓
GROUP BY句
↓
HAVING句
↓
SELECT句
↓
ORDER BY句
↓
LIMIT句

この順序を念頭に置いて、問いかけで出した疑問を1つずつ見ていきましょう。

なんでWHERE句でSUM関数(集約関数)は使えないの?

SUM関数やCOUNT関数など、 グループ化された データに対して処理をかけ1つの値にまとめる関数を集約関数と呼びます。

この グループ化された が重要で、SELECT文の中では GROUP BY句 がそれに該当します。

それでは、もう一度実行順序を見てみましょう。

FROM句
↓
JOIN句
↓
**WHERE句**
↓
**GROUP BY句**
↓
HAVING句
↓
SELECT句
↓
ORDER BY句
↓
LIMIT句

GROUP BY句よりもWHERE句の方が先に実行される、つまりWHERE句の時点ではまだグループ化がされていないことがわかるかと思います。
そのため、WHERE句でSUM関数やCOUNT関数といった集約関数を使うことができないのです。

逆に、HAVING句で集約関数を使えるのは、その前にGROUP BY句の処理が実行されグループ化されているためでもあります。

なんでSELECT句のASはORDER BY句では使えるのに、WHERE句では使えないの?

ASはカラムやテーブルに対して、別名を宣言できる処理です。
可読性の向上や別テーブルの同一名カラムを扱う際によく使われますね。

そんな便利なASですが、なぜWHERE句で別名を使えないのでしょうか?
これも実行順序を見れば一目瞭然です。

FROM句
↓
JOIN句
↓
**WHERE句**
↓
GROUP BY句
↓
HAVING句
↓
**SELECT句**
↓
**ORDER BY句**
↓
LIMIT句

WHERE句の時点ではまだSELECT句が実行されていないため、ASによる別名が宣言されておらず使用することができないのです。

逆に、ORDER BY句でASによる別名を使えるのは、その前にSELECT句が実行されているためです。

これを理解すると、FROM句でテーブル名に対してASを使用した場合に、他のどの句でも使えるのも納得できますね。

※データベースによっては、GROUP BY句でもASによる別名を使用できます。

なんでLIMITで出力行数を絞っているのに、処理時間は変わらないの?

LIMIT句では、最終的に出力するレコード数を制限することができます。
小さい数を指定すればするほど処理時間が短くなると思われがちですが、一概にそうとも限りません。

FROM句
↓
JOIN句
↓
WHERE句
↓
GROUP BY句
↓
HAVING句
↓
SELECT句
↓
ORDER BY句
↓
**LIMIT句**

すべての処理が実行された最後にLIMIT句によるレコードの絞り込みが実行されるため、基本的には処理時間が短くなることはありません。

2020/9/22追記
※ORDER BY句でインデックスが貼られたカラムを指定していた場合、全レコードをソートせずに先頭のnレコード分を取り出してくれるため、処理時間が早くなることもあります。
PostgreSQL
MySQL

まとめ

日頃なんとなく書いているSELECT文も、実行順序を正しく理解することで、必要なデータを より早くより正確に 抽出することができるようになれます。

また、データベースへの負荷も意識して書けるようになることで、予期せぬシステムダウンや費用発生を防ぐことにもつながるでしょう。

この記事が一人でも多くのSQL記述者の役に立てば幸いです。

それではよきSQLライフを!