这个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 |
+----+-------------+-------+-------+----------------+----------------+---------+----------------------+--------+-------------+
答案 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)
您的问题是o
与d
和t
之间的联接需要在所有情况下发生。也就是说,你需要:
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
,因此每个与您匹配的一个表中的行也选择另一个表中的所有行。