この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
データアナリティクス事業本部の森脇です。
RedshiftのPIVOT/UNPIVOT機能が発表されました。
https://docs.aws.amazon.com/redshift/latest/dg/r_FROM_clause-pivot-unpivot-examples.html
これを利用することで、いわゆる 縦持ち、 横持ちを入れ替えることが可能です。
早速試してみました。
PIVOT
まずは元となるテーブルを作成します。
今回は売上情報をイメージしたものを作成しました。
ひな祭りが近いのでそれっぽいデータを投入しておきます。
-- 疑似 売上テーブル
CREATE TABLE test_pivot (
order_at DATE, -- 注文日
item_code VARCHAR(10), -- 商品コード
category_name VARCHAR(100), -- カテゴリー名
item_name VARCHAR(100), -- アイテム名
quantity int, -- 個数
price int -- 価格
);
INSERT INTO test_pivot VALUES ('2022/02/01', 'item-0001', '食品', 'ひなあられ', 2, 150);
INSERT INTO test_pivot VALUES ('2022/02/01', 'item-0002', '季節用品', '雛人形セット', 1, 30000);
INSERT INTO test_pivot VALUES ('2022/02/01', 'item-0003', '飲料', '甘酒', 3, 300);
INSERT INTO test_pivot VALUES ('2022/02/01', 'item-0004', '食品', 'ちらし寿司', 2, 1500);
INSERT INTO test_pivot VALUES ('2022/02/02', 'item-0002', '季節用品', '雛人形セット', 2, 30000);
INSERT INTO test_pivot VALUES ('2022/02/02', 'item-0003', '飲料', '甘酒', 10, 300);
INSERT INTO test_pivot VALUES ('2022/02/04', 'item-0004', '食品', 'ちらし寿司', 15, 1500);
INSERT INTO test_pivot VALUES ('2022/02/04', 'item-0005', '食品', 'ケーキ', 1, 3000);
INSERT INTO test_pivot VALUES ('2022/02/05', 'item-0001', '食品', 'ひなあられ', 10, 150);
INSERT INTO test_pivot VALUES ('2022/02/05', 'item-0002', '季節用品', '雛人形セット', 5, 30000);
現状の中身はこんな感じです。
SELECT * FROM test_pivot ORDER BY 1;
カテゴリー別の総売上金額を算出してみます。
-- カテゴリ別 総売上額
SELECT category_name, SUM(quantity * price) as total
FROM test_pivot
GROUP BY category_name
;
いまはいわゆる横持ち状態です。この結果を縦持ちにしたい場合、PIVOT機能を使うことで非常に簡単に実現することができます。
-- カテゴリ別 総売上額(縦持ち)
SELECT *
FROM (SELECT category_name, quantity, price FROM test_pivot) PIVOT (
SUM(quantity * price) FOR category_name IN ('季節用品', '食品', '飲料')
);
複雑なクエリが必要ありません!
複数のAggregate関数に対応
上記例ではSUMを利用しました。
2022年2月24日現在、COUNT, SUM, MIN, MAX, AVGの関数に対応しているようです。
https://docs.aws.amazon.com/redshift/latest/dg/r_FROM_clause-pivot-unpivot-examples.html
PIVOT currently supports the COUNT, SUM, MIN, MAX, and AVG aggregate functions.
COUNT
-- COUNT: 日別商品注文回数(縦持ち)
SELECT *
FROM (SELECT order_at FROM test_pivot) PIVOT (
COUNT(*) FOR order_at IN ('2022/02/01', '2022/02/02', '2022/02/03', '2022/02/04', '2022/02/05')
);
MIN
-- MIN: 商品別最低注文個数(縦持ち)
SELECT *
FROM (SELECT item_name, quantity FROM test_pivot) PIVOT (
MIN(quantity) FOR item_name IN ('ひなあられ', '雛人形セット', '甘酒', 'ちらし寿司', 'ケーキ')
);
MAX
-- MAX: 商品別最大注文金額(縦持ち)
SELECT *
FROM (SELECT item_name, (quantity * price) as total FROM test_pivot) PIVOT (
MAX(total) FOR item_name IN ('ひなあられ', '雛人形セット', '甘酒', 'ちらし寿司', 'ケーキ')
);
AVG
-- AVG: 日別平均注文金額(縦持ち)
SELECT *
FROM (SELECT order_at, quantity, price FROM test_pivot) PIVOT (
AVG(quantity * price) FOR order_at IN ('2022/02/01', '2022/02/02', '2022/02/03', '2022/02/04', '2022/02/05')
);
CTASと組み合わせ
PIVOT/UNPIVOTはFROM句の機能です。
そのため、CTASでも利用できます。
CTAS
-- CTASでも使える
CREATE TABLE test_pivot_2 AS SELECT *
FROM (SELECT category_name, quantity, price FROM test_pivot) PIVOT (
SUM(quantity * price) FOR category_name IN ('季節用品', '食品', '飲料')
);
SELECT * FROM test_pivot_2;
マテリアライズドビュー
同様に、マテリアライズドビューとも組み合わせが可能です。
個人的にはこれがとても便利だと感じました。
マテビューを作っておけば、データの更新に複雑なSQLを用意する必要がありません。
-- マテビューでも使える
CREATE MATERIALIZED VIEW test_pivot_view AS SELECT *
FROM (SELECT category_name, quantity, price FROM test_pivot) PIVOT (
SUM(quantity * price) FOR category_name IN ('季節用品', '食品', '飲料')
);
SELECT * FROM test_pivot_view;
-- データが追加されたらマテビューをrereshするだけで反映される
INSERT INTO test_pivot VALUES ('2022/02/06', 'item-0001', '食品', 'ひなあられ', 10, 150);
REFRESH MATERIALIZED VIEW test_pivot_view;
SELECT * FROM test_pivot_view;
UNPIVOT
縦持ちから横持ちに変換したい場合には、UNPIVOTを使います。
PIVOTで作った「test_pivot_2」を使って確認します。
select * from test_pivot_2;
-- カテゴリ別総注文金額(→横持ちへ)
SELECT *
FROM (SELECT "季節用品", "食品", "飲料" FROM test_pivot_2) UNPIVOT (
total FOR category_name IN ("季節用品", "食品", "飲料")
);
こちらも簡単に実現することができました。
まとめ
PIVOT/UNPIVOTを使うことでデータの縦持ち/横持ちを簡単に変更することができました。 分析用テーブル/ビューを作るのにも役立ちそうです。