【初級編⑬】なんとなく書いていたSQLのSELECT文を根本から理解する(2/2)

前回の記事、【初級編⑫】なんとなく書いていたSELECT文を根本から理解する(1/2)では、SELECT文が実行される仕組みをExcelを使って説明しました。

今回は、そのSELECT文の中の各句(SELECTやFROMなど)が実行される順番について説明したいと思います。

SELECT文の内部処理を見てみる

SELECT文を含めSQLは、実行すると全体が一気に処理されてその最終結果だけしか画面に表示されません。ただ、SQLServerの内部ではSELECTやFROMなどの”句”毎に分解して解析され実行されています。

実際にSELECT文を実行する時にSQLServerが内部でどんな処理をしているかを見てみましょう。

SELECT 社員番号, 名前, 性別, 給料, 給料 * 12 AS '年収'
FROM Table_Syain
WHERE 性別 = '女'
ORDER BY 年収
------------------------------------------------------------------

社員番号  名前                   性別   給料          年収
----- -------------------- ---- ----------- -----------
00006 前田 六実                女    180000      2160000
00003 鈴木 三つ子               女    250000      3000000

(2 行処理されました)

クエリエディタには「推定実行プランの表示」というボタンがありますので、これをクリックすると「実行プラン(実行計画とも言う)」というものを表示できます。実行プランとは、まさにSQLServerが内部で解析した情報になります。

実行プランの表示

まだ詳しくは分からなくていいのですが、今回の実行プランの部分を拡大してみます。右から左に処理が流れているのですが、SELECT文が完了するまでに、①~③の3つの処理が実行されていることが分かります。①は「性別」が[女]のレコードを検索する処理で、その検索結果に対して②「給料」* 12 で年収を求める処理があり、③で「年収」の順番に並び替え処理が実行されています。

実行プランの表示

SELECT文の各句が実行される順番

SELECT文が実行される時に、各句が実行される順番を下に記載します。

<SELECT文の各句が実行される順番>
順番 内容
FROM句 実行対象のテーブルを指定します
WHERE句 テーブルに対してレコードの抽出条件を指定します
GROUP BY句 レコードをグループ化します
HAVING句 グループ化した結果に対して抽出条件を指定します
SELECT句 取得(表示)する列を指定します
ORDER BY句 取得した列を並び替えます

FROM句

全ての処理の中で、最初にFROM句が実行されます。FROM句では実行しようとしているSELECTの対象を指定するのですが、最初にこれをしないとどのテーブルに対して命令を実行するのか分からないので処理ができませんね。

FROM句で対象テーブルの指定が必要

テーブル結合もFROM句に書きますが、先にテーブル結合をしておいて、結合が全て終わった結果のテーブルに対して、後続の処理が実行されます。

WHERE句

2番目に、指定したテーブル(結合した結果)に対して、対象とするレコードの絞り込みを行います。不要なレコードは取り除くという処理になります。

ここで注意なのは、いくらGROUP BYやHAVING等を使っていても、WHEREはそもそも対象テーブル全件に対して先に絞り込みを掛けておき、GROUP BYはWHEREで絞り込まれた結果残ったレコードに対してのみグループ化されるということです。

社員テーブルに対して、男女別の人数を集計してみるとします。

社員テーブル全件で男女別の人数

例えば、年齢が30才未満の社員に対して、男女別の人数を集計すると、確かに人数が変わっていることが分かります。

30才未満の社員で男女別の人数

これは、GROUP BY句で集計されるのは、あくまでWHERE句で絞り込んだ結果のレコードに対してだからです。

GROUP BY句

前述のWHERE句でも触れましたが、GROUP BY句は「WHERE句で絞り込まれた結果のレコード」に対してグループ化を行います。

またグループ化というのは、集計することになるので、集計対象のキーにした列以外は取得できません。先ほども実行しましたが、社員の男女別の人数を集計してみます。

社員テーブル全件で男女別の人数

この時、SEELCTしているのは「性別」と「COUNT(性別)」だけですが、「性別」以外の列はSELECTすることができません。なぜなら、複数レコードを纏めてグループ化している為、例えば「社員番号」を取得するにしても、どのレコードの社員番号を取得すれば良いかが分からないからです。

グループ化した時はグループ化対象キー以外はSELECTできない

今回の場合、男は6人居てその6人の中からどれを選べばいいかの基準がありません。

男性6名の中から社員番号を特定できない

レコードを選択する基準が無いからSELECTできないのだから、SELECTするにはどのレコードを選べば良いのかを指定してあげればOKです。例えば簡単なやり方だと、数字だったら最大値、もしくは最小値を出す方法です。

SELECT MIN(社員番号) AS '社員番号の最小値'
	  ,性別
	  , COUNT(性別) AS '性別毎の人数'
FROM Table_Syain
GROUP BY 性別


社員番号の最小値 性別   性別毎の人数
-------- ---- -----------
00003    女    2
00001    男    6

(2 行処理されました)

最小の社員番号を取得

男性6名の中で最小の社員番号を特定

HAVING句

HAVING句は、GROUP BY句でグループ化した後、その集計対象に条件を指定します。

<WHERE句とHAVING句の違い>
内容
WHERE テーブルのレコード全件に対して抽出する
HAVING GROUP BY句で集計した対象レコードに対して抽出する

先の男女別の人数を求める際は、「性別」で集計してカウントを取りましたが、HAVING句の使い方は、この性別毎に集計した結果に対して条件を指定します。例えば次の様なSQLだと、

SELECT 性別
	  ,COUNT(性別) AS '性別毎の人数'
FROM Table_Syain
GROUP BY 性別
HAVING COUNT(性別) >= 3

性別   性別毎の人数
---- -----------
男    6

(1 行処理されました)

同じ性別のレコードをグループ化し、そのレコードの件数が3以上のもの、という条件指定になります。

HAVING句の役目






SELECT句

ここまでやって、やっとSELECT句の登場です。基本的に、この状態で出力対象のレコード件数は揃っていて、あとは取得したい列を指定するだけです。Excelで言うと不要な列を非表示にするだけ(表示したい列のみを残す)です。

前回の記事で説明したSELECT文「社員マスタに対して、女性社員の年収を求めて、年収の昇順に並べたリスト」を例にします。

SELECT句が実行されるタイミングでは、ちょうど下の図のWHERE句で絞り込みが終わった状態です

この状態から、必要な列だけSELECTしたり、「給料」 * 12 をして「年収」列を取得したりしていました。

ORDER BY句

SELECT句が実行され、取得する列が決まったら、最後にORDER BY句で並び替えを指定します。Excelで加工する時と同じ感覚かと思います。

SQLを習得するには、今回の記事で書いた「どういう順番で各句が実行されるか?」の理解が必須です。それを、頭の中でシュミレートできるようになれば、複雑なSELECT文でもスラスラ書けるようになるでしょう。

この記事と関連性の高い記事