👳

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句におけるサブクエリ(副問い合わせ)の動作の違いを説明できますか?
  • INEXISTS演算子の挙動はどう違い、どう使い分けるべきですか?

SQLを実際に動かしてみる

Githubはipynb形式のファイルをいい感じにプレビューしてくれるので、今回はipynbでSQLを実行できるDocker環境を作って検証してみました(書籍:データサイエンス100本ノックのサンプルリポジトリを参考にしました)

Github Codespacesを使えば、web上であればどの環境でも簡単に動作確認ができるので興味あればお試しください!

https://github.com/kmishima16/ipa-db-ipynb

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