【问题标题】:MySQL: how to index an "OR" clauseMySQL:如何索引“OR”子句
【发布时间】:2011-02-19 05:58:02
【问题描述】:

我正在执行以下查询

SELECT COUNT(*)
FROM table
WHERE field1='value' AND (field2 >= 1000 OR field3 >= 2000)

field1 上有一个索引,field2&field3 上有一个复合索引。

我看到 MySQL 总是选择 field1 索引,然后使用其他两个字段进行连接,这非常糟糕,因为它需要连接 146.000 行。

关于如何改进的建议?谢谢

(尝试提出的解决方案后进行编辑)

根据提出的解决方案,我在玩这个时在 Mysql 上看到了这个。

SELECT COUNT(*) FROM (SELECT * FROM table WHERE columnA = value1
UNION SELECT * FROM table WHERE columnB = value2) AS unionTable;

比执行慢很多:

SELECT COUNT(*)
FROM table
WHERE (columnA = value1 AND columnB = value2)
      OR (columnA = value1 AND columnC = value3)

有两个复合索引:

index1 (columnA,columnB)
index2 (columnA,columnC)

有趣的是,要求 Mysql “解释”它在两种情况下总是使用 index1 的查询,并且不使用 index2。

如果我将索引更改为:

index1 (columnB,columnA)
index2 (columnC,columnA)

查询到:

SELECT COUNT(*)
FROM table
WHERE (columnB = value2 AND columnA = value1)
      OR (columnC = value3 AND columnA = value1)

那么这是我发现 Mysql 工作的最快方式。

【问题讨论】:

    标签: mysql indexing composite where


    【解决方案1】:

    分解OR 谓词的典型方法是使用UNION

    请注意,您的示例不适合您的索引。即使你从谓词中省略了field1,你也会有field2 >= 1000 OR field3 >= 2000,它不能使用索引。如果您在 (field1, field2)(field1,field3)field2field3 上分别有索引,您将获得相当快的查询。

    SELECT COUNT(*) FROM
    (SELECT * FROM table WHERE field1 = 'value' AND field2 >= 1000
    UNION
    SELECT * FROM table WHERE field1 = 'value' AND field3 >= 2000) T
    

    请注意,您必须为派生表提供别名,这就是子查询别名为 T 的原因。

    一个真实的例子。列名和表名已匿名化!

    mysql> SELECT COUNT(*) FROM table;
    +----------+
    | COUNT(*) |
    +----------+
    |  3059139 |
    +----------+
    1 row in set (0.00 sec)
    
    mysql> SELECT COUNT(*) FROM table WHERE columnA = value1;
    +----------+
    | COUNT(*) |
    +----------+
    |     1068 |
    +----------+
    1 row in set (0.00 sec)
    
    mysql> SELECT COUNT(*) FROM table WHERE columnB = value2;
    +----------+
    | COUNT(*) |
    +----------+
    |      947 |
    +----------+
    1 row in set (0.00 sec)
    
    mysql> SELECT COUNT(*) FROM table WHERE columnA = value1 OR columnB = value2;
    +----------+
    | COUNT(*) |
    +----------+
    |     1616 |
    +----------+
    1 row in set (9.92 sec)
    
    mysql> SELECT COUNT(*) FROM (SELECT * FROM table WHERE columnA = value1
    UNION SELECT * FROM table WHERE columnB = value2) T;
    +----------+
    | COUNT(*) |
    +----------+
    |     1616 |
    +----------+
    1 row in set (0.17 sec)
    
    mysql> SELECT COUNT(*) FROM (SELECT * FROM table WHERE columnA = value1
    UNION ALL SELECT * FROM table WHERE columnB = value2) T;
    +----------+
    | COUNT(*) |
    +----------+
    |     2015 |
    +----------+
    1 row in set (0.12 sec)
    

    【讨论】:

    • 您遇到了一个错误:您将同时满足这两个条件(field2 >= 1000 field3 >= 2000)的元素计数了两次。 (提示:使用临时表;))
    • UNION 默认为 UNION DISTINCT。重复行作为 UNION 构造的一部分被删除。如果一个人真的要计算两次,一个人会使用“UNION ALL”。你有没有试过我在你自己的一些类似的桌子上建议的陈述?
    • @DavidM nice pwnage
    【解决方案2】:

    我是新来的,所以无法评论其他人的帖子,但这与 David M. 和 soulmerge 的帖子有关。

    临时表不是必需的。 UNION David M. 建议不要重复计算,因为 UNION 意味着一个不同的(即,如果在联合的一半中存在一行,则在另一半中忽略它)。如果你使用 UNION ALL,你会得到两条记录。

    UNION 的默认行为是从结果中删除重复的行。可选的 DISTINCT 关键字除了默认值之外没有任何作用,因为它还指定重复行删除。使用可选的 ALL 关键字,不会发生重复行删除,结果包括所有 SELECT 语句中的所有匹配行。

    http://dev.mysql.com/doc/refman/5.0/en/union.html

    【讨论】:

    • 救了我的命!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-02
    相关资源
    最近更新 更多