我试图在mysql中优化NOT IN子句:有些我最终在以下查询中结束:
SELECT @i:=(SELECT correct_option_word_id FROM sent_question WHERE msisdn='abc');
SELECT * FROM word WHERE @i IS NULL OR word_id NOT IN (@i);
sent_question
表和word
表之间没有关系。而且我也不能在correct_option_word_id
上放置索引。
有人可以解释一下,这种方法是否会优化查询?
UPDATE :如上所述here,两种方法:NOT IN和LEFT JOIN / IS NULL几乎同样有效。这就是为什么我不想使用LEFT JOIN / IS NULL方法。
更新2 : 解释原始查询的结果:
EXPLAIN SELECT * FROM word WHERE word_id NOT IN (SELECT correct_option_word_id FROM sent_question WHERE msisdn='abc');
+----+--------------------+---------------+------+-------------------------+-------------------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+---------------+------+-------------------------+-------------------------+---------+-------+------+-------------+
| 1 | PRIMARY | word | ALL | NULL | NULL | NULL | NULL | 10 | Using where |
| 2 | DEPENDENT SUBQUERY | sent_question | ref | fk_question_subscriber1 | fk_question_subscriber1 | 48 | const | 1 | Using where |
+----+--------------------+---------------+------+-------------------------+-------------------------+---------+-------+------+-------------+
答案 0 :(得分:1)
你是对的,NOT IN
和LEFT JOIN/IS NULL
方法都是equally efficient,但不幸的是,没有更快的选项,只有更慢的选项(NOT EXISTS
)
这是您的查询,简化:
SELECT *
FROM word
WHERE
word_id NOT IN (SELECT correct_option_word_id FROM sent_question WHERE msisdn='abc')
如您所知,MySQL将首先执行子查询并使用返回的NOT IN
子句结果集。然后,它将扫描word
中的所有行,以查看每行的列表中是否有word_id
。
不幸的是,对于这种情况,索引是包容性的,而非排他性的。它们对NOT
个查询没有帮助。 word
上的covering index可能仍然可能用于避免访问实际的表,并提供一些IO好处,但它不会用于传统的“查找”意义。但是,由于您要返回word
表中的所有列,因此拥有如此大的索引可能不太可行。
此处将使用的最重要的索引是子查询的sent_question.msisdn
索引。确保已定义该索引。 <{1}}上的多列“覆盖”索引最好。
如果您分享您的设计,我们可能会提供一些优化设计解决方案。
答案 1 :(得分:0)
我怀疑它会起作用。
尝试
SELECT *
FROM word AS w
LEFT JOIN sent_question AS sq
ON w.word_id = sq.correct_option_word_id AND sq.msisdn='abc'
WHERE sq.correct_option_word_id IS NULL
答案 2 :(得分:0)
尝试这个简单的查询
SELECT
sent_question.*,
word.word_id AS foundWord
FROM sent_question
LEFT JOIN word
ON word.word_id = sent_question.correct_option_word_id
WHERE sent_question.msisdn='abc'
// GROUP BY sent_question.correct_option_word_id // This shouldn't be needed but included for completion
HAVING foundWord IS NULL