DBスペシャリスト 午前2試験から、Jupyter NotebookでSQLクエリを学んでみよう
10/12(日)に、データベーススペシャリスト試験が実施されます。
私は試験1週間前になり、ようやく本腰を入れて対策を始めたところです。
4択選択問題の午前2試験では、データベースに関する包括的な知識が問われますが、SQL文法についても1~2割ほど出題があります。
今回は、過去問の中から実践的な内容が問われる良い問題だな~と思ったものをピックアップしました!
対象読者
出てきた問題の中から、解く上で必要とされる知識を問題にしてみました。
答えに詰まった方は、実際に解いてみると知識を深めるきっかけになるかもしれません!
-
RANK() OVER (PARTITION BY xxx ORDER BY yyy DESC)の動作を説明できますか? -
AVG()やCOUNT()は、NULL値が含まれていた場合どのように扱われますか? -
UNIONは、重複するレコードをどう扱いますか? -
WHERE句とFROM句におけるサブクエリ(副問い合わせ)の動作の違いを説明できますか? -
INとEXISTS演算子の挙動はどう違い、どう使い分けるべきですか?
SQLを実際に動かしてみる
Githubはipynb形式のファイルをいい感じにプレビューしてくれるので、今回はipynbでSQLを実行できるDocker環境を作って検証してみました(書籍:データサイエンス100本ノックのサンプルリポジトリを参考にしました)
Github Codespacesを使えば、web上であればどの環境でも簡単に動作確認ができるので興味あればお試しください!
2023年 午前2 問9
DDL
CREATE TABLE seiseki (
氏名 VARCHAR(50) NOT NULL,
クラス CHAR(1) NOT NULL,
得点 INT NOT NULL
);
INSERT INTO seiseki (氏名, クラス, 得点) VALUES
('情報太郎', 'A', 80),
('情報次郎', 'A', 63),
('情報花子', 'B', 70),
('情報桜子', 'B', 92),
('情報三郎', 'A', 78);
成績
| 氏名 | クラス | 得点 |
|---|---|---|
| 情報太郎 | A | 80 |
| 情報次郎 | A | 63 |
| 情報花子 | B | 70 |
| 情報桜子 | B | 92 |
| 情報三郎 | A | 78 |
問題: 下記表の「順位」カラムを出すために必要なクエリは何か?必要な字句を埋めよ
| 氏名 | クラス | 得点 | 順位 |
|---|---|---|---|
| 情報太郎 | A | 80 | 1 |
| 情報三郎 | A | 78 | 2 |
| 情報次郎 | A | 63 | 3 |
| 情報桜子 | B | 92 | 1 |
| 情報花子 | B | 70 | 2 |
SELECT
*,
???() OVER (PARTITION BY ??? ORDER BY ??? DESC) AS 順位
FROM
seiseki;
解答
rank() OVER (PARTITION BY クラス ORDER BY 得点 DESC) AS 順位です。
2022年 午前2 問7
DDL
CREATE TABLE 商品 (
商品コード CHAR(4) PRIMARY KEY,
商品名 VARCHAR(50) NOT NULL,
商品ランク CHAR(1)
);
CREATE TABLE 商品別売上実績 (
商品コード CHAR(4) PRIMARY KEY,
売上合計金額 INTEGER NOT NULL
);
INSERT INTO 商品 (商品コード, 商品名, 商品ランク) VALUES
('S001', 'PPP', 'A'),
('S002', 'QQQ', 'A'),
('S003', 'RRR', 'A'),
('S004', 'SSS', 'B'),
('S005', 'TTT', 'C'),
('S006', 'UUU', 'C');
INSERT INTO 商品別売上実績 (商品コード, 売上合計金額) VALUES
('S001', 50),
('S003', 250),
('S004', 350),
('S006', 450);
商品
| 商品コード | 商品名 | 商品ランク |
|---|---|---|
| S001 | PPP | A |
| S002 | QQQ | A |
| S003 | RRR | A |
| S004 | SSS | B |
| S005 | TTT | C |
| S006 | UUU | C |
商品別売上実績
| 商品コード | 売上合計金額 |
|---|---|
| S001 | 50 |
| S003 | 250 |
| S004 | 350 |
| S006 | 450 |
問題: 以下のSQLを実行して得られる値は?
SELECT
AVG(売上合計金額) AS 売上平均金額
FROM
商品
LEFT OUTER JOIN
商品別売上実績
ON
商品.商品コード = 商品別売上実績.商品コード
WHERE
商品ランク = 'A'
GROUP BY
商品ランク;
解答
150です。
2022年 午前2 問9
DDL
CREATE TABLE R (
char VARCHAR(10) NOT NULL,
num INTEGER
);
INSERT INTO R (char, num) VALUES
('apple', 1),
('banana', 5),
('cherry', 3),
('apple', 1);
R テーブル
| char | num |
|---|---|
| apple | 1 |
| banana | 5 |
| cherry | 3 |
| apple | 1 |
問題: テーブルRについて、以下のSQLのクエリから出力される値は等しい?
SELECT * FROM R UNION SELECT * FROM R;
SELECT * FROM R
解答
異なります。UNION ALLはすべてのレコードを出力しますが、UNIONは重複レコードは出力されません。Rテーブルには('apple', 1)が重複して存在するため、SELECT * FROM R UNION SELECT * FROM Rは3行を返しますが、SELECT * FROM Rは重複を含めて4行を返します。
2020年 午前2 問10
DDL
CREATE TABLE shain (
社員番号 INT PRIMARY KEY,
社員名 VARCHAR(50) NOT NULL,
性別 CHAR(1),
生年月日 DATE
);
INSERT INTO shain (社員番号, 社員名, 性別, 生年月日) VALUES
(1001, '情報一郎', '男', '1978-04-12'),
(1002, '情報一子', '女', '1990-09-25'),
(1003, '情報二郎', '男', '1985-11-03'),
(1004, '情報二子', '女', '2001-07-18'),
(1005, '情報三郎', '男', '1995-02-01');
社員
| 社員番号 | 社員名 | 性別 | 生年月日 |
|---|---|---|---|
| 1001 | 情報一郎 | 男 | 1978-04-12 |
| 1002 | 情報一子 | 女 | 1990-09-25 |
| 1003 | 情報二郎 | 男 | 1985-11-03 |
| 1004 | 情報二子 | 女 | 2001-07-18 |
| 1005 | 情報三郎 | 男 | 1995-02-01 |
問題: 「男女それぞれの最年長社員」を除いた全社員を取り出すSQLはどのように記述するか。???に該当する字句を埋めよ
| 社員番号 | 社員名 | 性別 | 生年月日 |
|---|---|---|---|
| 1003 | 情報二郎 | 男 | 1985-11-03 |
| 1004 | 情報二子 | 女 | 2001-07-18 |
| 1005 | 情報三郎 | 男 | 1995-02-01 |
select
*
from
shain as s1
where
s1.生年月日 > (
select
min(s2.生年月日)
from
shain as s2
where
??? = ???
);
解答
s1.性別 = s2.性別です。
2019年 午前2 問14
DDL
CREATE TABLE kaiin (
行番号 INTEGER PRIMARY KEY,
会員番号 TEXT,
項目名 TEXT,
項目値 TEXT
);
INSERT INTO kaiin (行番号, 会員番号, 項目名, 項目値) VALUES
(1, '0111', '会員名', '情報太郎'),
(2, '0111', '最終購入年月日', '2019-02-05'),
(3, '0112', '会員名', '情報花子'),
(4, '0112', '最終購入年月日', '2019-01-30'),
(5, '0112', '最終購入年月日', '2019-02-01'),
(6, '0113', '会員名', '情報次郎');
会員
| 行番号 | 会員番号 | 項目名 | 項目値 |
|---|---|---|---|
| 1 | 0111 | 会員名 | 情報太郎 |
| 2 | 0111 | 最終購入年月日 | 2019-02-05 |
| 3 | 0112 | 会員名 | 情報花子 |
| 4 | 0112 | 最終購入年月日 | 2019-01-30 |
| 5 | 0112 | 最終購入年月日 | 2019-02-01 |
| 6 | 0113 | 会員名 | 情報次郎 |
問題: 以下条件をもとに出力された結果表を出力した。クエリの???に該当するカラム名は?
条件:以下の「会員項目」について、
- 会員ごとに固有の会員番号をもつ
- 同一会員番号で、同一項目名の行が複数ある場合、より大きい行番号の項目値を採用する
結果表
| 会員番号 | 会員名 | 最終購入年月日 |
|---|---|---|
| 0111 | 情報太郎 | 2019-02-05 |
| 0112 | 情報花子 | 2019-02-01 |
| 0113 | 情報次郎 | NULL |
SELECT
t1.会員番号,
MAX(CASE WHEN t1.項目名 = ??? THEN t1.項目値 END) AS 会員名,
MAX(CASE WHEN t1.項目名 = ??? THEN t1.項目値 END) AS 最終購入年月日
FROM (
SELECT
t2.会員番号,
t2.項目名,
t2.項目値
FROM
kaiin t2
WHERE
t2.行番号 IN (
SELECT
MAX(行番号)
FROM
kaiin
GROUP BY
会員番号, 項目名
)
) AS t1
GROUP BY
t1.会員番号
ORDER BY
t1.会員番号;
解答
会員名、最終購入年月日です。
2020年 午前2 問8
DDL
CREATE TABLE Employee (
EmployeeCode CHAR(4) PRIMARY KEY,
ManagerCode CHAR(4),
EmployeeName VARCHAR(50),
FOREIGN KEY (ManagerCode) REFERENCES Employee(EmployeeCode)
);
INSERT INTO Employee (EmployeeCode, ManagerCode, EmployeeName) VALUES
('S001', NULL, 'A'),
('S002', 'S001', 'B'),
('S003', 'S001', 'C'),
('S004', 'S003', 'D'),
('S005', NULL, 'E'),
('S006', 'S005', 'F'),
('S007', 'S006', 'G');
社員テーブル
| employeecode | managercode | employeename |
|---|---|---|
| S001 | NULL | A |
| S002 | S001 | B |
| S003 | S001 | C |
| S004 | S003 | D |
| S005 | NULL | E |
| S006 | S005 | F |
| S007 | S006 | G |
問題: 以下のSQLを実行して得られる結果はどうなるか?
SELECT
X.EmployeeCode
FROM
Employee as X
WHERE NOT EXISTS (
SELECT
*
FROM
Employee AS Y
WHERE X.EmployeeCode = Y.ManagerCode
);
解答
ManagerCodeに存在しないEmployeeCode、つまりS002,S004,S007になります
問題: 以下のNOT INクエリにおいて、実行結果は同一であるか?
SELECT
X.EmployeeCode
FROM
Employee as X
WHERE X.EmployeeCode NOT IN (
SELECT
Y.ManagerCode
FROM
Employee AS Y
);
解答
異なります。IN句はサブクエリの結果にNULLが含まれていると、外側のクエリ全体の結果が1行も返されなくなるので、WHERE句にWHERE Y.ManagerCode IS NOT NULLを追加するなどしてNULLを含まないクエリを返すようにしましょう。
まとめ
普段からSQLに書き慣れていないと、イメージがつきづらい問題もあるのではないでしょうか。
今回はipynb形式で実際に手を動かしてクエリ結果を検証してみましたが、簡単に環境構築・実行できますし、実行結果が出力表として保持された状態でコミットできるので、学習環境としてかなり良いと思いました。
最後に、10/12(日)に受験予定のみなさま、ラストスパートがんばっていきましょう!
Discussion