SQL 面接の質問
SQL面接では、データに関する質問を正確かつ効率的に表現できるかが試されます。ライブでクエリを書くことが求められ、多くの場合、結合、集計、ウィンドウ関数が含まれます。
SQL 面接で問われる内容
結合とフィルタリング
INNER/LEFT/RIGHT/FULL結合、WHEREとHAVING、自己結合。
集計
GROUP BY、集計関数、グループ化の注意点(NULL、重複)。
ウィンドウ関数
ROW_NUMBER、RANK、LAG/LEAD、累計、グループごとの上位N件。
パフォーマンス
インデックス、クエリプラン、sargability、クエリが遅い理由。
SQL 面接の質問例
- テーブル内の2番目に高い給与を取得してください。良い回答が押さえる点
- DISTINCT/ORDER BY/LIMIT OFFSET
- スカラーサブクエリ
- NULL 処理
- 重複給与の扱い
サンプル回答を見る
2番目に高い給与を取得する方法として、DISTINCT + ORDER BY + LIMIT OFFSET を使う方法と、スカラーサブクエリを使う方法があります。前者はシンプルですが、OFFSET は大きなテーブルで遅くなる可能性があります。後者は、最高給与を除いた中から MAX を取る方法です。どちらも同じ給与が複数ある場合、DISTINCT で重複を排除する必要があります。最も一般的なのは、サブクエリで最高給与を除いた最大値を取る方法です。注意点として、テーブルに1行しかない場合のハンドリングが必要です。また、ウィンドウ関数 DENSE_RANK を使う方法もあり、こちらの方が汎用的です。
参考コードsql -- 方法1: サブクエリ SELECT MAX(salary) AS second_highest_salary FROM employees WHERE salary < (SELECT MAX(salary) FROM employees); -- 方法2: DISTINCT + LIMIT/OFFSET (MySQL/PostgreSQL) SELECT DISTINCT salary FROM employees ORDER BY salary DESC LIMIT 1 OFFSET 1; -- 方法3: ウィンドウ関数 (DENSE_RANK) SELECT DISTINCT salary FROM ( SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk FROM employees ) ranked WHERE rnk = 2; - グループごとに上位N行を取得してください(例:カテゴリごとに上位3製品)。良い回答が押さえる点
- ROW_NUMBER / RANK / DENSE_RANK
- PARTITION BY
- ウィンドウ関数のパフォーマンス
- 同順位の扱い
サンプル回答を見る
グループごとに上位N行を取得するには、ウィンドウ関数 ROW_NUMBER() または RANK() / DENSE_RANK() を使用します。ROW_NUMBER() は同順位を無視して一意の連番を振りますが、RANK() は同順位に同じ番号を付け、次の番号をスキップします。DENSE_RANK() は同順位に同じ番号を付け、次の番号は連続です。用途に応じて使い分けます。例えば、カテゴリごとに売上上位3製品を取得する場合、カテゴリで PARTITION BY し、売上で ORDER BY DESC して ROW_NUMBER を割り当て、その番号が N 以下の行をフィルタします。この方法はサブクエリや CTE と組み合わせて使います。注意点として、同順位の扱いを明確にし、必要なウィンドウ関数を選択してください。
参考コードsql -- 例: カテゴリごとに売上上位3製品 WITH ranked_products AS ( SELECT category_id, product_name, sales_amount, ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY sales_amount DESC) AS rn FROM products ) SELECT category_id, product_name, sales_amount FROM ranked_products WHERE rn <= 3 ORDER BY category_id, rn; - WHEREとHAVINGの違いを説明してください。良い回答が押さえる点
- WHERE は行フィルタ (グループ化前)
- HAVING はグループフィルタ (グループ化後)
- 集計関数の使用可否
- SQL の評価順序
サンプル回答を見る
WHERE と HAVING の違いは、WHERE は行レベルのフィルタリングをグループ化前に行い、集計関数が使えないのに対し、HAVING はグループ化後の集計結果に対してフィルタリングを行い、集計関数が使えます。評価順序は WHERE → GROUP BY → HAVING です。パフォーマンスのためには、WHERE で行を減らしてから GROUP BY を行うことが推奨されます。
- 重複行を見つけて1つだけ残すクエリを書いてください。良い回答が押さえる点
- GROUP BY + HAVING COUNT(*) > 1
- ROW_NUMBER で重複削除
- 自己結合
- トランザクションと一意制約
サンプル回答を見る
重複行を見つけて1つだけ残す方法はいくつかあります。まず、重複を特定するには GROUP BY と HAVING COUNT(*) > 1 を使います。次に、削除する方法として、ROW_NUMBER() ウィンドウ関数を使って重複グループ内に連番を振り、番号が1より大きい行を削除します。または、自己結合を使って同じ値を持つ行のうち、一意な識別子(例:ID)が小さいもの以外を削除することもできます。注意点として、削除前にバックアップを取ること、トランザクション内で実行してロールバック可能にすること、また将来の重複を防ぐために一意制約を追加することを検討してください。以下の例では、email が重複している行を、id が最小のものだけ残して削除します。
参考コードsql -- 重複行を確認 SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1; -- 重複削除: id が最小のものだけ残す (PostgreSQL/MySQL 8.0+) DELETE FROM users WHERE id NOT IN ( SELECT MIN(id) FROM users GROUP BY email ); -- または ROW_NUMBER を使う (PostgreSQL/MySQL 8.0+) WITH numbered AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY email ORDER BY id) AS rn FROM users ) DELETE FROM users WHERE id IN (SELECT id FROM numbered WHERE rn > 1); - 毎日の売上の累計を計算してください。良い回答が押さえる点
- SUM ウィンドウ関数 + ORDER BY
- 累計計算のパフォーマンス
- 日付の連続性と欠損
- フレーム指定のデフォルト
サンプル回答を見る
毎日の売上の累計を計算するには、SUM ウィンドウ関数に ORDER BY 日付を組み合わせます。デフォルトのフレームは RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ですが、通常は ROWS で指定します。例えば、売上テーブルが (sale_date, amount) を持つ場合、SELECT sale_date, amount, SUM(amount) OVER (ORDER BY sale_date ROWS UNBOUNDED PRECEDING) AS cumulative_sales とします。注意点として、日付が連続していない場合でも累計は正確ですが、欠損日を補完したい場合は OUTER JOIN などで日付マスタと結合する必要があります。また、大量データの場合、ウィンドウ関数は全行をソートするため、インデックスを適切に張ることが重要です。
参考コードsql -- 毎日の売上と累計 SELECT sale_date, SUM(amount) AS daily_sales, SUM(SUM(amount)) OVER (ORDER BY sale_date ROWS UNBOUNDED PRECEDING) AS cumulative_sales FROM sales GROUP BY sale_date ORDER BY sale_date; -- 別解: 日別集計を事前に計算 (パフォーマンス改善) WITH daily AS ( SELECT sale_date, SUM(amount) AS daily_amount FROM sales GROUP BY sale_date ) SELECT sale_date, daily_amount, SUM(daily_amount) OVER (ORDER BY sale_date ROWS UNBOUNDED PRECEDING) AS cumulative FROM daily ORDER BY sale_date; - 遅いクエリを高速化するにはどうすればよいですか?クエリプランから何がわかりますか?良い回答が押さえる点
- インデックス戦略
- クエリプランの読み方 (Seq Scan vs Index Scan)
- 統計情報とANALYZE
- EXPLAIN ANALYZE
- ボトルネック特定
サンプル回答を見る
遅いクエリの高速化は、まず EXPLAIN ANALYZE で実行計画を取得し、ボトルネックを特定します。Seq Scan が発生している場合はインデックス追加を検討し、結合順序が不適切なら統計情報を更新するかクエリを書き換えます。また、必要なカラムだけを SELECT し、サブクエリよりも JOIN を使うことでパフォーマンスが改善することがあります。インデックスは多すぎると更新が遅くなるため、適切なバランスが必要です。
準備方法
- 手でクエリを書く練習をしましょう — 多くの面接ではオートコンプリートのない共有エディタを使用します。
- ウィンドウ関数をマスターしましょう。それらは「グループごとの上位N件」や累計の問題の多くを解決します。
- NULLの動作と重複について明確に考えましょう — よくある落とし穴です。
- クエリプランを読み、なぜインデックスが役立つかを説明できるように準備しましょう。
よくある質問
どのSQL方言を勉強すべきですか?
標準SQLでほとんどの面接に対応できます。ウィンドウ関数とCTEは広くサポートされていますが、一部の関数はPostgreSQL、MySQL、SQL Serverで異なることを認識しておきましょう。
SQL面接でクエリの最適化は必要ですか?
データ/バックエンド職では、はい — インデックス、クエリプラン、クエリが遅い理由について議論できるように準備しましょう。
ウィンドウ関数はよく出題されますか?
非常に多いです。ROW_NUMBER/RANKや累計は、特にデータエンジニアリングや分析職で頻繁に出題されます。
SQL面接の練習方法は?
実際のスキーマに対してクエリ問題を解き、アプローチを説明しましょう。OfferslyはSQLに特化した質問を生成し、あなたの思考を採点します。
SQL の質問をAIで練習、瞬時にフィードバック
履歴書をアップロードして、パーソナライズされた模擬面接を受け、改善点を確認 — 無料で始められます。