MySQL语法和'OR'性能

时间:2009-03-20 22:17:06

标签: mysql

这个MySQL查询工作得很好

SELECT o.id 
FROM descriptions_programs d, titles_programs t, programs o
WHERE (d.object_id=o.id 
        AND MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) 
        AND d.current=1)
AND   (t.object_id=o.id 
        AND MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) 
        AND t.current=1)

但是如果我用OR替换一个AND,则查询运行很长时间。 (我必须杀了它。):

SELECT o.id 
FROM descriptions_programs d, titles_programs t, programs o
WHERE (d.object_id=o.id 
        AND MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) 
        AND d.current=1)
OR    (t.object_id=o.id 
        AND MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) 
        AND t.current=1)

这是为什么?不要对+瓷器的简单性感到不知所措。我只是为了调试而简化了这个。此外,如果我只运行一个MATCH AGAINST测试,它运行正常,所以两者都可以自己。我觉得我无意中通过USING OR引起了巨大的加入,但我只是不明白。我以前在一个有效的两个子选择的UNION上使用了一个IN测试,但这也应该有效。正确?

每个bobince的请求

更新。它不是超级慢,但在~500ms时,它的速度不如使用UNION discussed here那么快。

mysql> explain SELECT o.id
    -> FROM programs o
    -> JOIN titles_programs t ON t.object_id=o.id
    -> JOIN descriptions_programs d ON d.object_id=o.id
    -> WHERE MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) AND d.current=1
    -> OR MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) AND t.current=1
    -> ;
+----+-------------+-------+-------+

----------------+----------------+---------+----------------------+--------+-------------+
| id | select_type | table | type  | possible_keys  | key            | key_len | ref                  | rows   | Extra       |
+----+-------------+-------+-------+----------------+----------------+---------+----------------------+--------+-------------+
|  1 | SIMPLE      | o     | index | PRIMARY        | PRIMARY        | 4       | NULL                 | 148666 | Using index | 
|  1 | SIMPLE      | d     | ref   | object_current | object_current | 4       | haystack.o.id        |      1 |             | 
|  1 | SIMPLE      | t     | ref   | object_current | object_current | 4       | haystack.d.object_id |      1 | Using where | 
+----+-------------+-------+-------+----------------+----------------+---------+----------------------+--------+-------------+

2 个答案:

答案 0 :(得分:2)

杰森的回答很明显。另外,我尝试使用更现代的ANSI连接语法来减轻WHERE子句的负担,从而减轻那里的混乱:

SELECT o.id
FROM programs o
JOIN titles_programs t ON t.object_id=o.id
JOIN descriptions_programs d ON d.object_id=o.id
WHERE MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) AND d.current=1
OR MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) AND t.current=1

这将阻止无意中的交叉连接导致组合爆炸;除非数据库非常庞大,否则我希望它能在合理的时间内运行。

如果没有,你可以发布上述EXPLAIN SELECT的结果吗?据推测,没有使用全文索引中的一个或两个。我当然可以想象查询优化器无法使用第二个全文索引,通过尝试“填写”与第一个全文查询不匹配的行而不是直接转到索引,或者其他东西。

通常,当您想要组合两列的全文索引时,可以在两列上创建一个索引。在任何情况下,这都会快得多。但是,这意味着您必须将标题和描述放在同一个表中。这可能不是那么困难:因为全文仅适用于MyISAM表(并且您通常不希望MyISAM表中的规范数据),您可以将数据的最终副本保存在正确规范化的InnoDB表中,并附加一个MyISAM表仅含有剥离和茎秆的搜索诱饵。

如果这些都不好......好吧,我想我会回到你提到的UNIONing,加上一个应用程序级过滤器来删除重复的ID。

答案 1 :(得分:1)

您的问题是odt之间的联接需要在所有情况下发生。也就是说,你需要:

SELECT o.id 
FROM descriptions_programs d, titles_programs t, programs o
WHERE d.object_id=o.id AND t.object_id=o.id AND
(
        MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) 
        AND d.current=1
) OR (  MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) 
        AND t.current=1
)

为什么呢?因为在您的第一个查询中,您可以忽略这些括号 - 所有内容都是AND,并且表格连接正常。在你的第二个查询中,那不是真的。

考虑数据库实际在做什么:它将“t中的所有行”与“d中的所有行”交叉,因此t*d行。通常,您使用连接(就像我一样)将其限制为有效行的线性列表。

但是在您的OR查询中,您允许 行匹配o而非两者匹配o,因此每个与您匹配的一个表中的行也选择另一个表中的所有行