【SQL講座3】サブクエリについて
今回は、サブクエリについてです。
基本的にサブクエリには書く場所によって、
- SELECT句
- FROM句
- WHERE(HAVING)句
の3種類があります。
最近は、Accessのシステムをやることはなくなったので詳しくないのですが、AccessのSQLではFROM句、SELECT句には書けなかったり、他のDBMSでもバージョンによってできないことがあります。
それぞれ、書く場所によって特徴があります。
■ SELECT句(基本的にサブクエリの結果がスカラであること)
サブクエリ内のWHERE(HAVING)句で、親にあたるFROM句にあるテーブル・ビュー・サブクエリ・テーブルファンクションのカラムを参照することができます。
親にあたるSQL文の出力結果の件数と同じ回数実行され、その結果は必ず1件でなければいけません。
■ FROM句(サブクエリの結果がほかの句で利用されるとき)
親にあたるカラムを一切参照することはできません。
独立したSQLとして実行できる必要があり、1度しか実行されません(つまり、WHERE句の条件はリテラルでなければなりません)。
JOIN の仕方によって、最終の出力データが増えることも減ることもあります。出力結果はメモリー上に展開され、JOINに元のインデックスが使われることはありません。
■ WHERE(HAVING)句(絞込み条件のためのサブクエリ)
※ わたしはHAVING句で使ったことはないけど、文法的には使える。
サブクエリ内のWHERE(HAVING)句で、親にあたるFROM句にあるテーブル・ビュー・サブクエリ・テーブルファンクションのカラムを参照することができます。
サブクエリの件数に関わらず、最終出力結果は減ることはあっても増えることはありません。
◇ IN述語のとき
基本的にサブクエリを親のFROM句より先に実行します。サブクエリの結果の数だけ、OR と同様に評価します。
◇ EXISTS述語のとき
基本的にサブクエリを親のFROM句の後で実行します。親のFROM句ので抽出されたデータと一致するサブクエリの結果が、1件でもあればそれ以上は評価しません。
意味を書けばこれだけのことです。
ネストして使われることがあるのはFROM句で、括弧の内側から順に実行されます。結果はメモリ上にすべて展開され、元のインデックスを使われることはないため、内側のサブクエリで十分な絞込みを行っていないとパフォーマンスは非常に悪くなります。
一番外側のWHERE句に正しい絞込み条件を書けば、結果は同じになりますけれど、要するに、外側のWHERE句で絞り込めばそれだけ読み飛ばしが起きるということです。
「サブクエリをネストしたら遅くなる」というのは間違いではないですが、正しくもありません。
「無駄に読み飛ばせば遅くなる」が正解です。
初心者がよく間違うことは、WHERE句のサブクエリが複数レコード返したときに不安になったりすることです。WHERE句に条件を追加すれば、出力結果が減ることがあっても増えることはありません。
◇ ◇ ◇ ◇
詳しい説明は、ハンズオンセミナーで行っています。
もっと基本的な部分から、テーブルファンクション・OLAP関数ぐらいまで説明します。
ご興味のある方はinfo@g1sys.co.jpまで。
コメント
saki1208
saki1208です。
ずいぶんご無沙汰していましたが、年度末も過ぎ平常通りに戻ってきました。
Accessでも上記の各句でサブクエリの使用は可能ですよ。
もう十年ほどAccessはまともに触ってませんが、2003、2007では部分的に
確認してみました。
就職後に初めてまともに作ったプログラムはAccess1.1でした。誰も何も教
えてくれる人がいなくて、オンラインヘルプを参照しながら試行錯誤した
覚えがあります。Accessのマニュアルを借りて返って家で読んだり、自分仕
事以外のことを試してみたり。マクロではなく、すべてAccess Basicで作る
という要求でしたからすごく時間がかかりましたし、当時はすべてクエリも
しくはDAOを使用して作成しておりSQLを理解することはできませんでした
けど、今にして思えば原点だったかなと…
PC/AT互換機を初めて購入したのもその頃ですねぇ。
本題に関係ない話になっちゃいました。
saki1208
少し飲み過ぎのようです。
日本語がおかしいですが、適宜補完願います。(w)
saki1208 さん、おはようございます。
ホントですね。
Accessでもできました。
まともなプログラムを書いたときはAccess2.0で素人だったので、
できないものだと思っていました。
Accessで組んでいて、自分が言語仕様を決めるなら括弧書きで
サブクエリーが使えるように作るのに……と考えて、イロイロ
試したのでしたがダメであきらめました。
Oracleを初めて触ったときに試しにやってみたら、こたえが
返ってきてびっくり。
当時はFROM句でサブクエリーを使うためにユニークな名前の
クエリーを作ってから動的SQLで処理していたな……。
1995年ぐらいのAccessの教科書にはそんなこと書いてなかったし、
CreateQueryDef ……ってやって、ユニークにしないとマルチユーザ
で使えないので、名称管理含め、めちゃくちゃ苦労した恨みがあって、
「Accessはダメ!」って……。逆恨みだったのねorz
長~く使ってなかったから勘違いしていました。
勉強になりました。ありがとうございました。