面试题答案
一键面试子查询实现
SELECT sub.category_name, sub.product_name
FROM (
SELECT
c.category_name,
p.product_name,
SUM(oi.quantity) AS total_quantity,
ROW_NUMBER() OVER (PARTITION BY c.category_id ORDER BY SUM(oi.quantity) DESC) AS rn
FROM
products p
JOIN
categories c ON p.category_id = c.category_id
JOIN
orders_items oi ON p.product_id = oi.product_id
GROUP BY
c.category_id, c.category_name, p.product_id, p.product_name
) sub
WHERE sub.rn = 1;
性能问题
- 嵌套查询的开销:子查询通常需要先执行子查询部分,再执行外部查询,这种嵌套结构增加了查询解析和执行的复杂度,特别是在大数据量下,开销较大。
- 多次扫描数据:在子查询和外部查询中,可能会对相同的表进行多次扫描,导致I/O开销增加。
- 聚合和排序操作:在子查询中使用
GROUP BY
和ROW_NUMBER()
函数进行聚合和排序,这对于大数据量的表操作是比较耗时的。
替代优化方案
方案一:使用公共表达式(CTE)
WITH category_product_quantity AS (
SELECT
c.category_id,
c.category_name,
p.product_id,
p.product_name,
SUM(oi.quantity) AS total_quantity
FROM
products p
JOIN
categories c ON p.category_id = c.category_id
JOIN
orders_items oi ON p.product_id = oi.product_id
GROUP BY
c.category_id, c.category_name, p.product_id, p.product_name
),
ranked_products AS (
SELECT
category_id,
category_name,
product_name,
total_quantity,
ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY total_quantity DESC) AS rn
FROM
category_product_quantity
)
SELECT
category_name,
product_name
FROM
ranked_products
WHERE
rn = 1;
CTE 使得查询逻辑更加清晰,并且数据库优化器有时能更好地处理CTE,可能会减少重复扫描数据的问题。
方案二:使用临时表
-- 创建临时表存储每个产品的总订单数量
CREATE TEMPORARY TABLE temp_product_quantity AS
SELECT
c.category_id,
c.category_name,
p.product_id,
p.product_name,
SUM(oi.quantity) AS total_quantity
FROM
products p
JOIN
categories c ON p.category_id = c.category_id
JOIN
orders_items oi ON p.product_id = oi.product_id
GROUP BY
c.category_id, c.category_name, p.product_id, p.product_name;
-- 在临时表上进行排序和筛选
SELECT
category_name,
product_name
FROM (
SELECT
category_id,
category_name,
product_name,
total_quantity,
ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY total_quantity DESC) AS rn
FROM
temp_product_quantity
) sub
WHERE
rn = 1;
-- 删除临时表
DROP TEMPORARY TABLE temp_product_quantity;
临时表可以将中间结果存储起来,减少重复计算,在一定程度上提高查询性能,尤其是对于复杂的查询。