我编写了一个 SQL 查询来获取所有电影,如果存在,则包含他担任演员的电影的名字“Brad Pitt”。查询如下:
select m.title, p_cast.person_name
From movie m left join (
movie_cast m_cast join person p_cast on m_cast.person_id = p_cast.person_id and
p_cast.person_name = 'Brad Pitt'
) on m.movie_id = m_cast.movie_id
我期望 MySQL Workbench 中的执行计划显示数据库左连接 movie_cast 和 person 之间的连接结果。然而,该计划建议采用不同的顺序:它似乎首先将 movie 与 movie_cast 加入,然后才与应用了“Brad Pitt”过滤器的 person 加入。这是计划。 .
如果对执行计划的这种解释是准确的,我正在尝试了解左连接实际上是在哪里实现的。如果连接顺序如所描述的那样,我预计包含布拉德·皮特以外的演员的行将被保留,但事实并非如此。
执行计划是否真正代表了内部发生的事情(如果是,如何理解执行流程?),或者它的显示或解释方式是否存在潜在错误?
您的查询的简化版本。不需要括号。从电影到演员到人物的每个级别都执行 LEFT JOIN 条件
select
m.title,
p_cast.person_name
From
movie m
left join movie_cast m_cast
on m.movie_id = m_cast.movie_id
join person p_cast
on m_cast.person_id = p_cast.person_id
and p_cast.person_name = 'Brad Pitt'
现在,这个结果可能会超出您的预期,因为演员的左连接将为每个演员获得 1 条记录,但前提是符合“Brad Pitt”资格的人的左连接将提取实际姓名。所以你会得到一个列表,该列表具有相同的移动显示和一堆空值,直到布拉德·皮特为止。
现在,让我们添加一些内容来查看您的意图与查询的内容。所以让我们看看这是否更有意义......你真的在问吗......给我一份布拉德·皮特主演的所有电影的清单。然后,从中返回所述电影的所有演员。如果是这种情况,那么我会首先查询与布拉德·皮特相关的所有电影,然后再次加入其他演员阵容。
select
m.title,
castName.Person_Name
from
person p
join movie_cast mcBrad
p.person_id = mcBrad.person_id
join movie_cast AllCast
on mcBrad.movie_id = AllCast.movie_id
JOIN person castName
on AllCast.person_id = castName.person_id
JOIN movie m
on AllCast.movie_id = m.movie_id
where
p.person_name = 'Brad Pitt'
因此,我们从布拉德·皮特 (Brad Pitt) 加入与他相关的演员阵容的人物过滤器开始。这将仅返回与布拉德·皮特相关的每部电影的一条记录。现在我们有一部电影了,再次加入电影演员阵容,但仅基于布拉德合格的电影并获取所有演员阵容,但针对每部电影。现在我们有了演员表,我们可以重新加入到电影中每个演员的人员表中。第二次连接到电影表以提取电影标题。
希望不同的观点可能更符合您的意图?
执行计划的图形表示并非不合理,但我可以理解为什么它可能会令人困惑。如果你查看
EXPLAIN ANALYZE
的输出,你会看到类似这样的内容:
-> Nested loop left join
-> Table scan on m
-> Nested loop inner join
-> Covering index lookup on m_cast using PRIMARY (movie_id=m.movie_id)
-> Filter: (p_cast.person_name = 'Brad Pitt')
-> Single-row index lookup on p_cast using PRIMARY (person_id=m_cast.person_id)
在这里我们可以看到
Table scan on m
和Nested loop inner join
的结果之间的左连接。对于 Nested loop inner join
返回的每一行,以 Table scan on m
作为内连接的输入,在循环中执行 m.movie_id
。
更重要的是,这突出显示了
p_cast.person_name
上缺少索引,并且它返回给定 m_cast
中的所有行,然后从 movie_id
检索行并过滤 p_cast
。如果您在 person_name
上添加索引,您可能会看到类似这样的内容:
person_name
这里我们可以看到
-> Nested loop left join
-> Table scan on m
-> Nested loop inner join
-> Covering index lookup on p_cast using idx_name (person_name='Brad Pitt')
-> Single-row covering index lookup on m_cast using PRIMARY (movie_id=m.movie_id, person_id=p_cast.person_id)
和
movie_id
都用于查找person_id
中的行。