【MySQL】UNION と UNION ALL はどっちが処理速度速いの？

# はじめに
この記事は ミライトデザイン Advent Calendar 2022 の 20 日目の記事となっています。

先日は[bucch](https://zenn.dev/t_kitamura)さんの[「ReactでGoogleMapの実装をしてみた」](https://zenn.dev/t_kitamura/articles/423f023bffe0e1)でした!

SQLクエリを作成していてテーブルを結合する際に`UNION ALL`を使う機会があったのですが、
`UNION`っていうのもあるし、どっちがどのくらい処理速度速いんだろ？っと疑問に思ったので実際に処理速度を計測してみることにしました。


# UNION と UNION ALL
どちらも、別々の`SELECT`文で取得したデータを縦に結合して一つのデータとすることができる構文です。
大きな違いは「重複するデータを排除するかしないか」です。

結合の際の注意点として以下は覚えておく必要があります。
- カラムの数が同じであること
- カラムの型が同じであること

今回の目的は「処理速度の比較」なので簡単に説明します。

## UNIONについて
公式サイトを確認すると
```
UNION [ALL | DISTINCT]
```
とあります。
どうやら`UNION`と記述するとデフォルトで、
```
UNION　DISTINCT
```
になるようです。

実際に`UNION`を試してみます。
使用するテーブル以下２つです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360742/b2fb4eda-26e4-21e2-57b8-9cf713dafb38.png)

「id=3, 4」のレコードの内容が重複している状態です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360742/f1db6069-7f71-21ad-fc64-f624a9ab3176.png)

では`UNION`を使って2つのテーブルを結合してみます。
```
SELECT id, name FROM test_a
UNION
SELECT id, name FROM test_b
```
取得結果は以下です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360742/27421228-18cc-ec74-f4a8-769c68cbd4d3.png)

「test_a」 と 「test_b」 とでの合計のデータ数は８件ですが、
重複していた2件が排除され６件となっています。
(塗りつぶしの部分)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360742/e6fbba92-2d80-1b93-19b0-3e41f58dab83.png)

## UNION ALL
`UNION`と違ってレコードの重複を排除しません。

使用するテーブルは「UNION」の時と同じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360742/b2fb4eda-26e4-21e2-57b8-9cf713dafb38.png)

では`UNION　ALL`を使って2つのテーブルを結合してみます。
```
SELECT id, name FROM test_a
UNION ALL
SELECT id, name FROM test_b
```

取得結果は以下です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360742/c960aefc-e86d-b25c-bd54-7d119c210adb.png)

`UNION`と違い2つのテーブルで重複している2件が排除されず8件全て取得されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360742/91384cc8-8627-adfa-60cf-eaefa94585fe.png)

# 実際に速度を比較してみる
ただ、その前にどっちが速いかの予想をしておきます。
それは・・・
`UNION ALL`
です。

理由は`UNION`は「重複を排除」する処理を行っていて、
`UNION ALL`は行っていないからです。
行う処理は少ない方が速いんじゃないかな、という考えです。

## 前提
- 結合するテーブルは2つです。
    - ここの説明では仮に「テーブルA」,「テーブルB」としておきます。
- 用意したカラムは「id（int型）,name(varchar型)」の２カラムのみ
- `UNION`と`UNION ALL`の比較をするために重複レコードが存在する必要があリますが、重複レコード数は全体の「20％」とし、Bテーブルに持たせます。
（%はエイヤーで決めましたがこういうパターンは●％がいいよ、などありましたら教えていただけますと幸いです）
- クエリの実行は5回ずつ行い、比較は平均値で行います。
- MySQLのバージョンは「5.7.34」です。
（テスト環境作成の都合上PHPMyAdminを使用しました）

【例】データ件数「10件」の場合に作成するテーブルイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360742/8158e801-a1e1-3dad-91fc-c0d697c1dbfc.png)
テーブルA,B共に件数は10件とし、テーブルBの件数の20％をテーブルAのデータと重複させます。
ここでは10件なので2件をテーブルAのデータと重複させます。

※実際のレコードは重複したレコードと重複しないレコードの順序はバラバラなことがほとんどかとは思いますが、今回は単純に`UNION`と`UNION ALL`を比較したいだけなので重複データは並んでいます。
そのため実際は今回の計測結果よりも時間がかかることが予想されます。

## 計測するレコードの件数
今回６パターンのデータ取得の処理速度を計測してみます。
1. 100件（20件重複）
2. 1,000件（200件重複）
3. 10,000件（2,000件重複）
4. 100,000件（20,000件重複）
5. 1,000,000件（200,000件重複）
6. ※1,000,000件（重複なし）

※`UNION`と`UNION ALL`は重複排除の有無なので重複しているデータを主としてで計測していますが、1パターンだけ、テーブルA,Bに重複がないとどのくらい違うのか？気になったので計測してみます。

## 計測結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360742/4657d19b-b72f-f25e-4db0-8329ca6ab192.png)

数字だけだと見づらいので平均値を使って縦棒グラフにしてみます。
（1,000,000件は文字がグラフに入りきらないので漢字で表記しています）
![UNION と UNION ALL の速度比較.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360742/4ff77d4e-01e0-071b-ce37-4df0bcd48625.png)

結果を箇条書きで記載します。
・100件〜10,000件までは`UNION`、`UNION ALL`には差があまりない
・100,000件、1,000,000件同士のテーブル結合だと顕著に速度差が出る
・`UNION`より`UNION ALL`の方が速い
・データ重複なしの場合でも`UNION`より`UNION ALL`の方が速い

データ件数が少ない場合はさほど差がありませんでしたが、
データ件数が多くなると`UNION ALL`の方が処理速度が速いという予想通りの結果になりました。

データ重複がないパターンを計測する前は、
重複を排除しない`UNION ALL`の方が速いだろうけど、`UNION`、`UNION ALL`でそこまで差はでないんじゃないかと考えていましたが、
データ重複のパターンと同様の差が出る結果となりました。

## 結果を踏まえて
`UNION`はデータが重複していようがいまいが「重複がないか」のチェックを行うので、
データ重複の有無はそこまで速度に関係がなさそうです。

今回データ重複がない場合において、`UNION ALL`の方が処理速度が速いという結果のため、
絶対にデータ重複がない、と知っている場合は、
`UNION`と`UNION ALL`であれば速度的に優位な`UNION ALL`を選ぶべきということになります。

ただし仕様的にデータ重複を排除する必要がある場合、
優先順位は「速度 < 仕様」であり、仕様的に`UNION`を選択することが正しければ、
速度的に`UNION ALL`が優位でも`UNION`を使うべきですね。

# おわりに
実際に`UNION`と`UNION ALL`を使ったクエリの処理速度を計測してみて、
速度差を実感でき、状況に応じて使い分ける必要性があることを理解できたので良かったです。
今後、より効率の良いクエリ作成を心がけていきます！

さて、明日の記事はクエリマスターの[Nyokki](https://qiita.com/Nyokki)さんです。
クエリといえばNyokkiさん、Nyokkiさんといえばクエリ、非常に楽しみです。

### 参考
https://dev.mysql.com/doc/refman/5.6/ja/union.html#:~:text=UNION%20%E3%81%AF%E3%80%81%E8%A4%87%E6%95%B0%E3%81%AE%20SELECT,%E5%90%8D%E3%81%A8%E3%81%97%E3%81%A6%E4%BD%BF%E7%94%A8%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82