【问题标题】:Can MySQL use index in a RANGE QUERY with ORDER BY?MySQL 可以在 ORDER BY 的 RANGE QUERY 中使用索引吗?
【发布时间】:2011-05-04 06:35:31
【问题描述】:

我有一个 MySQL 表:

CREATE TABLE mytable (
     id INT NOT NULL AUTO_INCREMENT,
     other_id INT NOT NULL,
     expiration_datetime DATETIME,
     score INT,
     PRIMARY KEY (id)
) 

我需要以以下形式运行查询:

SELECT * FROM mytable
WHERE other_id=1 AND expiration_datetime > NOW() 
ORDER BY score LIMIT 10

如果我将此索引添加到 mytable:

CREATE INDEX order_by_index
ON mytable ( other_id, expiration_datetime, score);

MySQL 能否在上述查询中使用整个order_by_index

好像应该可以,但是根据MySQL的documentation:“即使ORDER BY不完全匹配索引也可以使用索引,只要所有未使用的部分索引和所有额外的 ORDER BY 列是 WHERE 子句中的常量。"

上面的段落似乎表明索引只能用于常量查询,而我的是范围查询。

谁能澄清在这种情况下是否会使用索引?如果没有,有什么办法可以强制使用索引?

谢谢。

【问题讨论】:

    标签: mysql indexing query-optimization sql-order-by


    【解决方案1】:

    MySQL 将使用索引来满足 where 子句,并使用文件排序对结果进行排序。

    它不能使用 order by 的索引,因为您没有将 expire_datetime 与常量进行比较。因此,返回的行在索引中并不总是有一个共同的前缀,因此不能使用索引进行排序。

    例如,考虑一个包含 4 条索引记录的样本集:

    a) [1,'2010-11-03 12:00',1]
    b) [1,'2010-11-03 12:00',3]
    c) [1,'2010-11-03 13:00',2]
    d) [2,'2010-11-03 12:00',1]
    

    如果我在 2010-11-03 11:00 运行您的查询,它将返回索引中不连续的行 a、c、d。因此 MySQL 需要做额外的传递来对结果进行排序,在这种情况下不能使用索引。

    【讨论】:

    • >>它不能使用 order by 的索引,因为您没有将 expire_datetime 与常量进行比较。
    【解决方案2】:

    谁能澄清在这种情况下是否会使用索引?如果没有,有什么办法可以强制使用索引?

    您在过滤条件中有一个范围,而ORDER BY 与该范围不匹配。

    这些条件不能用单个索引来满足。

    要选择创建哪个索引,您需要运行这些查询

    SELECT  COUNT(*)
    FROM    mytable
    WHERE   other_id = 1
            AND (score, id) <
            (
            SELECT  score, id
            FROM    mytable
            WHERE   other_id = 1
                    AND expiration_datetime > NOW() 
            ORDER BY
                    score, id
            LIMIT 10
            )
    

    SELECT  COUNT(*)
    FROM    mytable
    WHERE   other_id = 1
            AND expiration_datetime >= NOW()
    

    并比较它们的输出。

    如果第二个查询产生与第一个查询相同或更多的值,那么您应该在(other_id, score) 上使用索引(并让它在expiration_datetime 上过滤)。

    如果第二个查询产生的值明显少于第一个,您应该在(other_id, expiration_datetime) 上使用索引(并让它在score 上排序)。

    您可能会对这篇文章感兴趣:

    【讨论】:

    • 要检查选择性,您需要检查不同的值,所以不要忘记执行 COUNT DISTINCT。
    • @AllTheCode: COUNT DISTINCT 什么?
    【解决方案3】:

    听起来您已经检查了文档并设置了索引。使用 EXPLAIN 并查看...

    EXPLAIN SELECT * FROM mytable
    WHERE other_id=1 AND expiration_datetime > NOW() 
    ORDER BY score LIMIT 10
    

    【讨论】:

      猜你喜欢
      • 2011-09-29
      • 2012-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多