可視化SQL | 現在の給料と昇給前の給料 を1行で表示する(その8) | Window 関数
初めに
この問題は、SQLパズル #15 『現在の給料と昇給前の給料 』 を参考にしています
手元に『SQL パズル』があれば問題の詳細が記載されているのでよりわかりやすいです
下記に表示するテーブルとデータを見ると、社員の昇給日と給料は1行づつ追加されていて
複数昇給している社員とまだ昇給していない社員がいます
この問題は、社員のお給料を保持しているテーブルから
『現在の給料』と『昇給前の給料』を取出して、1行で表示させる問題です
『その8』は『その7』と同じで Window 関数を使用します。
CASE文を使って、順番をつけられたデータを判定しています
最後に取出すデータは同じですが、その過程は人それぞれです
色んな方法を見る事はいい勉強になります
下記画像の様な出力結果となります
各社員に対して、今のお給料の昇給日とお給料、前回の昇給日とお給料が
1行で表示されています
PostgreSQL で動作確認しています
テーブル と データ
SQL
CREATE TABLE Salaries (
emp_name CHAR(10) NOT NULL,
sal_date DATE NOT NULL,
sal_amt DECIMAL(8,2) NOT NULL,
PRIMARY KEY (emp_name, sal_date)
)
INSERT INTO Salaries
VALUES ('Tom', '1996-06-20', 500.00),
('Tom', '1996-08-20', 700.00),
('Tom', '1996-10-20', 800.00),
('Tom', '1996-12-20', 900.00),
('Dick', '1996-06-20', 500.00),
('Harry', '1996-07-20', 500.00),
('Harry', '1996-09-20', 700.00);
CREATE TABLE Salaries (
emp_name CHAR(10) NOT NULL,
sal_date DATE NOT NULL,
sal_amt DECIMAL(8,2) NOT NULL,
PRIMARY KEY (emp_name, sal_date)
)
INSERT INTO Salaries
VALUES ('Tom', '1996-06-20', 500.00),
('Tom', '1996-08-20', 700.00),
('Tom', '1996-10-20', 800.00),
('Tom', '1996-12-20', 900.00),
('Dick', '1996-06-20', 500.00),
('Harry', '1996-07-20', 500.00),
('Harry', '1996-09-20', 700.00);
▼ Salaries テーブルの中身
Window 関数を使ってデータを加工する
SQL
SELECT emp_name,
sal_date,
sal_amt,
RANK() OVER (PARTITION BY emp_name ORDER BY sal_date DESC)
FROM Salaries
SELECT emp_name,
sal_date,
sal_amt,
RANK() OVER (PARTITION BY emp_name ORDER BY sal_date DESC)
FROM Salaries
▼ 出力
Window関数の一つ RANK関数を使って、データに順番をつけます
PARTITION BY emp_name ・・・は GROUP BY em_name と見てください
⇒ データを社員名で分割するイメージです
⇒ 詳細は割愛させていただきます
分割されたデータを昇給日の降順でソートしたデータに対して
1 -> 2 -> 3 -> 4 と順番( RANK )をつけます
直近の昇給日が1で、1つ前の昇給日が2 となっています
・・・ここまでは『その7』と同じです
順番が3未満のデータだけ取り出す
SQL
SELECT S1.*
FROM
(
SELECT emp_name,
sal_date,
sal_amt,
RANK() OVER (PARTITION BY emp_name ORDER BY sal_date DESC)
FROM Salaries
) AS S1 (emp_name, sal_date, sal_amt, pos)
WHERE pos < 3 -- 番号が3未満だけを取出す
SELECT S1.*
FROM
(
SELECT emp_name,
sal_date,
sal_amt,
RANK() OVER (PARTITION BY emp_name ORDER BY sal_date DESC)
FROM Salaries
) AS S1 (emp_name, sal_date, sal_amt, pos)
WHERE pos < 3 -- 番号が3未満だけを取出す
▼ 出力(画像右側)
お給料情報は昇給日が新しい順番に1番から番号が振られています
『今の給料』と『1つ前の給料』が取出したいデータなので
この番号(pos)が 1 と 2 のデータを取出しています
CASE文でSQLを加工する
SQL
SELECT S1.*,
CASE WHEN pos = 1 THEN sal_date ELSE NULL END AS curr_date,
CASE WHEN pos = 1 THEN sal_amt ELSE NULL END AS curr_amt,
CASE WHEN pos = 2 THEN sal_date ELSE NULL END AS prev_date,
CASE WHEN pos = 2 THEN sal_amt ELSE NULL END AS prev_amt
FROM
(
SELECT emp_name,
sal_date,
sal_amt,
RANK() OVER (PARTITION BY emp_name ORDER BY sal_date DESC) FROM Salaries
) AS S1 (emp_name, sal_date, sal_amt, pos)
WHERE pos < 3
ORDER BY S1.emp_name DESC,S1.pos
SELECT S1.*,
CASE WHEN pos = 1 THEN sal_date ELSE NULL END AS curr_date,
CASE WHEN pos = 1 THEN sal_amt ELSE NULL END AS curr_amt,
CASE WHEN pos = 2 THEN sal_date ELSE NULL END AS prev_date,
CASE WHEN pos = 2 THEN sal_amt ELSE NULL END AS prev_amt
FROM
(
SELECT emp_name,
sal_date,
sal_amt,
RANK() OVER (PARTITION BY emp_name ORDER BY sal_date DESC) FROM Salaries
) AS S1 (emp_name, sal_date, sal_amt, pos)
WHERE pos < 3
ORDER BY S1.emp_name DESC,S1.pos
▼ 出力
先頭の SELECT文の中に CASE文を使って項目を追加しています
『今の給料情報』と『1つ前の給料情報』が
縦に並んでいるのを横に並ぶように加工しています
昇給日を見ると
posが1の時に sal_date を表示させて curr_date と名前を付ける
posが2の時に sal_date を表示させて prev_date と名前を付ける
回答SQL
SQL
SELECT S1.emp_name,
MAX(CASE WHEN pos = 1 THEN sal_date ELSE NULL END) AS curr_date,
MAX(CASE WHEN pos = 1 THEN sal_amt ELSE NULL END) AS curr_amt,
MAX(CASE WHEN pos = 2 THEN sal_date ELSE NULL END) AS prev_date,
MAX(CASE WHEN pos = 2 THEN sal_amt ELSE NULL END) AS prev_amt
FROM
(
SELECT emp_name,
sal_date,
sal_amt,
RANK() OVER (PARTITION BY emp_name ORDER BY sal_date DESC) FROM Salaries
) AS S1 (emp_name, sal_date, sal_amt, pos)
WHERE pos < 3
GROUP BY S1.emp_name -- 追加
ORDER BY S1.emp_name DESC
SELECT S1.emp_name,
MAX(CASE WHEN pos = 1 THEN sal_date ELSE NULL END) AS curr_date,
MAX(CASE WHEN pos = 1 THEN sal_amt ELSE NULL END) AS curr_amt,
MAX(CASE WHEN pos = 2 THEN sal_date ELSE NULL END) AS prev_date,
MAX(CASE WHEN pos = 2 THEN sal_amt ELSE NULL END) AS prev_amt
FROM
(
SELECT emp_name,
sal_date,
sal_amt,
RANK() OVER (PARTITION BY emp_name ORDER BY sal_date DESC) FROM Salaries
) AS S1 (emp_name, sal_date, sal_amt, pos)
WHERE pos < 3
GROUP BY S1.emp_name -- 追加
ORDER BY S1.emp_name DESC
▼ 出力
emp_name | curr_date | curr_amt | prev_date | prev_amt |
---|---|---|---|---|
Tom | 1996-12-20 | 900 | 1996-10-20 | 800 |
Harry | 1996-09-20 | 700 | 1996-07-20 | 500 |
Dick | 1996-06-20 | 500 | 空白 | 空白 |
社員名(emp_name) で GROUP BY して1人1行表示にしている
データが集約されているので、CASE分で追加した項目も MAXを使って集約する
⇒ 集約しないと下記の様なエラーとなる
参考文献
Author And Source
この問題について(可視化SQL | 現在の給料と昇給前の給料 を1行で表示する(その8) | Window 関数), 我々は、より多くの情報をここで見つけました https://qiita.com/hidesroom/items/3aa33c4b51f6fc67d7b3著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .