面试题答案
一键面试SQL语句
WITH RankedProducts AS (
SELECT
p.product_id,
p.product_name,
op.quantity,
ROW_NUMBER() OVER (PARTITION BY p.category_id ORDER BY op.quantity DESC) AS rn
FROM
products p
JOIN
orders_products op ON p.product_id = op.product_id
)
SELECT
c.category_name,
rp.product_name,
rp.quantity
FROM
categories c
LEFT JOIN
RankedProducts rp ON c.category_id = rp.product_id AND rp.rn = 1;
使用EXPLAIN分析执行计划
EXPLAIN
WITH RankedProducts AS (
SELECT
p.product_id,
p.product_name,
op.quantity,
ROW_NUMBER() OVER (PARTITION BY p.category_id ORDER BY op.quantity DESC) AS rn
FROM
products p
JOIN
orders_products op ON p.product_id = op.product_id
)
SELECT
c.category_name,
rp.product_name,
rp.quantity
FROM
categories c
LEFT JOIN
RankedProducts rp ON c.category_id = rp.product_id AND rp.rn = 1;
可能存在的性能瓶颈
- JOIN操作:
products
表与orders_products
表的JOIN操作,如果表数据量较大,会消耗大量资源。特别是如果product_id
列上没有索引,JOIN操作可能需要全表扫描。 - 窗口函数:
ROW_NUMBER()
窗口函数计算每个类别内产品的销量排名,当数据量很大时,这可能导致性能问题。 - LEFT JOIN:
categories
表与RankedProducts
的LEFT JOIN操作,如果RankedProducts
结果集较大,会影响性能。
优化措施
- 添加索引:在
products
表的product_id
和category_id
列,以及orders_products
表的product_id
列上添加索引。
CREATE INDEX idx_products_product_id ON products(product_id);
CREATE INDEX idx_products_category_id ON products(category_id);
CREATE INDEX idx_orders_products_product_id ON orders_products(product_id);
- 优化窗口函数:如果数据量极大,可以考虑通过预处理数据,将窗口函数的计算结果存储在物化视图中,以减少实时计算的开销。
CREATE MATERIALIZED VIEW RankedProductsMV AS
SELECT
p.product_id,
p.product_name,
op.quantity,
ROW_NUMBER() OVER (PARTITION BY p.category_id ORDER BY op.quantity DESC) AS rn
FROM
products p
JOIN
orders_products op ON p.product_id = op.product_id;
然后使用物化视图进行查询:
SELECT
c.category_name,
rpmv.product_name,
rpmv.quantity
FROM
categories c
LEFT JOIN
RankedProductsMV rpmv ON c.category_id = rpmv.product_id AND rpmv.rn = 1;
- 调整JOIN顺序:根据表的大小和数据分布,尝试调整JOIN的顺序,可能会提高性能。例如,如果
categories
表相对较小,可以将其放在JOIN操作的前面。