MySQL:优化子查询

Posted

技术标签:

【中文标题】MySQL:优化子查询【英文标题】:MySQL: Optimizing Sub-queries 【发布时间】:2021-04-15 15:58:27 【问题描述】:

我有这个查询需要进一步优化,因为它需要太多的 cpu 时间,而且我似乎找不到任何其他方法来更有效地编写它。有没有其他方法可以在不改变表格的情况下编写这个?

SELECT category, b.fruit_name, u.name
, r.count_vote, r.text_c
FROM Fruits b, Customers u
, Categories c
, (SELECT * FROM 
    (SELECT * 
     FROM Reviews 
     ORDER BY fruit_id, count_vote DESC, r_id
   ) a 
   GROUP BY fruit_id
) r
WHERE b.fruit_id = r.fruit_id 
AND u.customer_id = r.customer_id 
AND category = "Fruits";

【问题讨论】:

您的 Categories 表似乎没有与其他表连接/相关 .. 您没有该表的 where 条件 .. 您应该将相关表架构添加一个适当的数据样本和预期结果 不要使用逗号分隔的连接。它们很容易出错,这就是 1992 年在标准 SQL 中引入显式连接的原因。这甚至在 mysql 被发明之前。你应该放弃你的书/老师/教程来教授这种古老的语法。 类别表与其他类别表的关系如何?去水果桌?但为什么水果会属于与“水果”不同的类别呢?另一方面,客户不属于名为“水果”的类别。这看起来有点奇怪。也许您只是过度简化了示例查询? @ThorstenKettner 是的,我确实过度简化了那些不是实际的表和字段,而是一个示例 虽然是真的,但是是什么让您相信您的查询不是最理想的?您是否使用过任何分析技术?参考aman-garg.medium.com/… 如果你知道为什么你的查询是次优的,你也会知道如何解决它。 【参考方案1】:

下次您需要帮助优化查询时,请包括表/索引结构、索引基数的指示和查询的 EXPLAIN 计划。

这里似乎完全没有理由使用单个子查询,更不用说 2. 使用子查询主要会阻止 DBMS 优化器完成其工作。因此,您最大的胜利将来自消除这些子查询。

CROSS JOIN 创建了一个有意的笛卡尔连接 - 它还不清楚该表中的任何属性是否实际需要结果,是否在输出中产生多个相同行,或者只是一个错误。

查询最后一行中的属性category 不属于任何表(但我怀疑它来自类别表)。

此外,您的代码使用没有聚合函数的 GROUP BY 子句。这将产生不确定的结果并且是一个错误。假设您没有利用它的副作用,查询可以重写为:

SELECT 
  category, b.fruit_name, u.name, r.count_vote, r.text_c
FROM Fruits b
JOIN Reviews r 
ON r.fruit_id = b.fruit_id
JOIN Customers u ON u.customer_id = r.customer_id 
ORDER BY r.fruit_id, count_vote DESC, r_id;

由于您的查询中除了连接之外没有谓词,因此除了确保连接谓词上有索引之外,没有进一步优化的余地。

通常情况下,最大的好处可能来自于简单地询问为什么需要在单个查询中检索表中的每一行的问题。

【讨论】:

【参考方案2】:

您的 Categories 表似乎没有与其他表连接/相关,这会在所有行之间产生 catesia 产品

如果您想要不同的结果,请不要使用 group by 但 distint 这样您就可以避免不必要的子查询

而且你不需要在子查询上排序

SELECT category
    , b.fruit_name
    , u.name
    , r.count_vote
    , r.text_c
FROM Fruits b
INNER JOIN  Customers u ON u.customer_id = r.customer_id
INNER JOIN  Categories c ON  ?????? /Your Categories table seems not joined/related  to the others  / 
INNER JOIN  (
    SELECT distinct fruit_id, count_vote, text_c, customer_id
    FROM Reviews    
) r ON  b.fruit_id = r.fruit_id 
WHERE category = "Fruits";

为了更好地阅读,您应该使用显式连接语法并避免基于逗号分隔的表名和 where 条件的旧连接语法

【讨论】:

这给了我一个错误代码:“r.customer_id”上的未知列...【参考方案3】:

这是用显式连接重写的查询:

SELECT 
  category, b.fruit_name, u.name, r.count_vote, r.text_c
FROM Fruits b
JOIN 
(
  SELECT * FROM 
  (
    SELECT * 
    FROM Reviews 
    ORDER BY fruit_id, count_vote DESC, r_id
  ) a 
  GROUP BY fruit_id
) r on r.fruit_id = b.fruit_id
JOIN Customers u ON u.customer_id = r.customer_id 
CROSS JOIN Categories c
WHERE c.category = 'Fruits';

(我这里猜测是category列属于categories表。)

有些部分看起来很可疑:

    为什么要交叉连接 Categories 表,甚至不显示表的列? ORDER BY fruit_id, count_vote DESC, r_id 应该做什么?子查询结果被视为无序集,因此ORDER BY 是多余的,可以被 DBMS 忽略。您想在这里实现什么目标? SELECT * FROM [ revues ] GROUP BY fruit_id 无效。如果您按fruit_id 分组,您希望获得什么count_vote 和什么r.text_c 作为ID?您不要告诉 DBMS(这将类似于 MAX(count_vote) 和 MIN(r.text_c)for instance. MySQL should through an error, but silently replacescount_vote, r.text_cbyANY_VALUE(count_vote), ANY_VALUE(r.text_c)`。这意味着您会得到任意选择的水果值。

因此,您的问题的答案是:不要试图加快速度,而是要修复它。 (也许你想提出一个新的请求来显示查询并解释它应该做什么,以便人们可以帮助你。)

【讨论】:

我的意思是显示 c.category 我只是没有放别名... 好吧,我猜对了。我想说的是,您在不使用它们的情况下加入类别行。选择列表不包含任何类别列。我想交叉连接不是有意的,但我当然不能确定。为什么您的查询中有类别表?它的目的是什么?

以上是关于MySQL:优化子查询的主要内容,如果未能解决你的问题,请参考以下文章

mysql 子查询 优化

那个mysql 子查询和连接查询 一般常用哪个 谁效率高些

mysql中主查询和子查询关系是啥?

使用子查询优化 mysql 查询

mysql优化---in型子查询,exists子查询,from 型子查询

MySQL 查询优化 - 子查询 + 多连接