MySQL:如何索引"OR"子句

Jos*_*éMi 21 mysql indexing composite where

我正在执行以下查询

SELECT COUNT(*)
FROM table
WHERE field1='value' AND (field2 >= 1000 OR field3 >= 2000)
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

比执行要慢很多:

SELECT COUNT(*)
FROM table
WHERE (columnA = value1 AND columnB = value2)
      OR (columnA = value1 AND columnC = value3)
Run Code Online (Sandbox Code Playgroud)

有两个合成索引:

index1 (columnA,columnB)
index2 (columnA,columnC)
Run Code Online (Sandbox Code Playgroud)

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

如果我将索引更改为:

index1 (columnB,columnA)
index2 (columnC,columnA)
Run Code Online (Sandbox Code Playgroud)

并查询:

SELECT COUNT(*)
FROM table
WHERE (columnB = value2 AND columnA = value1)
      OR (columnC = value3 AND columnA = value1)
Run Code Online (Sandbox Code Playgroud)

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

Dav*_*d M 28

打破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
Run Code Online (Sandbox Code Playgroud)

请注意,您必须为派生表提供别名,这就是子查询为别名的原因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)
Run Code Online (Sandbox Code Playgroud)

  • UNION默认为UNION DISTINCT.作为UNION构造的一部分,将删除重复的行.如果真的要算两次,就会使用'UNION ALL'.你有没有试过我在你自己的类似桌子上建议的陈述? (8认同)

小智 6

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

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

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

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