【问题标题】:How to avoid a 'where' clause affecting row ordering?如何避免影响行排序的“where”子句?
【发布时间】:2017-10-11 07:56:02
【问题描述】:

我有一个案例,我从另一个 select 执行 select,如果我添加 where 子句,则返回行的顺序会更改。

例子:

SELECT t.id
FROM (
       SELECT t.id
       FROM table1 t
       ORDER BY
         t.viewsTotal ASC
       LIMIT 20
       OFFSET 0
     ) base
  INNER JOIN table1 t ON base.id = t.id
  LEFT JOIN table2 t2 ON t2.id = t1.secondTableId
# WHERE t2.someBoolColumn = FALSE
;

现在,内部select 和外部select 的顺序相同,但如果我取消注释where 条件,外部select 将改变顺序。

如何防止这种情况发生?

让我们假设给定示例如下:

  1. 我一个也做不了select
  2. 我不知道在执行外部select 时对内部select 应用了什么顺序。所以,如果我从一个连接的表中订购,我不会知道我需要在这里加入它。

关于我的用例的更多信息

有一个提供内部选择的查询构建器,它可以通过连接到该内部选择的第三个表应用顺序,如果我想应用相同的顺序,我需要知道哪些表被连接,并且对于这个糟糕的查询生成器,我没有这些知识

【问题讨论】:

  • 在外部SELECT 上应用ORDER BY
  • @RemyLebeau 这是有问题的,因为我不知道内部选择的应用顺序,如果通过任何连接进行排序,我也必须在外部选择上加入这些表,但我没有在外部选择阶段了解它
  • 没有“行顺序”,除非您使用 ORDER BY 子句。它是未定义的。任何事情都会影响它。
  • @Vardius:我不明白你在说什么。内部select 以某种方式对行进行排序,因此它知道要返回什么。外部select 执行的任何连接都不会更改该顺序。内部select 只返回一些行,而外部select 如果取消注释where,则将其中的一些过滤掉。它还应该根据需要对剩余的行进行排序。所以没有理由不在外部select 上使用ORDER BY,不管内部select 如何排序初始行。
  • @Vardius:外部select 无法知道内部select 的顺序,除非您对所有内容进行硬编码,因此selects 都使用相同的排序标准。你似乎不知道。所以外部select 需要它自己的ORDER BY 来订购它想要的最终结果,不管内部select 如何订购。

标签: mysql sql sql-order-by query-builder


【解决方案1】:

tl;dr 如果您希望结果集中有特定的顺序,请使用ORDER BY

没有ORDER BY 子句的任何RDMS 服务器的结果集中的行排序在形式上是不可预测的。 不可预测就像随机的,只是更糟。随机排序意味着每次运行查询时都会以不同的顺序获取行。如果存在真正的随机排序,那么当您对排序的假设失败时,简单的单元测试将难以通过。

不可预测意味着你会以相同的顺序得到它们,直到你没有。这意味着你的单元测试将通过,你的系统测试将通过,你的系统将失败六个月投入生产,如果它取决于结果集排序。

为什么会这样?服务器的查询计划器可以随意使用任何算法来满足您给它的查询。这些算法对不同类型的表和不同大小的表的工作方式不同。如果您不通过指定结果集排序来限制查询计划器,它可能会选择一些算法,该算法给出的排序对您的程序员来说似乎很奇怪。

从字面上看,查询规划器内置了价值数千程序员年的优化。

对于习惯了各种编程语言所鼓励的过程式思维方式的人来说,有时很难将思维方式切换到 SQL 使用的声明性/描述性模式。使用 SQL(至少是干净的 SQL,没有 SELECT @a := @a+1 和其他黑客之类的东西),您只是在描述您想要的结果集。服务器生成符合您的规范的结果。

【讨论】:

  • 如果您不指定订单,数据库服务器不仅可以选择意外订单,还可以将订单从一个查询更改为下一个查询。 (可能随着表的增长/缩小,也可能只是因为服务器改变了主意。)
  • 对。查询计划可以依赖于任何东西。从以前的查询中缓存的数据?是的。 (错误)估计索引基数?是的。磁盘驱动器上读磁头的当前位置?是的(但我认为 MySQL 没那么聪明)。
【解决方案2】:

我建议您不要依赖生成我的 SQL 的隐式排序(因为根据 Bohemian 的评论没有隐式排序)。相反,您应该使用 ORDER BY 语句并在查询中选择您应该对结果进行排序的列之一。这样,您可以确保无论 WHERE 子句如何,结果始终以相同的方式呈现。

【讨论】:

  • 这个答案是不正确的,或者充其量是误导,因为没有隐式排序。你不能“依赖”不存在的东西。
猜你喜欢
  • 1970-01-01
  • 2015-06-04
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
  • 2016-07-09
  • 1970-01-01
  • 1970-01-01
  • 2018-07-15
相关资源
最近更新 更多