我是一个新的 SQL 学习者,也是 StackOverflow 的新手。希望我第一次发帖时没有错过任何重要的东西。
我碰巧从我的导师那里得到两个以下问题,说他们有不同的表现。但我不明白为什么它们在逻辑和计算成本方面不同。
查询 1:
SELECT First_Name,
SUM(total_sales_amount) AS sub_total_sales_amount FROM
(
select A.First_Name, C.product_quantity * D.Retail_Price AS t otal_sales_amount From join_demo.customer as A
inner join join_demo.customer_order as B on A.customer_id = B.customer_id
inner join join_demo.order_details C on B.order_id = C.order_id
inner join join_demo.product as D on C.product_id= D.product_id
) E
GROUP BY 1
ORDER BY sub_total_sales_amount DESC LIMIT 1;
查询 2(有人告诉我这个有更好的性能):
SELECT A.First_Name, SUM(C.product_quantity * D.Retail_Price) AS sub_total_sales_amount
From join_demo.customer as A
inner join join_demo.customer_order as B on A.customer_id = B.customer_id
inner join join_demo.order_details C on B.order_id = C.order_id
inner join join_demo.product as D on C.product_id= D.product_id GROUP BY 1
ORDER BY sub_total_sales_amount DESC LIMIT 1;
我在本地 Mac 上运行 MySQL。但我想这将是一个关于 SQL 性能调优的一般问题。 有人可以解释这个问题吗?非常感谢!
更新:
感谢@Tim 和@MatBailie。我在每个查询之前都添加了 EXPLAIN
。
结果完全一样。我猜两个查询的性能水平相同。
id | 选择类型 | 表 | 分区 | 类型 | possible_keys | 键 | key_len | 参考 | 行 | 过滤 | 额外 |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 简单 | A | NULL | 全部 | NULL | NULL | NULL | NULL | 3 | 100 | 使用临时;使用文件排序 |
1 | 简单 | B | NULL | 全部 | NULL | NULL | NULL | NULL | 4 | 25 | 使用哪里;使用连接缓冲区(哈希连接) |
1 | 简单 | C | NULL | 全部 | NULL | NULL | NULL | NULL | 5 | 20 | 使用哪里;使用连接缓冲区(哈希连接) |
1 | 简单 | D | NULL | 全部 | NULL | NULL | NULL | NULL | 5 | 20 | 使用哪里;使用连接缓冲区(哈希连接) |
答案 0 :(得分:1)
旧版本的 MySQL 用于自动物化派生表(FROM
子句中的子查询)。 “Materialize”意味着 MySQL 运行子查询并将结果保存在一个临时位置(在这种情况下,在进行聚合之前)。
我认为从 5.7 版开始优化器得到了改进(尽管历史可能是错误的)。现在,MySQL 在物化方面更加智能,通常会将子查询与外部查询合并。
因此,更新版本的 MySQL 应该产生相同的执行计划。当然,优化器可能会混淆,优化器可能会决定具体化子查询,这在大多数情况下会减慢查询速度。
您可以在 documentation 中阅读更多相关信息。
您还应该学习使用有意义的表别名,例如 c
代表 customers
。并且,限定所有 列引用,以便清楚列的来源。任意字母可能比根本没有别名更糟糕(假设列都是限定的)。
答案 1 :(得分:0)
第一个查询使用显式子查询首先生成一个中间结果,其中包含每个名字的每个总金额。然后,它在外部查询中聚合 name 以生成您想要的总和。第二个版本不使用任何这样的中间子查询,而是直接在连接的表上聚合。因此,第一个查询可能会在内存和性能方面产生额外的开销,因为 MySQL 必须聚合中间表。
但是,您应该检查两个查询的 EXPLAIN
计划以验证这一点。也有可能 MySQL 足够聪明,可以使用与第二个相同的计划来执行第一个查询。
答案 2 :(得分:0)
请提供SHOW CREATE TABLE
。
听起来好像缺少这些索引:
B: INDEX(customer_id)
C: INDEX(order_id)
D: INDEX(product_id)