28

我正在执行以下查询

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

字段 1 上有一个索引,字段 2 和字段 3 上有另一个索引。

我看到 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 工作的最快方式。

4

2 回答 2

36

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

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

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)
于 2010-05-13T19:46:35.723 回答
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

于 2010-05-13T20:13:12.890 回答