【问题标题】:Strange behavior in mySQL 8 with order by, limit and parenthesesmySQL 8 中带有 order by、limit 和括号的奇怪行为
【发布时间】:2019-12-05 20:33:19
【问题描述】:

假设我有一个包含以下值的表

我执行以下查询并获得附加结果

SELECT * FROM testvalues ORDER BY textval;

到目前为止一切都很好。我现在将添加一个 LIMIT 语句。

SELECT * FROM testvalues ORDER BY textval LIMIT 3;

一切看起来都还不错。但是请注意当我在查询中添加括号时会发生什么。

(SELECT * FROM testvalues ORDER BY textval) LIMIT 3;

关于括号为什么会导致顺序不正确的任何想法。似乎使用括号仅用于确保在应用限制之前完成内部查询并因此对其进行排序。但似乎它会导致应用限制并完全丢弃排序。这是一个错误吗?一个已知的错误?我没有看到任何报告。

这是因为最近从 MySQL5.5 升级到 MySQL8 以及它们如何处理包含 order by 和/或 limit 操作的 UNION 查询。无论它们是否是联合的一部分,都可能使用了太多括号。我们的一些 SQL 是生成的,因此在某些情况下,引擎可能会自动添加括号,以预测结果可能会用于更大的联合查询。反正。我偏离了核心问题。

编辑/更新:

正如 nbk 和 nick 指出的那样,括号的使用,无论在这种情况下看起来多么无害,都会导致 MySQL 将其作为子查询来处理。但是,以下查询实际上有效,这似乎与给出的解释相反。

SELECT root.* FROM (SELECT * FROM testvalues ORDER BY textval) AS root LIMIT 3;

现在这是子查询的完整形式,但是看起来它确实在子查询中应用了排序,然后限制了结果。

编辑 2:

跟进尼克的回应。是的,这是一个实际查询的示例,为了清楚起见已对其进行了简化,并使用模拟数据来防止泄露敏感的真实数据。

我想我对原始问题的回答基本上是“MySQL 已决定将其优化为子查询,因此忽略 ORDER BY”。这让我很头疼。为什么以下查询在功能上看起来相同时却以不同方式处理。

(SELECT * FROM testvalues ORDER BY textval) LIMIT 3;

SELECT root.* FROM (SELECT * FROM testvalues ORDER BY textval) AS root LIMIT 3;

【问题讨论】:

    标签: mysql sql-order-by limit


    【解决方案1】:

    子查询/派生表中的排序不会影响最终输出;这些行由外层ORDER BY 子句排序。由于您没有,因此最后一个查询中结果的顺序是不确定的。如果写成

    就可以正常工作
    (SELECT * FROM testvalues) ORDER BY textval LIMIT 3;
    

    MariaDB knowledge base 中有此行​​为的描述。

    请注意,当您尝试“真实”子查询时,即

    SELECT root.* FROM (SELECT * FROM testvalues ORDER BY textval) AS root LIMIT 3
    

    优化器将传播ORDER BY 子句,因为它满足manual 中描述的条件:

    优化器在派生表中传播 ORDER BY 子句或 如果这些条件都满足,请查看对外部查询块的引用 真的:

    • 外部查询未分组或聚合。

    • 外部查询未指定 DISTINCT、HAVING 或 ORDER BY。

    • 外部查询将此派生表或视图引用作为 FROM 子句中的唯一来源。

    当这些条件不成立时:

    否则,优化器将忽略 ORDER BY 子句。

    【讨论】:

    • 编辑/更新的附加信息似乎与给出的解释相矛盾。
    • @BrianMueller 但确实代表了您使用它的方式?该查询可以优化为子查询...
    • 如果您询问原始问题是否代表实际用法 - 是的(该表是作为示例生成的,以防止泄露敏感数据,但它是真实查询的准确示例)。处理该问题的开发人员发现,如果他将其变成“真正的子查询”,则结果会按预期工作。当我从这里提出答案时,他用他的例子反驳说,当他将其更改为“真正的子查询”时,它似乎与给定的规则相矛盾。此外,确保我们可以将其优化为单个查询 - 但与 prod 中现在存在的代码相关的问题。
    • @BrianMueller 查看我的编辑。该手册说,对于您发布的“真正的子查询”,优化器会将ORDER BY 子句传播到外部查询。想必你现有的查询不满足这三个条件?
    • 所以,核心原因是 A) MySQL 确定所写的查询是基于括号的 SUBQUERY 但是 B) 它不会将 ORDER BY 传播到外部查询中,因为它不符合给出的所有 3 条规则。此外,它不符合的规则很可能是第三条规则,因为它无法推断出 FROM(尽管它显然构成了一个),不确定派生表是 from 子句中的唯一来源。感谢您提供拼图的所有部分。
    【解决方案2】:

    正如这里所说的数百次,你的括号使它成为一个子查询,因此是一个表:

    根据 SQL 标准的“表” - 一组无序的行。中的行 表(或 FROM 子句中的子查询)不出现在任何 具体顺序。这就是优化器可以忽略 ORDER BY 的原因 您指定的子句。

    来源

    https://mariadb.com/kb/en/library/why-is-order-by-in-a-from-subquery-ignored/

    但如果当然也适用于 mysql 也是有效的。

    我个人认为您的问题已得到解答。

    但是对于您的第二个问题。

    您应该始终查看 mysql 的解释功能,因为您可以看到它在这种情况下使用 filesort

    CREATE TABLE `testvalues` (
    numval INT ,
    `textval` VARchar(2) );
    
    INSERT INTO `testvalues` (numval,textval)
    VALUES (1,'a'),(6,'f'),(7,'g'),(4,'d'),(2,'b'),(5,'e'),(8,'h'),(3,'c');
    
    select * From testvalues;
    
    数字 |文本值 -----: | :------ 1 |一种 6 | F 7 | G 4 | d 2 | b 5 | e 8 | H 3 | C
    EXPLAIN SELECT root.* FROM (SELECT * FROM testvalues ORDER BY textval) AS root LIMIT 3;
    
    编号 |选择类型 |表|隔断 |类型 |可能的键 |关键 | key_len |参考 |行 |过滤 |额外的 -: | :------------ | :--------- | :--------- | :--- | :------------ | :--- | :-------- | :--- | ---: | --------: | :------------- 1 |简单 |测试值 | |全部 | | | | | 8 | 100.00 |使用文件排序

    db小提琴here

    【讨论】:

    • 编辑/更新的附加信息似乎与给出的解释相矛盾。
    • 虽然我没有在这里发布它,但我已经进行了解释,并且知道一个实际上正在执行排序,而另一个没有。我还运行了分析,可以看到一个包含“排序结果”和“创建排序索引”,而另一个没有。然而,这不是有用的信息,因为已经很清楚一个正在排序而另一个没有。真正的答案和解决方案的关键与子查询的关系似乎不大,而与 MySQL 引擎在某些情况下而不是在其他情况下推断信息的能力有关。
    猜你喜欢
    • 2014-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多