SQLの論理形式

 新人の子たちに SQL を教えてきました。GROUP BY のキーに指定していない列名をSELECT句に書いてはいけない、という禁則の意味がよく理解できずに困っている例を多く見かけました。特に、GROUP BY句で列にCASE式を使った場合、もはやそれはキーとして元の列とは同じでありえない、というところの理解が難しかったよう。

 一般に、GROUP BY句を指定したら、SELECT句に書ける要素は三種類に制限されます。

  1. GROUP BYで指定した集約キー
  2. 集約関数
  3. 定数

 そういえば私も初心者のころ、この制限の必然性が分からなくて悩んだ記憶があります。GROUP BYがパーティション・カットの機能を果たす、ということをイメージできれば、この種の疑問は解消するのですが、それが身体感覚として身につくのはちょっとかかりそう。

 SQL を最初に学ぶとき、このあたりで混乱しやすいのは、きっとSQLを書く(読む)順序と実行順序が一致しない、という点にも一因があるのでしょう。私たちはほとんど例外なくSELECT句から書き始めてしまいますけど、実際には、SELECT句というのはSQLの中で一番最後に実行される場所なので、あんまり書くときに強く意識しない方がいいのです。

 SQLの実行順序は、

  FROM → WHERE → GROUP BY → HAVING → SELECT (→ ORDER BY)

 です。ORDER BYは正確にはSQLの一部ではないので、これは本当に蚊帳の外に置いてかまいません。そうすると、SELECT句が正真正銘のラストになります(SELECT句でつけた列の別名を GROUP BY 句で参照できないのは、この理由による。ただしちょっとズルしてこれを許可するDBもある)。

 実際、SELECT句がやっていることというのは、表示用に見た目を整形したり、計算列を算出したりするだけで、大したことをしていません。料理で言えば、最後に味を整えているようなもの。一番先頭に位置しているので、どうしてもこいつに目を奪われがちですけど、実はロジックを考えるときは無視してかまいません。WHERE、GROUP BY、HAVING の方がずっと重要な役割を持つ。

 そんなわけで、むかし Jonathan Gennick の「An Incremental Approach to Developing SQL Queries」という記事で「SQLをFROM句から書いたほうが思考の流れがスムーズになる」という主張を読んだときは、これは鋭い着眼点だと唸りました。そう、複雑な SQL を書かねばならない場合、いきなり SELECT 句から書くより、実行順序に沿って FROM句から書いたほうがずっと自然にロジックを追えるのです。SELECT句から書く方法をトップダウン・アプローチと呼ぶなら、こちらは差し詰めボトムアップ・アプローチです。C言語に喩えるなら、いきなり出来上がりを想定したmain関数から書くのがトップダウン、小さな部品的なモジュールから作って、最後に組み合わせるのがボトムアップです。手続き型言語のモジュールとSQLの「句」を完全に対応させるのは無理があるけど、でもアナロジーとしては分かってもらえます?

 SQL の見た目(文法形式)は、その内部のロジック(論理形式)を正しく反映していない。かのウィトゲンシュタイン先生も、私たちが陥る混乱の根は、だいたいにおいて私たちが言語の表層に気をとられて、その奥に潜む論理形式を見通せていないことに起因している、と言っておられるではないですか。