MySQLのunionの使い方について、サンプルSQLを使ってまとめました。
以下、データベースとして、MySQLのサンプルデータベースEmployeesを使っています。
サンプルデータベースのインストール方法は、下記を参考にしてください。
【関連記事】
▶MySQLの入門には、GUIツールで慣れ、サンプルDBを使った学習が効果的
MySQLのUNIONの構文
SELECT ... UNION [ALL | DISTINCT] SELECT ... [UNION [ALL | DISTINCT] SELECT ...]
参考)MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.9.4 UNION 構文
複数のselect文の結果を1つの結果に統合するためのステートメントです。2つめ以降の結果は、1つめの結果と同じデータ型にする必要があります。データ型さえ合っていれば、異なるテーブルのselect結果でもunionが使用可能です。
ファイル出力を行うinto outfileは、最後のselect文にのみ使用可能。
SQL99(SQL3)に準拠したSQL標準のステートメントです。特にMySQL独自の拡張はないようです。
【関連記事】
▶SQLとはどういうもの? 独自拡張と標準SQLの大きな違いって、何?
select * from employees where emp_no < 10005 union select * from employees where emp_no between 10007 and 10010;
上記SQLは、employeesテーブルから、emp_no<10005の行と、emp_noが10007~10010の行をselectして結合するSQLです。実行結果はこうなります。
+--------+------------+------------+-----------+--------+------------+ | emp_no | birth_date | first_name | last_name | gender | hire_date | +--------+------------+------------+-----------+--------+------------+ | 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 | | 10002 | 1964-06-02 | Bezalel | Simmel | F | 1985-11-21 | | 10003 | 1959-12-03 | Parto | Bamford | M | 1986-08-28 | | 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 | | 10007 | 1957-05-23 | Tzvetan | Zielinski | F | 1989-02-10 | | 10008 | 1958-02-19 | Saniya | Kalloufi | M | 1994-09-15 | | 10009 | 1952-04-19 | Sumant | Peac | F | 1985-02-18 | | 10010 | 1963-06-01 | Duangkaew | Piveteau | F | 1989-08-24 | +--------+------------+------------+-----------+--------+------------+ 8 rows in set (0.00 sec)
MySQLのunionサンプルSQLコード
union allの前に、select文ごとにorder byを実行する方法
複数のselect文ごとにorder byでソートをするには、それぞれのselect文を括弧で囲み、limitを合わせて指定する必要があります。
mysql> (select * from employees where emp_no <= 10010 order by birth_date limit 10) UNION ALL ( select * from employees where emp_no between 10011 and 10020 order by first_name limit 10);
上記のSQLは、employees(雇用者テーブル)のうち、emp_noが10010以下の10件をbirth_date(誕生日)でソートし、emp_noが1011~10020の10件をfirst_name(姓名の名)でソートして結合します。
実行結果はこうなります。
+--------+------------+------------+-------------+--------+------------+ | emp_no | birth_date | first_name | last_name | gender | hire_date | +--------+------------+------------+-------------+--------+------------+ | 10009 | 1952-04-19 | Sumant | Peac | F | 1985-02-18 | | 10006 | 1953-04-20 | Anneke | Preusig | F | 1989-06-02 | | 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 | | 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 | | 10005 | 1955-01-21 | Kyoichi | Maliniak | M | 1989-09-12 | | 10007 | 1957-05-23 | Tzvetan | Zielinski | F | 1989-02-10 | | 10008 | 1958-02-19 | Saniya | Kalloufi | M | 1994-09-15 | | 10003 | 1959-12-03 | Parto | Bamford | M | 1986-08-28 | | 10010 | 1963-06-01 | Duangkaew | Piveteau | F | 1989-08-24 | | 10002 | 1964-06-02 | Bezalel | Simmel | F | 1985-11-21 | | 10014 | 1956-02-12 | Berni | Genin | M | 1987-03-11 | | 10017 | 1958-07-06 | Cristinel | Bouloucos | F | 1993-08-03 | | 10013 | 1963-06-07 | Eberhardt | Terkki | M | 1985-10-20 | | 10015 | 1959-08-19 | Guoxiang | Nooteboom | M | 1987-07-02 | | 10018 | 1954-06-19 | Kazuhide | Peha | F | 1987-04-03 | | 10016 | 1961-05-02 | Kazuhito | Cappelletti | M | 1995-01-27 | | 10019 | 1953-01-23 | Lillian | Haddadi | M | 1999-04-30 | | 10011 | 1953-11-07 | Mary | Sluis | F | 1990-01-22 | | 10020 | 1952-12-24 | Mayuko | Warwick | M | 1991-01-26 | | 10012 | 1960-10-04 | Patricio | Bridgland | M | 1992-12-18 | +--------+------------+------------+-------------+--------+------------+ 20 rows in set (0.00 sec)
limitなしでorder byを指定した場合は、ソート条件は無視されます。また、UNION ALLではなくUNIONを使用した場合も強制的にソートされるため、注意しましょう。
unionで結合したクエリをjoinする方法
mysql> select Q1.emp_no, first_name, last_name, dept_name from (select emp_no, first_name, last_name from employees where emp_no < 10010 union all select 10100, 'yamada', 'tarou') Q1 left join dept_emp on Q1.emp_no = dept_emp.emp_no left join departments on dept_emp.dept_no = departments.dept_no;
unionで結合したselect文を括弧で囲んでサブクエリにすることでjoin可能です。
上記のSQLは、emp_noが10010より小さいemployeesのレコードと、emp_no=10100、first_name=’yamada’、last_name=’tarou’というデータをunionで結合し、dept_emp(部署・雇用者紐付けテーブル)と、departments(部署テーブル)をjoinしています。
実行結果は以下のようになります。
+--------+------------+-----------+--------------------+ | emp_no | first_name | last_name | dept_name | +--------+------------+-----------+--------------------+ | 10001 | Georgi | Facello | Development | | 10002 | Bezalel | Simmel | Sales | | 10003 | Parto | Bamford | Production | | 10004 | Chirstian | Koblick | Production | | 10005 | Kyoichi | Maliniak | Human Resources | | 10006 | Anneke | Preusig | Development | | 10007 | Tzvetan | Zielinski | Research | | 10008 | Saniya | Kalloufi | Development | | 10009 | Sumant | Peac | Quality Management | | 10100 | yamada | tarou | Human Resources | +--------+------------+-----------+--------------------+ 10 rows in set (0.00 sec)
unionで仮想的な連番テーブルを生成する方法
mysql> SELECT @seq := 1 AS seq UNION SELECT @seq := @seq + 1 AS seq from employees LIMIT 15;
fromで指定しているemployeesは、テーブルの件数を擬似的なループのために利用しています。十分なデータ件数があるテーブルであれば何でも構いません。
実行するとこうなります。
+------+ | seq | +------+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 | | 10 | | 11 | | 12 | | 13 | | 14 | | 15 | +------+ 15 rows in set (0.23 sec)
ただし、件数の少ないdepartmentsテーブル(9件)をfromに指定するとこうなります。
+------+ | seq | +------+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 | | 10 | +------+ 10 rows in set (0.00 sec)
9回のループで、生成される連番は10までとなります。
まとめ
- UNIONは、select文の結果を結合できる
- UNIONは、結果を自動的にソートする。ソートしたくない場合は、UNION ALLを使う。
- UNION ALLでORDER BYを使う場合は、LIMITを指定する。指定しないと自動ソートされる。
- UNIONしたクエリをJOINするには、括弧で囲んでサブクエリ化する。
- UNIONで連番テーブルを生成する場合、fromで指定するテーブルには十分な件数のあるものを選ぶ。