我们可以优化这个MySQL SELECT查询(2个嵌套的SELECT和2个INNER JOIN)

时间:2011-11-01 19:13:55

标签: mysql sql optimization query-optimization

SELECT id,name,color
FROM animals a
INNER JOIN animal_tags atags ON atags.animal_id = a.id
WHERE atags.tag_id = 70 OR a.id IN (
SELECT a2.id FROM animals a2
WHERE a2.inherit_tags = 1 AND a2.species IN
(SELECT species.id FROM species
INNER JOIN species_tags stags ON stags.species_id = species.id
WHERE stags.tag_id = 70))

基本上,我正在寻找任何与tag_id 70(比如说“Fur”)有关联的动物和属于与tag_id 70关联的物种的任何动物,并且该动物应该继承其标签物种。

我的另一个选择是基本上删除继承标签片段,只是总是为动物定义标签......但不知何故,这似乎没有很正常化。 (请注意,* _tags表只包含关系,每个tag_id都与“tags”表关联,其中存储了标记详细信息。这对此查询并不重要,只是为了向您显示我的表模式已完全规范化。)

2 个答案:

答案 0 :(得分:2)

我相信这个查询应该是等价的。

SELECT id, name, color
    FROM animals a
        INNER JOIN animal_tags atags
            ON a.id = atags.animal_id
        LEFT JOIN species s
            INNER JOIN species_tags stags
                ON s.id = stags.species_id
                    AND stags.tag_id = 70
            ON a.species = s.id
                AND a.inherit_tags = 1
    WHERE atags.tag_id = 70
        OR s.id IS NOT NULL

答案 1 :(得分:2)

由于您实际上不需要结果集中的标记表中的值,因此不需要完整的连接。通常,查询优化器可以使用exists更有效地处理半连接。

此外,当物种表不必要时,您将加入中间表(动物 - 物种 - 种类标签),因为您所有三个表都加入了species_id。你可以绕过物种,从动物到物种标签。

最后,由于您已经在使用animals表并且正在评估OR条件,因此无需将该表连接到自身。这应该证明更有效。我还要确保你在tag_id,species_id等上有索引。

SELECT id, name, color
FROM animals a
WHERE EXISTS(SELECT 1 
               FROM animal_tags atags 
               where atags.tag_id = 70 
                   and atags.animal_id = a.id) 
    OR (a.inherit_tags = 1 
        AND EXISTS( SELECT 1 from species_tags stags
                   WHERE stags.species_id = a.species 
                        AND stags.tag_id = 70
                 )
        )