使用临时变量优化mysql NOT IN查询

时间:2012-02-21 14:00:27

标签: mysql

我试图在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 |
+----+--------------------+---------------+------+-------------------------+-------------------------+---------+-------+------+-------------+

3 个答案:

答案 0 :(得分:1)

你是对的,NOT INLEFT 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