这是没有子查询的主要查询:
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),但这使情况变得更糟,进行了全表扫描。
答案 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)。然后使用该值构建第二个查询。