完整模拟面试题集
在面试条件下限时完成端到端题目,综合运用连接、窗口函数和 CTE。
完整模拟面试题集 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。
SQL 面试环节如何进行
这项综合练习会让您在面试条件下完成一系列完整的模拟题,综合运用连接、窗口函数和 CTE。首先要掌握的是方法层面的能力:如何在面试现场表现。
- 复述问题并确认模式。
- 在编写代码前,先澄清边界情况(NULL 值、并列和重复数据)。
- 讲述您的思路,然后编写查询。
- 在脑中用一个很小的样例进行测试。
面试官对您过程的评价,和对最终查询的评价同样重要。
共享模式
下面所有问题都使用这个小型电子商务模式。请先读一遍,这样每个查询才容易理解。
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。
优先使用 NOT EXISTS,因为它能安全处理 NULL(不像 NOT IN)。请提及这一差异;这正是面试官想考察的内容。
-- 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:第二高的订单金额
“找出第二高的不重复订单金额。”最简洁且能正确处理并列的解决方案是使用 DENSE_RANK,这样重复的金额会共享同一排名。
需要指出的边界情况是:如果不存在第二个不同的值,查询将不返回任何行。这可能可以接受,也可能需要根据需求使用 COALESCE 包装。
SELECT amount
FROM (
SELECT amount,
DENSE_RANK() OVER (ORDER BY amount DESC) AS rnk
FROM orders
) ranked
WHERE rnk = 2;问题 4:每位客户的最新订单
“返回每位客户最近的一笔订单。”这是按键保留最新行的模式,可以通过按客户分区并按日期降序排列的 ROW_NUMBER 来解决。
请添加一个并列决胜字段(订单编号),这样当两笔订单日期相同时,结果仍然是确定的。这是优秀候选人会提到的细节。
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 结合起来。
第一步按月份聚合;第二步使用 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 中对已建立索引的列使用函数。 - 对于大型反连接,优先使用 EXISTS 而不是 IN;带有 NULL 的
NOT IN会悄无声息地返回空结果。 - 在 WHERE 中筛选外连接得到的列,会悄悄地将其变成内连接。
- 始终添加并列决胜字段,确保前 N 个结果是确定的。
- 检查 EXPLAIN 计划,查看大表上是否存在顺序扫描。
快速检查
您需要获取每位客户唯一的一笔最新订单,并且两笔订单可能具有相同的日期。
回顾:完整模拟面试题集
您已经从头到尾练习了面试中最常见的问题:
- 使用聚合 + LIMIT 获取消费金额最高的前 N 个结果。
- 使用 NOT EXISTS 实现反连接(可安全处理 NULL)。
- 使用 DENSE_RANK 查找第 N 高的值,使用 ROW_NUMBER 查找每个键的最新记录和每组的顶级记录。
- 使用 LAG 处理月环比,使用 SUM OVER 计算累计总额。
- 使用间隙与岛屿的行号技巧处理连续记录。
- 每次回答最后都要讨论索引、EXPLAIN 和常见陷阱。
常见问题解答
「完整模拟面试题集」课时是免费的吗?
是的 — 「完整模拟面试题集」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。
「完整模拟面试题集」这节课中我会学到什么?
在面试条件下限时完成端到端题目,综合运用连接、窗口函数和 CTE。 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Coding Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「完整模拟面试题集」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Coding Interview Prep 课中编写并运行代码吗?
能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 通过第三范式实现规范化
- ER 建模与关系基数
- 星型模式与数据仓库设计
- 完整模拟面试题集