简体   繁体   English

优化MySQL中的ORDER BY LIMIT查询

[英]Optimizing ORDER BY LIMIT queries in MySQL

In my web app I made an internal messaging system. 在我的网络应用程序中,我制作了一个内部邮件系 I want to place a 'previous' and a 'next' link on each page (where the user viewing the message). 我想在每个页面上放置一个“上一个”和一个“下一个”链接(用户查看该消息的位置)。

In order to get the next and previous id I execute two queries: 为了获得下一个和上一个id,我执行两个查询:

For the previous one: 对于前一个:

SELECT id FROM pages WHERE (id<$requestedPageId) ORDER BY id DESC LIMIT 1

And for the next one: 对于下一个:

SELECT id FROM pages WHERE (id>$requestedPageId) ORDER BY id LIMIT 1

EXPLAIN says the the query type is "range" and the rows column says it would examine all rows that has smaller or bigger id than the page's id (a big number). EXPLAIN表示查询类型为“range”,而rows列表示它将检查id大于或大于页面id(大数字)的所有行。 The Extra row says "Using where". 额外的行说“在哪里使用”。

It seems MySQL ignores that I want only one row. 似乎MySQL忽略了我只想要一行。 Isn't MySQL smart enough to optimize this kind of query so it would find the row for the page and search for the first matching row back/forth? MySQL不够智能,无法优化这种查询,因此它会找到页面的行并来回搜索第一个匹配的行吗?

Is there a better approach to get the next and previous page's id? 是否有更好的方法来获取下一页和上一页的ID?

Additional notes: 补充笔记:

  • This problem seems to exist on every ORDER BY LIMIT type queries (eg.: when I split a long list to multiple pages.). 这个问题似乎存在于每个ORDER BY LIMIT类型的查询中(例如:当我将长列表拆分为多个页面时)。
  • Where clause is not this simple (I want to let the user access the next/previous page he has permission to access. No joins though.) Where子句不是这么简单(我想让用户访问他有权访问的下一页/上一页。但是没有加入。)
  • All columns appear in WHERE are indexed (id is the primary key) WHERE中的所有列都显示为索引(id是主键)
  • variables are protected against injection. 变量可以防止注入。

EDIT1: EDIT1:

So the query I'm using currently: 所以我目前正在使用的查询:

SELECT id
FROM reports
WHERE (id<$requestedPageId) AND ((isPublic=1) OR (recipientId=$recipient))
ORDER BY id DESC
LIMIT 1

Or when I re-factor it as the answer said: 或者当我重新考虑它时,答案说:

SELECT MAX(id)
FROM reports
WHERE (id<$requestedPageId) AND ((isPublic=1) OR (recipientId=$recipient))

For the previous 对于以前

SELECT MAX(id) FROM pages WHERE id<$requestPageId

And for the next 并为下一个

SELECT MIN(id) FROM pages WHERE id>$requestedPageId

The database is behaving as expected. 数据库的行为符合预期。 Your query is a range query because of the less-than symbol (id < $requestedPageId). 由于小于号码(id <$ requestedPageId),您的查询是范围查询。 The OR statement makes it harder to use a single index to find the results. OR语句使得使用单个索引来查找结果变得更加困难。 And, sorting the results means it has to get all matching rows to perform the sort, even though you only want 1 row. 并且,对结果进行排序意味着它必须获取所有匹配的行才能执行排序,即使您只需要1行。

You're not going to be able to make this a "const" type query, but you may be able to optimize it using indexes, sub-queries, and/or union statements. 您无法将其设置为“const”类型查询,但您可以使用索引,子查询和/或联合语句对其进行优化。

Here is one query to rule them all. 这是一个统治它们的查询。 I'm not saying this is the best solution, but just one way of approaching the problem. 我不是说这是最好的解决方案,而只是解决问题的一种方法。 To start, this query will work better if you create two indexes, one on recipientId and another on isPublic. 首先,如果您创建两个索引,此查询将更好地工作,一个在recipientId上,另一个在isPublic上。

SELECT
GREATEST(
  ( SELECT MAX( id ) FROM reports 
    WHERE id < $requestedPageId AND recipientId = $recipient ),
  ( SELECT MAX( id ) FROM reports 
    WHERE id < $requestedPageId AND isPublic = 1 )
) AS prev_id
LEAST(
  ( SELECT MIN( id ) FROM reports 
    WHERE id > $requestedPageId AND recipientId = $recipient ),
  ( SELECT MIN( id ) FROM reports 
    WHERE id > $requestedPageId AND isPublic = 1 )
) AS next_id

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM