総合模擬面接問題集
面接を想定し、結合、ウィンドウ関数、CTEを組み合わせたエンドツーエンドの問題に時間制限付きで取り組みます。
「総合模擬面接問題集」はCoddyKit上の無料Coding Interview Prepレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはCoding Interview Prep学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Coding Interview Prepコースには全4レッスンが含まれています。
SQL面接の進め方
この総仕上げでは、面接の状況を想定し、結合、ウィンドウ関数、CTEを組み合わせた本格的な模擬問題に取り組みます。まずはメタスキル、つまり面接の場での振る舞い方から始めます。
- 問題を言い換えて確認し、スキーマを確認します。
- コーディングする前に、NULL、同順位、重複などのエッジケースを確認します。
- アプローチを説明してから、クエリを書きます。
- 頭の中で小さなサンプルに対してテストします。
面接官は最終的なクエリだけでなく、問題への取り組み方も同じように評価します。
共通スキーマ
以下のすべての問題では、この小規模なeコマースのスキーマを使用します。各クエリの意味が分かるように、最初に一度目を通してください。
customers(id, name, country)orders(id, customer_id, order_date, status, amount)order_items(order_id, product_id, quantity)products(id, name, category, price)
このスキーマを覚えておいてください。以降のレッスンでは、これらのテーブルを参照します。
-- orders.status is one of: 'paid','pending','cancelled'
-- amount is the order total in the customer's currency問題1:支出額上位の顧客
「支払済み金額の合計が上位3人の顧客について、名前と合計金額を返してください。」
アプローチは、支払済みの注文に絞り、顧客ごとに集計して並べ替え、上位に制限します。面接官が仕込むエッジケースとして、キャンセル済みと保留中の注文を除外することも明示してください。
SELECT c.name,
SUM(o.amount) AS total_spend
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'paid'
GROUP BY c.id, c.name
ORDER BY total_spend DESC
LIMIT 3;問題2:一度も注文していない顧客
「一度も注文したことがない顧客を一覧表示してください。」これはアンチ結合のパターンです。LEFT JOINとIS NULLを使う方法、またはNOT EXISTSを使う方法の2つが、簡潔な解決策です。
NOT INとは異なりNULL安全であるため、NOT EXISTSを優先してください。この違いに触れましょう。まさに面接官が確認したいポイントです。
-- NULL-safe anti-join
SELECT c.id, c.name
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
);問題3:2番目に高い注文金額
「重複を除いた注文金額のうち、2番目に高い金額を見つけてください。」最も簡潔で同順位にも対応できる解決策は、重複する金額に同じ順位を付けるDENSE_RANKを使う方法です。
確認すべきエッジケースは、2番目に異なる値が存在しない場合です。この場合は行を返しません。要件によってはこれで問題ありませんが、COALESCEのラッパーが必要になることもあります。
SELECT amount
FROM (
SELECT amount,
DENSE_RANK() OVER (ORDER BY amount DESC) AS rnk
FROM orders
) ranked
WHERE rnk = 2;問題4:顧客ごとの最新の注文
「各顧客の最も新しい注文を返してください。」これはキーごとに最新の行だけを残すパターンで、顧客ごとにパーティションを分け、日付の降順で並べたROW_NUMBERを使って解決します。
2つの注文の日付が同じ場合にも結果を決定的にするため、タイブレーカーとして注文IDを追加してください。この点まで含めると、候補者として高く評価されます。
SELECT customer_id, id AS order_id, order_date, amount
FROM (
SELECT o.*,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date DESC, id DESC
) AS rn
FROM orders o
) t
WHERE rn = 1;問題5:前月比の成長率
「月ごとの支払済み売上と、前月に対する変化率を計算してください。」これは、CTEでの集計とLAGを組み合わせる問題です。
手順1では月ごとに集計し、手順2ではLAGを使って各月と前月を比較します。最初の月には前月がないため、除算でエラーにならないように処理してください。
WITH monthly AS (
SELECT DATE_TRUNC('month', order_date) AS mth,
SUM(amount) AS revenue
FROM orders
WHERE status = 'paid'
GROUP BY DATE_TRUNC('month', order_date)
)
SELECT mth,
revenue,
LAG(revenue) OVER (ORDER BY mth) AS prev_revenue,
ROUND(
100.0 * (revenue - LAG(revenue) OVER (ORDER BY mth))
/ NULLIF(LAG(revenue) OVER (ORDER BY mth), 0), 2
) AS pct_change
FROM monthly
ORDER BY mth;問題6:カテゴリごとの代表商品
「各カテゴリについて、総数量が最も多い商品を返してください。」これはグループごとの上位N件を求めるパターンです。集計し、パーティション内で順位を付け、順位1に絞り込みます。
同順位を考慮する必要がある場合は、ROW_NUMBERをRANKに置き換えて、同率首位の商品をすべて表示します。この選択理由を説明できれば、両者の違いを理解していることが伝わります。
WITH sales AS (
SELECT p.category,
p.name AS product,
SUM(oi.quantity) AS qty
FROM order_items oi
JOIN products p ON p.id = oi.product_id
GROUP BY p.category, p.name
)
SELECT category, product, qty
FROM (
SELECT s.*,
ROW_NUMBER() OVER (
PARTITION BY category ORDER BY qty DESC
) AS rn
FROM sales s
) r
WHERE rn = 1;問題7:売上の累計
「日ごとの支払済み売上の累計を表示してください。」順序付きフレームを指定したウィンドウSUMを使うと、自己結合なしで累計を計算できます。
行単位で正確に累積するにはROWSフレームを指定すると説明してください。デフォルトのRANGEフレームは、日付が同じ行がある場合に予期しない動作をすることがあります。
SELECT order_date,
SUM(daily) OVER (
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM (
SELECT order_date, SUM(amount) AS daily
FROM orders
WHERE status = 'paid'
GROUP BY order_date
) d
ORDER BY order_date;問題8:連続するアクティブ日
「支払済みの注文がある日が3日以上連続するユーザーを見つけてください。」これは、行番号の差分トリックを使うギャップと島の応用問題です。
ユーザーごとの行番号から日付を引くと、連続する期間の中では一定の値になります。その値でグループ化して件数を数えます。これはシニアレベルの理解を示すポイントです。
WITH days AS (
SELECT DISTINCT customer_id, order_date
FROM orders WHERE status = 'paid'
),
grp AS (
SELECT customer_id, order_date,
order_date - (ROW_NUMBER() OVER (
PARTITION BY customer_id ORDER BY order_date
) * INTERVAL '1 day') AS island
FROM days
)
SELECT customer_id, COUNT(*) AS streak_len
FROM grp
GROUP BY customer_id, island
HAVING COUNT(*) >= 3;パフォーマンスとよくある落とし穴
正しいクエリを書いた後、面接官は「どうすれば高速化できますか?」と質問し、典型的な落とし穴を見抜けるか確認します。次のチェックリストを用意しておきましょう。
- 結合列とフィルタ列にインデックスを付けます(例:
orders(customer_id, status))。WHERE句でインデックス列に関数を適用するのは避けます。 - 大規模なアンチ結合ではINよりEXISTSを優先します。NULLを含む
NOT INは、何も返さずに終了することがあります。 - 外部結合した列をWHEREでフィルタリングすると、気付かないうちに内部結合になります。
- 上位N件の結果を決定的にするため、必ずタイブレーカーを追加します。
- 大きなテーブルでシーケンシャルスキャンが発生していないか、EXPLAINプランを確認します。
クイックチェック
各顧客について最も新しい注文を1件だけ取得する必要があり、同じ日付の注文が2件存在する可能性があります。
振り返り:模擬面接問題総復習
面接で頻出する問題に、最初から最後まで取り組みました。
- 上位N件の支出額には、集計とLIMITを使用します。
- NOT EXISTSによるアンチ結合(NULL安全)を使用します。
- N番目に高い値にはDENSE_RANK、キーごとの最新行とグループごとの上位行にはROW_NUMBERを使用します。
- 月次比較にはLAG、累計にはSUM OVERを使用します。
- 連続期間には、ギャップと島の行番号トリックを使用します。
- すべての回答の最後に、インデックス、EXPLAIN、よくある落とし穴について説明します。
よくある質問
「総合模擬面接問題集」レッスンは無料ですか?
はい。「総合模擬面接問題集」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Coding Interview Prepコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Coding Interview Prepコースには全4レッスンが含まれています。
「総合模擬面接問題集」で何を学びますか?
面接を想定し、結合、ウィンドウ関数、CTEを組み合わせたエンドツーエンドの問題に時間制限付きで取り組みます。 ブラウザで直接実行するハンズオンコードでCoding Interview Prepを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Coding Interview Prepを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのCoding Interview Prepは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「総合模擬面接問題集」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このCoding Interview Prepレッスンでコードを書いて実行できますか?
はい。すべてのCoding Interview Prepレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。