blog.amedama.jp Open in urlscan Pro
54.199.90.60  Public Scan

URL: https://blog.amedama.jp/entry/2017/06/28/223558
Submission: On June 02 via api from US — Scanned from JP

Form analysis 1 forms found in the DOM

GET https://blog.amedama.jp/search

<form class="search-form" role="search" action="https://blog.amedama.jp/search" method="get">
  <input type="text" name="q" class="search-module-input" value="" placeholder="記事を検索" required="">
  <input type="submit" value="検索" class="search-module-button">
</form>

Text Content

CUBE SUGAR CONTAINER


技術系のこと書きます。


2017-06-28


SQL: UNION を使ってテーブルを縦に連結する

MariaDB Mac OS X SQL

今回は SQL の UNION を使ってみる。

試した環境は次の通り。

$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.12.5
BuildVersion:   16F73
$ mysql --version
mysql  Ver 15.1 Distrib 10.2.6-MariaDB, for osx10.12 (x86_64) using readline 5.1



典型的な使い方

例えばスキーマ設計において、パフォーマンスの向上などを目的として同じカラムを持ったテーブルが複数あったりすることがある。
特定の月のデータを入れるテーブルとか、あるいは毎月新しいテーブルを作っていくとか、そんな感じ。

次のサンプルでは同じカラムを持ったテーブルが日付で分かれている。

> CREATE TABLE purchases_20170627 (
    ->   purchase_id INTEGER,
    ->   user_id VARCHAR(255)
    -> );
Query OK, 0 rows affected (0.04 sec)

> CREATE TABLE purchases_20170628 (
    ->   purchase_id INTEGER,
    ->   user_id VARCHAR(255)
    -> );
Query OK, 0 rows affected (0.02 sec)


上記のテーブルに、それぞれサンプルのレコードを追加しておこう。

> INSERT INTO
    ->   purchases_20170627
    -> VALUES
    ->   (1, 'Alice'),
    ->   (2, 'Bob'),
    ->   (3, 'Carol');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

> INSERT INTO
    ->   purchases_20170628
    -> VALUES
    ->   (4, 'Alice'),
    ->   (5, 'Bob');
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0


さて、上記のようなテーブルを一つのクエリで処理したいとする。 そんなときこそ今回扱う UNION の出番といえる。 UNION では複数の SELECT
文を繋げて扱うことができる。 もちろん同じカラムという前提はあるけど。

次のクエリでは先ほどの二つのテーブルの内容をまとめて取得している。

> SELECT
    ->   '20170627' AS date,
    ->   purchase_id,
    ->   user_id
    -> FROM purchases_20170627
    -> UNION ALL
    -> SELECT
    ->   '20170628' AS date,
    ->   purchase_id,
    ->   user_id
    -> FROM purchases_20170628;
+----------+-------------+---------+
| date     | purchase_id | user_id |
+----------+-------------+---------+
| 20170627 |           1 | Alice   |
| 20170627 |           2 | Bob     |
| 20170627 |           3 | Carol   |
| 20170628 |           4 | Alice   |
| 20170628 |           5 | Bob     |
+----------+-------------+---------+
5 rows in set (0.00 sec)


同じカラムを持った内容をどんどん縦に繋げていくイメージ。


擬似的なテーブルを作るのに使う

続いては UNION を使って擬似的なテーブルを作るやり方について。 例えば SELECT 文を UNION で繋いでいけば CREATE TABLE ...
しなくても擬似的なテーブルが用意できる。

次のクエリでは WITH と共に使うことで擬似的に作ったテーブルの内容を表示している。

> WITH
    -> device_types AS (
    ->   SELECT 1 AS device_type, 'LB' AS device_name
    ->   UNION ALL
    ->   SELECT 2 AS device_type, 'L2SW' AS device_name
    ->   UNION ALL
    ->   SELECT 3 AS device_type, 'L3SW' AS device_name
    -> )
    -> SELECT
    ->   *
    -> FROM device_types;
+-------------+-------------+
| device_type | device_name |
+-------------+-------------+
|           1 | LB          |
|           2 | L2SW        |
|           3 | L3SW        |
+-------------+-------------+
3 rows in set (0.00 sec)


ちなみに、これまでは UNION ALL を使っていたけど、重複する内容を省きたいときは UNION DISTINC を使う。

> WITH
    -> device_types AS (
    ->   SELECT 1 AS device_type, 'LB' AS device_name
    ->   UNION DISTINCT
    ->   SELECT 1 AS device_type, 'LB' AS device_name
    ->   UNION DISTINCT
    ->   SELECT 3 AS device_type, 'L3SW' AS device_name
    -> )
    -> SELECT
    ->   *
    -> FROM device_types;
+-------------+-------------+
| device_type | device_name |
+-------------+-------------+
|           1 | LB          |
|           3 | L3SW        |
+-------------+-------------+
2 rows in set (0.00 sec)


同じ内容を持ったレコードが削除されている。

