MySQL 如何处理查询中的 ORDER BY 和 LIMIT?

Posted

技术标签:

【中文标题】MySQL 如何处理查询中的 ORDER BY 和 LIMIT?【英文标题】:How does MySQL process ORDER BY and LIMIT in a query? 【发布时间】:2011-06-10 04:06:59 【问题描述】:

我的查询如下所示:

SELECT article FROM table1 ORDER BY publish_date LIMIT 20

ORDER BY 是如何工作的?它会排序所有记录,然后获取前 20 条记录,还是获取 20 条记录并按 publish_date 字段排序?

如果是最后一篇,则不能保证您确实获得了最新的 20 篇文章。

【问题讨论】:

请注意,如果某些publish_dates 相等,则按它们排序不会给出确定的结果,这意味着如果您使用LIMIT 进行分页,您最终可能会在不同的页面上得到相同的项目! 注意你应用这些的顺序。如果你先做 LIMIT 再做 ORDER BY,它会抛出一个错误。 ORDER BY 必须是查询中的第一个。 【参考方案1】:

它将首先订购,然后获取前 20 个。数据库还将处理 ORDER BY 之前的 WHERE 子句中的任何内容。

【讨论】:

所以时间是一样的? 错了! LIMIT 打破 ORDER BY。使用 LIMITORDER BY 返回错误结果。 LIMIT 以某种方式重新排序 ORDER BY 返回的结果集 @Green,你错了。阅读此内容以获得解释:dev.mysql.com/doc/refman/5.7/en/limit-optimization.html 当 ORDER BY 列被索引时,当该列中有超过 1 条具有相同值的记录时,它可能会以与没有 LIMIT 的情况下不同的顺序返回记录。 针对此类问题的一个快速解决方案是通过最好具有唯一值来添加一个额外的列,以便当第一个按列排序的值相同时,数据库获得一致的行排序规则多行。【参考方案2】:

LIMIT 子句可用于限制 SELECT 语句返回的行数。 LIMIT 接受一个或两个数字参数,它们都必须是非负整数常量(使用准备好的语句时除外)。

有两个参数,第一个参数指定要返回的第一行的偏移量,第二个参数指定要返回的最大行数。初始行的偏移量为0(不是1):

SELECT * FROM tbl LIMIT 5,10; # Retrieve rows 6-15

要检索从某个偏移量到结果集末尾的所有行,您可以为第二个参数使用一些较大的数字。此语句检索从第 96 行到最后一行的所有行:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

使用一个参数,该值指定从结果集开头返回的行数:

SELECT * FROM tbl LIMIT 5; # Retrieve first 5 rows

换句话说,LIMIT row_count 等价于 LIMIT 0, row_count。

所有详细信息:http://dev.mysql.com/doc/refman/5.0/en/select.html

【讨论】:

不是检索第5-14行吗? @adonis 不,不是。该示例来自MySQL Documentation 第 5 行是第 6 行。 5 行(0 到 4)被忽略。 但是使用没有 ORDER BY 的 LIMIT 可能会产生不一致的结果!不幸的是,必须在应用 LIMIT 之前对整个结果集进行排序,否则 DBMS 可以随意对结果进行任意排序,然后对该结果集进行 OFFSET 和 LIMIT 排序。我读到这可能是由于 DBMS 选择了基于 OFFSET 和 LIMIT 的备用查询计划,因此是任意顺序。 问题是询问限制和订购依据。但答案与这个问题完全无关【参考方案3】:

LIMIT 通常作为最后的操作,所以会先对结果进行排序,然后限制为 20 个。实际上,只要找到前 20 个排序结果,排序就会停止。

【讨论】:

您的第二句话与您的第一句话背道而驰。找到前 20 个结果时,排序不能停止,因为正如您所说,排序将在返回结果之前完成。 MySQL 只能在排序完成后才知道前 20 个结果是什么。 @Tom,如果按索引列排序,实际上它可以。这里解释一下:dev.mysql.com/doc/refman/5.7/en/limit-optimization.html【参考方案4】:

如果有合适的索引,在这种情况下是在publish_date 字段上,那么 MySQL 不需要扫描整个索引来获取请求的 20 条记录——这 20 条记录将在索引的开头找到。但如果没有合适的索引,则需要对表进行全扫描。

这里有一个 2009 年的 MySQL Performance Blog article

【讨论】:

【参考方案5】:

您可以在订单末尾添加[asc]或[desc]以获取最早或最新的记录

例如,这将首先为您提供最新记录

ORDER BY stamp DESC

ORDER BY 之后附加LIMIT 子句

【讨论】:

欢迎来到***。我想你可能误解了这个问题。我相信他们是在询问操​​作顺序而不是“如何排序”。 (但由于这个问题已经在不久前得到回答,所以没有实际意义;)【参考方案6】:

您可以使用此代码 SELECT article FROM table1 ORDER BY publish_date LIMIT 0,10 其中 0 是记录的起始限制和 10 条记录

【讨论】:

不,这不是必需的LIMIT 10LIMIT 0,10 的简写。 是的,LIMIT 0,10 不需要,但您可以像这样限制 10,20 要求【参考方案7】:

正如@James 所说,它将对所有记录进行排序,然后获取前 20 行。

既然如此,你肯定会得到20篇最先发表的文章,较新的文章不会显示。

根据你的情况,我建议你把desc添加到order by publish_date,如果你想要最新的文章,那么最新的文章会在最前面。

如果您需要保持结果按升序排列,并且仍然只想要 10 篇最新文章,您可以让 mysql 对结果进行两次排序。

下面的这个查询将对结果进行降序排序并将结果限制为 10(即括号内的查询)。还是会按照降序排序,我们对此并不满意,所以要求mysql再排序一次。现在我们在最后一行有了最新的结果。

select t.article 
from 
    (select article, publish_date 
     from table1
     order by publish_date desc limit 10) t 

order by t.publish_date asc;

如果你需要所有的列,可以这样做:

select t.* 
from 
    (select * 
     from table1  
     order by publish_date desc limit 10) t 

order by t.publish_date asc;

当我手动编写查询来检查数据库中的各种内容时,我会使用这种技术。我没有在生产环境中使用过,但是现在当我对它进行基准标记时,额外的排序不会影响性能。

【讨论】:

您的额外排序实际上不会对性能产生任何可衡量的影响,因为它仅限于 10 行/项目 :-)。通常,对内存表(子选择正在生成)进行排序非常快且几乎无法测量,除非您有数百万行或 DBMS 将结果集分页到磁盘,因为它不适合内存(在这种情况下根据 DBMS,它也可以中止查询)。【参考方案8】:

可以简化为:

SELECT article FROM table1 ORDER BY publish_date DESC FETCH FIRST 20 ROWS ONLY;

您还可以在 ORDER BY 中添加许多参数,这些参数仅以逗号分隔,例如:ORDER BY publish_date, tab2, tab3 DESC 等...

【讨论】:

以上是关于MySQL 如何处理查询中的 ORDER BY 和 LIMIT?的主要内容,如果未能解决你的问题,请参考以下文章

oracle使用order by排序null值如何处理

oracle使用order by排序null值如何处理?

MySQL 在 VARCHAR 上引用索引前缀时如何使用 ORDER BY?

Coalesce 函数如何处理数据类型

如何处理mysql中的时间戳读取问题

如何处理mysql中的时间戳读取问题