この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
既に存在するデータの縦持ちと横持ちを入れ替えて見たい場合の方法を書いていきます。
環境
Mac OSX 10.10.5 Yosemite Redshift 1.0.1125
縦持ち ⇨ 横持ち
Pivot(ピボット)と言います。
縦持ちテーブルの準備
商品別の月別売上の表です。
CREATE SCHEMA IF NOT EXISTS blog;
CREATE TABLE blog.vertical (
id INTEGER
, item_name VARCHAR(1)
, month SMALLINT
, amount INTEGER
);
INSERT INTO blog.vertical VALUES
(1,'A',1,150)
,(2,'A',2,120)
,(3,'A',3,100)
,(4,'B',1,20)
,(5,'B',2,60)
,(6,'B',3,80)
,(7,'C',1,100)
,(8,'C',2,200)
,(9,'C',3,300);
postgres=# SELECT * FROM blog.vertical;
id | item_name | month | amount
----+-----------+-------+--------
1 | A | 1 | 150
2 | A | 2 | 120
3 | A | 3 | 100
4 | B | 1 | 20
5 | B | 2 | 60
6 | B | 3 | 80
7 | C | 1 | 100
8 | C | 2 | 200
9 | C | 3 | 300
(9 rows)
Pivotしてみる
SELECT
item_name
, MAX("1") AS "1"
, MAX("2") AS "2"
, MAX("3") AS "3"
FROM
(
SELECT
item_name
, CASE month WHEN 1 THEN amount END "1"
, CASE month WHEN 2 THEN amount END "2"
, CASE month WHEN 3 THEN amount END "3"
FROM
blog.vertical
GROUP BY
item_name, month, amount
ORDER BY
item_name, amount
) A
GROUP BY
item_name;
まずは服問い合わせの実行結果が下記になります。 見やすくするためにSQLには記述のないmonthを追加しています。
.
item_name | month | 1 | 2 | 3
-----------+-------+-----+-----+-----
A | 1 | 150 | |
A | 2 | | 120 |
A | 3 | | | 100
B | 1 | 20 | |
B | 2 | | 60 |
B | 3 | | | 80
C | 1 | 100 | |
C | 2 | | 200 |
C | 3 | | | 300
(9 rows)
それをitem_name別にで最大値を取得すると下記になり、横持ちに移行完了です。
実行結果
item_name | 1 | 2 | 3
-----------+-----+-----+-----
A | 150 | 120 | 100
B | 20 | 60 | 80
C | 100 | 200 | 300
(3 rows)
横持ち ⇨ 縦持ち
Unpivot(アンピボット)と言います。
横持ちテーブル準備
Pivotの結果をSELECT INTOで作成しても良いですね。
CREATE TABLE blog.horizontal (
item_name CHAR(1)
, "1" INTEGER
, "2" INTEGER
, "3" INTEGER
);
INSERT INTO blog.horizontal
VALUES
('A',150,120,100)
,('B',20,60,80)
,('C',100,200,300);
postgres=# SELECT * FROM blog.horizontal;
item_name | 1 | 2 | 3
-----------+-----+-----+-----
A | 150 | 120 | 100
B | 20 | 60 | 80
C | 100 | 200 | 300
(3 rows)
Unpivotするための月だけのテーブルを作成
CREATE TABLE blog.month (month INTEGER);
INSERT INTO blog.month VALUES(1),(2),(3);
postgres=# SELECT * FROM blog.month;
month
-------
1
2
3
(3 rows)
Unpivotしてみる
SELECT
item_name
, month
, CASE month
WHEN 1 THEN "1"
WHEN 2 THEN "2"
WHEN 3 THEN "3"
END amount
FROM
blog.horizontal
cross join blog.month
ORDER BY
item_name, month, amount;
クロスジョインして下記の状態を作ります。
postgres=# SELECT * FROM blog.horizontal, blog.month ORDER BY item_name, month;
item_name | 1 | 2 | 3 | month
-----------+-----+-----+-----+-------
A | 150 | 120 | 100 | 1
A | 150 | 120 | 100 | 2
A | 150 | 120 | 100 | 3
B | 20 | 60 | 80 | 1
B | 20 | 60 | 80 | 2
B | 20 | 60 | 80 | 3
C | 100 | 200 | 300 | 1
C | 100 | 200 | 300 | 2
C | 100 | 200 | 300 | 3
(9 rows)
その結果から、縦持ちにしたいamountをmonthの値で条件分岐させ、どのカラム「1〜3」のどこを参照するか決めています。
実行結果
.
item_name | month | amount
-----------+-------+--------
A | 1 | 150
A | 2 | 120
A | 3 | 100
B | 1 | 20
B | 2 | 60
B | 3 | 80
C | 1 | 100
C | 2 | 200
C | 3 | 300
(9 rows)
最初に作った縦持ちテーブルとほぼ同じになりました。
さいごに
頻繁には使わないかもしれませんが、必要な事もあるので頭の片隅で覚えておくと良いと思います。