MST

星途 面试题库

面试题:MySQL复杂子查询的优化与执行计划解读

现有三个表`products`(`product_id`、`product_name`、`category_id`),`categories`(`category_id`、`category_name`),`orders_products`(`order_id`、`product_id`、`quantity`)。要找出每个类别中销量最高的产品名称及销量。请写出实现该功能的SQL语句,此SQL可能涉及子查询。使用`EXPLAIN`分析其执行计划,说明可能存在的性能瓶颈以及如何优化。
30.3万 热度难度
数据库MySQL

知识考点

AI 面试

面试题答案

一键面试

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;

可能存在的性能瓶颈

  1. JOIN操作products表与orders_products表的JOIN操作,如果表数据量较大,会消耗大量资源。特别是如果product_id列上没有索引,JOIN操作可能需要全表扫描。
  2. 窗口函数ROW_NUMBER()窗口函数计算每个类别内产品的销量排名,当数据量很大时,这可能导致性能问题。
  3. LEFT JOINcategories表与RankedProducts的LEFT JOIN操作,如果RankedProducts结果集较大,会影响性能。

优化措施

  1. 添加索引:在products表的product_idcategory_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);
  1. 优化窗口函数:如果数据量极大,可以考虑通过预处理数据,将窗口函数的计算结果存储在物化视图中,以减少实时计算的开销。
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;
  1. 调整JOIN顺序:根据表的大小和数据分布,尝试调整JOIN的顺序,可能会提高性能。例如,如果categories表相对较小,可以将其放在JOIN操作的前面。