はじめに
本記事では同じ結果が得られるUNIONとCASE式を使用して実行計画を確認し、どちらの方がパフォーマンスが良いのか確認していきたいと思います!
実行計画の確認方法がわからない方は「PostgreSQLで実行計画を確認してみる」を参考にしてください🙌
準備
まずクエリを叩く為のテーブルを準備します☝️
-- テーブル作成
CREATE TABLE Items
( item_id INTEGER NOT NULL,
year INTEGER NOT NULL,
item_name CHAR(32) NOT NULL,
price_tax_ex INTEGER NOT NULL,
price_tax_in INTEGER NOT NULL,
PRIMARY KEY (item_id, year));
-- データ追加
INSERT INTO Items VALUES(100, 2000, 'カップ' ,500, 525);
INSERT INTO Items VALUES(100, 2001, 'カップ' ,520, 546);
INSERT INTO Items VALUES(100, 2002, 'カップ' ,600, 630);
INSERT INTO Items VALUES(100, 2003, 'カップ' ,600, 630);
INSERT INTO Items VALUES(101, 2000, 'スプーン' ,500, 525);
INSERT INTO Items VALUES(101, 2001, 'スプーン' ,500, 525);
INSERT INTO Items VALUES(101, 2002, 'スプーン' ,500, 525);
INSERT INTO Items VALUES(101, 2003, 'スプーン' ,500, 525);
INSERT INTO Items VALUES(102, 2000, 'ナイフ' ,600, 630);
INSERT INTO Items VALUES(102, 2001, 'ナイフ' ,550, 577);
INSERT INTO Items VALUES(102, 2002, 'ナイフ' ,550, 577);
INSERT INTO Items VALUES(102, 2003, 'ナイフ' ,400, 420);
UNION と CASE を使用して同じ結果を取得する
UNION
SELECT item_name, year, price_tax_ex AS price
FROM Items
WHERE year <= 2001
UNION
SELECT item_name, year, price_tax_in AS price
FROM Items
WHERE year >= 2002
ORDER BY item_name, year;
CASE
SELECT item_name, year,
CASE WHEN year <= 2001 THEN price_tax_ex
WHEN year >= 2002 THEN price_tax_in END AS price
FROM Items;
実行結果(UNION / CASE)
以下のようにUNION / CASEどちらも同じ結果を返します👍
item_name | year | price
--------------------------------------+------+-------
カップ | 2000 | 500
カップ | 2001 | 520
カップ | 2002 | 630
カップ | 2003 | 630
スプーン | 2000 | 500
スプーン | 2001 | 500
スプーン | 2002 | 525
スプーン | 2003 | 525
ナイフ | 2000 | 600
ナイフ | 2001 | 550
ナイフ | 2002 | 577
ナイフ | 2003 | 420
(12 rows)
実行計画を確認する
UNION
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------
Sort (cost=51.02..51.78 rows=306 width=140) (actual time=0.775..0.778 rows=12 loops=1)
Sort Key: items.item_name, items.year
Sort Method: quicksort Memory: 25kB
-> HashAggregate (cost=35.33..38.39 rows=306 width=140) (actual time=0.489..0.497 rows=12 loops=1)
Group Key: items.item_name, items.year, items.price_tax_ex
Batches: 1 Memory Usage: 37kB
-> Append (cost=0.00..33.03 rows=306 width=140) (actual time=0.377..0.396 rows=12 loops=1)
-> Seq Scan on items (cost=0.00..15.75 rows=153 width=140) (actual time=0.376..0.380 rows=6 loops=1)
Filter: (year <= 2001)
Rows Removed by Filter: 6
-> Seq Scan on items items_1 (cost=0.00..15.75 rows=153 width=140) (actual time=0.009..0.011 rows=6 loops=1)
Filter: (year >= 2002)
Rows Removed by Filter: 6
Planning Time: 3.541 ms
Execution Time: 1.305 ms
(15 rows)
CASE
QUERY PLAN
-----------------------------------------------------------------------------------------------------
Seq Scan on items (cost=0.00..16.90 rows=460 width=140) (actual time=0.132..0.136 rows=12 loops=1)
Planning Time: 0.505 ms
Execution Time: 0.302 ms
(3 rows)
パフォーマンス差のポイント😳
Execution Time(処理時間)
UNION:1.305 ms
CASE:0.302 ms
Seq Scan(テーブル全体をスキャン)
UNION:(2回スキャン)
Seq Scan on items
Seq Scan on items items_1
CASE:(1回スキャン)
Seq Scan on items
CASE式はUNIONよりパフォーマンスが良くなりやすい
SQLで同じ結果を取得できる場合でも、UNION と CASE 式で処理性能に差が出ることがあります。
CASE式の特徴
- 1つのテーブルを1回スキャンすればよい
- 条件が単純な場合では特に高速
UNIONの特徴
- 複数のSELECTを結合するため、テーブルを複数回スキャンすることが多い
- 条件が単純な場合は、CASE式に比べて処理時間が長くなりやすい
UNIONを使用したほうがパフォーマンスが良いケースもある
SQLで複数の条件を組み合わせてデータを取得する場合、CASE式を使った方が高速な場合が多いですが、UNIONを使った方がパフォーマンスが良くなるケースも存在します。
例えば以下のような状況等です。
- インデックスを利用できる場合
- 複雑な条件でテーブルを分割したほうが効率的な場合
※実際のパフォーマンスはテーブルサイズや条件によって変化します。実行計画を確認して最適な方法を選ぶのがベストです。🧐
まとめ
- CASE式は単純条件で高速
- UNIONは条件やインデックス次第ではパフォーマンスが高くなることもある
- 実行計画を確認して、パフォーマンスをチェックするのがベスト
私自身まだまだ勉強中ですが、引き続き実践しながら学んだことを発信していきたいと思います!🐊