今まで何度かSQLが集合ということを扱ってきました。
今回はそれがよくわかる例を。
実務ではあまりないと思うのですが、集合をイメージしやすい例題として、2つのテーブルが全く同じケースを考えます。
下記のような2つのテーブルがあるとします。(tbl_a,tbl_b)
keycol | col_1 | col_2 | col_3
——–+——-+——-+——-
A | 2 | 3 | 4
B | 0 | 7 | 9
C | 5 | 1 | 6
keycol | col_1 | col_2 | col_3
——–+——-+——-+——-
A | 2 | 3 | 4
B | 0 | 7 | 9
C | 5 | 1 | 6
この2つが全く同じかどうかはどう求めればよいのでしょうか。
実はUNIONを使えばOKのようです。
1 2 3 4 |
SELECT COUNT(*) AS row_cnt FEOM ( SELECT * FROM tbl_a UNION SELECT * FROM tbl_b ) tmp; |
上記のSQLで行数がtbl_a(or tbl_b)とまったく同じになっていればOKです。
UNIONは全く同一の行を削除するからです。
UNIONは集合でいうと和集合を表し、上記の例だと2つの集合が完全に一致します。もし1レコードでも違うものがあれば数は4になります。
ちなみにSQLでは差集合も存在しまして、例えば下記のSQLだとなんのレコードもでてきませんが、もし違いがあればそのレコードが出てきます。
1 2 3 4 5 6 |
SELECT * FROM ( SELECT * FROM tbl_a EXCEPT SELECT * FROM tbl_b ) tmp; |
これを使って2つのテーブルが等しいか等しくないかを下記のSQLで判断できます。
ちなみにUNION(和集合)、INTERSECT(共通集合)、EXCEPT(差集合)は下記リンクのページのベン図で見るのが一番わかりやすいかと思います。
集合論を使って問題を解いてみましょう。
例えば下記のようなskillsテーブルがあるとします。
skill
——–
Oracle
UNIX
Java
そして、下記テーブル(empskills)をみて、skillをすべて所有しているメンバーを抽出するにはどうしたらいいか考えてみましょう。
emp | skill
——–+——–
相田 | Oracle
相田 | UNIX
相田 | Java
相田 | C#
神崎 | Oracle
神崎 | UNIX
神崎 | Java
平井 | UNIX
平井 | Oracle
平井 | PHP
平井 | Perl
平井 | C++
若田部 | Perl
渡来 | Oracle
この問題はこのページに「すべての商品を持っている店舗はどれか」という問題と近いですね。というか趣旨自体は全く同じですね。
例えばある個人を特定し、下記のようなSQLを投げた場合、その人物が持っているskillが帰ってきます。これとskillsテーブルを比べて差分がなければすべてのスキルをマスターしているということになります。
1 2 3 4 5 6 |
SELECT skill FROM empskills es1 WHERE es1.emp ='相田' |
例えば相田さんのスキルをskillsテーブルとの差分を取ってみましょう。
1 2 3 4 5 6 7 8 9 10 11 |
SELECT * FROM skills EXCEPT SELECT es1.skill FROM empskills es1 WHERE es1.emp ='相田' |
この場合結果が返ってきません。つまりレコードが存在していないわけなんですね。この状態が正しい状態です。あとはempを変数化し、NOT EXISTSを使えばいいのです。
答えのSQLは下記のようになります。
1 2 3 4 5 6 7 8 9 10 11 |
SELECT -- DISTINTがないと該当者のレコードがすべて出力される DISTINCT(es2.emp) FROM empskills es2 WHERE NOT EXISTS ( SELECT * FROM skills EXCEPT SELECT es1.skill FROM empskills es1 WHERE es1.emp = es2.emp ) |
イメージ的には以前のリンクの問題よりはわかりやすいかなあと思います。