0Pricing
Coding Interview Prep · 课时

完整模拟面试题集

在面试条件下限时完成端到端题目,综合运用连接、窗口函数和 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 通过第三范式实现规范化
  2. ER 建模与关系基数
  3. 星型模式与数据仓库设计
  4. 完整模拟面试题集
← 返回 Coding Interview Prep