見出し画像

SQL修行vol.10

『達人に学ぶSQL徹底指南書』ー9.SQLで集合演算

3回目の送別会(全部自分の)が終わり、やっと転職の実感が沸いてきたなぁと同時に、SQLは集合指向言語であるということの意味がやっとわかってきた気がする。今日はそんなSQLと集合演算についてのまとめ。

導入

🐣注意1
SQLの扱う集合は重複行を許す多重集合であるため、ALLオプションがある

集合論での集合:重複する要素は認められない
RDBのテーブル:重複行を認める多重集合

例:UNION・INTERSECT
そのまま使う→結果から重複行が排除される(重複排除のためにソートが発生する)
ALLを使う  →結果から重複行は排除されない(ソートは発生しない)

🐣注意2
演算の順番に優先順位がある

INTERSECT→UNION、EXCEPT

🐣注意3
DBMSごとに集合演算子の実装状況にバラツキがある

🐣注意4
除算の標準的な定義がない

和:UNION
差:EXCEPT
積:CROSS JOIN
商:(なし)

活用ケース1:テーブル同士のコンペアー集合の相等性チェック

例:名前が違うだけで中身が同じ2つのテーブルを比較する

--方法1
SELECT
   COUNT(*) AS row_cnt
FROM
   ( SELECT *
     FROM tbl_A
     UNION
     SELECT *
     FROM tbl_B
   ) TMP;

上記のクエリ結果がtbl_Aとtbl_Bの行数と一致すれば両者は等しいテーブルである。
∵UNIONにALLオプションがないので重複行が排除され、等しいテーブルであればきれいに重なり合う。

この方法では、事前に2つのテーブルの行数を調べておくことが必要である。

--方法2
SELECT
   CASE WHEN COUNT(*) = 0 THEN '等しい'
                          ELSE '異なる'
   END AS result
FROM
   ((SELECT * FROM tbl_A
     UNION
     SELECT * FROM tbl_B)
     EXCEPT
    (SELECT * FROM tbl_A
     INTERSECT
     SELECT * FROM tbl_B)
   ) TMP;

∵ ( A INTERSECT B ) ⊆ ( A UNION B )なので、 ( A UNION B ) EXCEPT ( A INTERSECT B ) の結果が空集合であれば、A = B。

例:2つのテーブル間で相違した行を具体的に表示する(つまり排他的和集合)

(SELECT * FROM tbl_A
 EXCEPT
 SELECT * FROM tbl_B)
UNION ALL
(SELECT * FROM tbl_B
 EXCEPT
 SELECT * FROM tbl_A)

活用ケース2:差集合で関係除算を表現する

🐣除算の実現方法

  1. NOT EXISTSを入れ子にする

  2. HAVING句を使った一対一対応を利用する

  3. 割り算を引き算で表現する

例:EmpSkillsテーブルからSkillsテーブルの技術全てに精通した社員を探す

SELECT DISTINCT
   emp
FROM
   EmpSkills ES1
WHERE NOT EXISTS
   (SELECT skill
    FROM Skills)
   EXCEPT
   (SELECT sill
    FROM EmpSkills ES2
    WHERE ES2.emp = ES1.emp);

活用ケース3:等しい部分集合を見つける

例:供給業者が取り扱っている部品の一覧から数も種類も全く同じ部品を取り扱う供給業者のペアを見つける

SELECT
   SP1.sup AS s1,
   SP2.sup AS s2
FROM
   SupParts SP1,
   SUpParts SP2
WHERE
   SP1.sup < SP2.sup
   AND
   SP1.part = S2.part
GROUP BY
   SP1.sup,
   Sp2.sup
HAVING
   COUNT(*) = (SELECT COUNT(*) 
                FROM SupParts S3
               WHERE S3.sup = SP1.sup)
   AND
   COUNT(*) = (SELECT COUNT(*) 
                FROM SupParts S4
               WHERE S4.sup = SP1.sup)

活用ケース4:重複行を削除する高速なクエリ

例:重複行のある商品マスタから重複行を削除する

--補集合をEXCEPTで求める
DELETE
FROM Products
WHERE 
   rowid IN (SELECT rowid
             FROM Products
             EXCEPT
             SELECT MAX(rowid)
             FROM Products
             GROUP BY name, price);

--補集合をNOT INで求める
DELETE
FROM Products
WHERE 
   rowid NOT IN (SELECT MAX(rowid)
                 FROM Products
                 GROUP BY name, price);

この記事が気に入ったらサポートをしてみませんか?