読者です 読者をやめる 読者になる 読者になる

Blank?=False

「呉下の阿蒙にあらず」をモットーにしたITエンジニアの日々

SQLのUNIONの正しい使い所がわからない

今日、久しぶりにUNION文を使う機会があったのですが、
それはベストなテーブル設計であれば1つのテーブルになっているべきものが
2つに分かれていたので、UNIONで結合して処理する、と言うものでした。

ここで、なんとなくですが
UNIONを使うときは使いにくい設計のテーブルのデータを上手いこと使うため、ということばかりだなぁと気づきました。

逆に、ベストなテーブル設計になっているDBMSでUNIONを使うときはどんなときか、ちょっと気になったので調べてみました。

UNIONとは

以前の記事にも書いたとおり、テーブルの足し算です。
2つのテーブルを「縦に」結合する、というイメージです。

stonebeach-dakar.hatenablog.com

実際にあった使用例

テーブル構成

実際に今回UNIONを使ったのは、製品のデータを区分ごとに別々のテーブルで管理しているデータベースでした。
例として、同じカラムをもつ2つの商品テーブルがあったとします。

キッチン用品商品テーブル

mysql> select * from shohin_Cookware;
+-----------+-----------------+--------------+--------------+------------+
| shohin_id | shohin_mei      | hanbai_tanka | shiire_tanka | torokubi   |
+-----------+-----------------+--------------+--------------+------------+
| K001      | 包丁            |         3000 |         2800 | 2009-09-20 |
| K002      | 圧力鍋          |         6800 |         5000 | 2009-01-15 |
| K003      | フォーク        |          500 |         NULL | 2009-09-20 |
| K004      | おろしがね      |          880 |          790 | 2008-04-28 |
+-----------+-----------------+--------------+--------------+------------+
4 rows in set (0.00 sec)


洋服商品テーブル

mysql> select * from shohin_clothes;
+-----------+-----------------------+--------------+--------------+------------+
| shohin_id | shohin_mei            | hanbai_tanka | shiire_tanka | torokubi   |
+-----------+-----------------------+--------------+--------------+------------+
| C001      | Tシャツ               |         1000 |          500 | 2008-09-20 |
| C002      | Gパン                 |         3000 |         2000 | 2009-09-11 |
| C003      | カッターシャツ        |         4000 |         2800 | NULL       |
+-----------+-----------------------+--------------+--------------+------------+
3 rows in set (0.00 sec)

(久しぶりにMySQLの結果をコピペしましたがやっぱり日本語使うとずれちゃいますねぇ。良い解決方法ないかな。)

使用例

こういった注文テーブルがあったとします。

mysql> select * from Purchase_Order;
+----------+-----------+-------------+
| Order_ID | Shohin_ID | Order_Value |
+----------+-----------+-------------+
|        1 | C001      |          10 |
|        2 | K003      |           1 |
|        3 | C003      |           5 |
|        4 | K002      |           2 |
|        5 | C002      |           4 |
+----------+-----------+-------------+
5 rows in set (0.00 sec)


この注文の各商品のデータを取り出したいとします。
やり方は色々ありますが、今回製品テーブルをすべて結合したものを内的結合することで対応しました。

mysql> SELECT P_Order.Order_ID, P_Order.Shohin_ID, Shohin.Shohin_mei, Shohin.hanbai_tanka
    -> FROM Purchase_Order AS P_Order
    -> INNER JOIN (
    ->             SELECT * FROM Shohin_Clothes
    ->             UNION
    ->             SELECT * FROM Shohin_Cookware
    ->            ) AS Shohin
    -> ON Shohin.Shohin_id = P_Order.shohin_id;
+----------+-----------+-----------------------+--------------+
| Order_ID | Shohin_ID | Shohin_mei            | hanbai_tanka |
+----------+-----------+-----------------------+--------------+
|        1 | C001      | Tシャツ               |         1000 |
|        2 | K003      | フォーク              |          500 |
|        3 | C003      | カッターシャツ        |         4000 |
|        4 | K002      | 圧力鍋                |         6800 |
|        5 | C002      | Gパン                 |         3000 |
+----------+-----------+-----------------------+--------------+
5 rows in set (0.00 sec)

何がおかしいの?

まず、この2つ、そもそもテーブルを分ける必要性がほとんどありません。
shohinテーブル1つだけにして区分カラムを準備すれば事足りると思います。

mysql> select * from shohin;
+-----------+-----------------------+--------------------+--------------+--------------+------------+
| shohin_id | shohin_mei            | shohin_bunrui      | hanbai_tanka | shiire_tanka | torokubi   |
+-----------+-----------------------+--------------------+--------------+--------------+------------+
| C001      | Tシャツ               | 衣服               |         1000 |          500 | 2009-09-20 |
| C002      | Gパン                 | 衣服               |         3000 |         2000 | 2009-09-11 |
| C003      | カッターシャツ        | 衣服               |         4000 |         2800 | NULL       |
| K001      | 包丁                  | キッチン用品       |         3000 |         2800 | 2009-09-20 |
| K002      | 圧力鍋                | キッチン用品       |         6800 |         5000 | 2009-01-15 |
| K003      | フォーク              | キッチン用品       |          500 |         NULL | 2009-09-20 |
| K004      | おろしがね            | キッチン用品       |          880 |          790 | 2008-04-28 |
+-----------+-----------------------+--------------------+--------------+--------------+------------+
7 rows in set (0.00 sec)

なので、このUNIONの利用方法の場合そもそものテーブル設計がダメだったので無理やり使うための方法でしかないので、
こういった風にベストなテーブル設計となっている場合、UNIONを使うことがあるのかが気になったのです。

もしこのブログを読んだ方でベストなテーブル設計になっていてUNIONを使うシチュエーションというのをご存知ならコメントいただけると嬉しいです。