UNION で作った擬似的なテーブルも CREATE TABLE ... で作ったテーブルと同じように扱うことができる。
例えば、次のように擬似的なテーブルと関連するようなテーブルを作っておく。

> CREATE TABLE devices(
    ->   id INTEGER,
    ->   type INTEGER
    -> );
Query OK, 0 rows affected (0.02 sec)

> INSERT INTO
    ->   devices
    -> VALUES
    ->   (1, 1),
    ->   (2, 1),
    ->   (3, 2),
    ->   (4, 3);
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0


そして両者を JOIN してみよう。

> WITH
    -> device_types AS (
    ->   SELECT 1 AS device_type, 'LB' AS device_name
    ->   UNION ALL
    ->   SELECT 2 AS device_type, 'L2SW' AS device_name
    ->   UNION ALL
    ->   SELECT 3 AS device_type, 'L3SW' AS device_name
    -> )
    -> SELECT
    ->   devices.id,
    ->   device_types.device_name AS device_type
    -> FROM devices
    -> JOIN device_types
    -> ON devices.type = device_types.device_type;
+------+-------------+
| id   | device_type |
+------+-------------+
|    1 | LB          |
|    2 | LB          |
|    3 | L2SW        |
|    4 | L3SW        |
+------+-------------+
4 rows in set (0.01 sec)


ちゃんと上手く JOIN できた。

まあ上記くらいの内容なら擬似的なテーブルを作るよりも CASE を使った方が楽ちんかな。

> SELECT
    ->   devices.id,
    ->   CASE
    ->     WHEN devices.type = 1 THEN 'LB'
    ->     WHEN devices.type = 2 THEN 'L2SW'
    ->     WHEN devices.type = 3 THEN 'L3SW'
    ->   END AS device_type
    -> FROM devices;
+------+-------------+
| id   | device_type |
+------+-------------+
|    1 | LB          |
|    2 | LB          |
|    3 | L2SW        |
|    4 | L3SW        |
+------+-------------+
4 rows in set (0.00 sec)



再帰クエリの中で使う

UNION のもう一つの重要な使い方として再帰クエリ (WITH RECURSIVE) の中での用法がある。
これは再帰的にクエリを実行して得られた内容を結合するのに UNION を使うということ。 再帰クエリについては以下に詳しく書いた。

blog.amedama.jp

ちなみに MySQL 5.7 には再帰クエリが実装されていないけど MariaDB 10.2 なら使える。


まとめ

今回は UNION を使う場面について見てみた。



ビッグデータ分析・活用のためのSQLレシピ

 * 作者: 加嵜長門,田宮直人,丸山弘詩
 * 出版社/メーカー: マイナビ出版
 * 発売日: 2017/03/27
 * メディア: 単行本(ソフトカバー)
 * この商品を含むブログを見る







もみじあめ (id:momijiame) 5年前



関連記事
 * 2020-05-08
   MySQL の InnoDB でトランザクション分離レベルの違いを試す
   今回は MySQL の InnoDB を使ってトランザクション分離レベル (…
 * 2017-06-30
   SQL: CASE 句を使って縦持ちのデータを横持ちに変換する
   使った環境は次の通り。 $ sw_vers ProductName: Mac OS X Prod…
 * 2017-06-29
   SQL: COALESCE() 関数で NULL を別の値に置き換える
   RDB のスキーマ設計において、なるべくなら Nullable なカラム…
 * 2017-06-27
   SQL:2003 のウィンドウ関数を MariaDB 10.2 で試す
   今回は SQL:2003 の規格で追加されたウィンドウ関数を使ってみ…
 * 2017-06-18
   SQL: 内部的なコードを人間に分かりやすいラベルに変換して表示する
   RDB のスキーマには、たまに対応表などを参照しながらでないと…


« SQL: NULLIF() 関数を使ってゼロ除算を防ぐ SQL:2003 のウィンドウ関数を MariaDB 10.2… »

プロフィール
読者です 読者をやめる 読者になる 読者になる
603

このブログについて
検索

本を書きました

連絡先

問い合わせフォーム

最新記事
 * Linux の Network Namespace で L2TP (IPsec なし) を試す
 * Linux の Network Namespace で PPPoE を試す
 * Python: scikit-learn の LabelEncoder を説明変数の変換に使うのは誤り
 * strongSwan の IPsec VPN を Network Namespace で試す (Route-based / VTI デバイス)
 * WireGuard の VPN を Linux の Network Namespace で試す

注目記事
 * Python: Streamlit を使って手早く WebUI 付きのプロトタイプを作る
 * stress コマンドを使ってマシンに負荷をかける
 * Docker のホストとコンテナ間でファイルをやり取りする
 * Python: ipywidgets で Jupyter に簡単な UI を作る
 * Python: seaborn を使った可視化を試してみる

スポンサードリンク






引用をストックしました

ストック一覧を見る 閉じる

引用するにはまずログインしてください

ログイン 閉じる

引用をストックできませんでした。再度お試しください

閉じる

限定公開記事のため引用できません。

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