请帮助我理解为什么子查询会影响主查询对索引的使用

时间:2010-12-24 18:14:44

标签: sql mysql

这是没有子查询的主要查询:

SELECT * FROM 
    mytable AS idx
WHERE 
    idx.ID IN (1,2,3) 
    AND idx.P1 = 'galleries';

此表的索引是 id_path(ID,P1)

此时一切正常,使用索引,检查3行,返回2行。如果没有索引,则必须检查9行。

现在,如果我用一个返回完全相同的ID集的子查询替换ID列表, 主查询仍然返回正确的行,但它停止使用索引并检查9行,就像索引从未存在过一样。

SELECT * FROM 
    mytable AS idx
WHERE 
    idx.ID IN (SELECT idxrev.ID FROM mytable AS idxrev WHERE idxrev.ID IN (1,2,3)) 
    AND idx.P1 = 'galleries';

我的问题是,为什么会发生这种情况,我可以做些什么来使主查询像以前一样使用索引。我尝试添加 USE INDEX(id_path),但这使情况变得更糟,进行了全表扫描。

2 个答案:

答案 0 :(得分:1)

SELECT  *
FROM    mytable AS idx
WHERE   idx.ID IN
        (
        SELECT  idxrev.ID
        FROM    mytable AS idxrev
        WHERE   idxrev.ID IN (1,2,3)
        ) 
        AND idx.P1 = 'galleries'

MySQL进行半连接的唯一方法是嵌套循环。

需要占用idx的每一行,然后对idxrev进行检查(使用索引)。

当然,在这种情况下,更好的方法是HASH SEMI JOIN,或者只是将您的查询缩减为原始查询,但MySQL无法满足要求。

要使查询使用索引,只需恢复为原始查询:)

答案 1 :(得分:0)

这是MySQL的一个重大谜团;它不能很好地处理子查询。您可以尝试将 IN 更改为 EXISTS ,这有时会更快。在这个例子中看起来有点傻,因为你仍然使用硬编码列表,但我认为那仅仅是为了测试,对吧?

SELECT * FROM 
    mytable AS idx
WHERE 
    idx.ID EXISTS 
        (SELECT idxrev.ID 
        FROM mytable AS idxrev 
        WHERE 
           idxrev.ID = idx.ID AND
           idxrev.ID IN (1,2,3)) 
    AND idx.P1 = 'galleries';

如果这没有帮助,也许您可​​以运行两个查询。首先,您将所有ID放入逗号分隔列表中(如果您愿意,可以使用GROUP_CONCAT)。然后使用该值构建第二个查询。