了解涉及左连接和过滤的 MySQL 查询中的执行计划差异

问题描述 投票:0回答:2

我编写了一个 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 加入。这是计划。 Query Execution plan.

如果对执行计划的这种解释是准确的,我正在尝试了解左连接实际上是在哪里实现的。如果连接顺序如所描述的那样,我预计包含布拉德·皮特以外的演员的行将被保留,但事实并非如此。

执行计划是否真正代表了内部发生的事情(如果是,如何理解执行流程?),或者它的显示或解释方式是否存在潜在错误?

mysql left-join sql-execution-plan
2个回答
0
投票

您的查询的简化版本。不需要括号。从电影到演员到人物的每个级别都执行 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) 加入与他相关的演员阵容的人物过滤器开始。这将仅返回与布拉德·皮特相关的每部电影的一条记录。现在我们有一部电影了,再次加入电影演员阵容,但仅基于布拉德合格的电影并获取所有演员阵容,但针对每部电影。现在我们有了演员表,我们可以重新加入到电影中每个演员的人员表中。第二次连接到电影表以提取电影标题。

希望不同的观点可能更符合您的意图?


0
投票

执行计划的图形表示并非不合理,但我可以理解为什么它可能会令人困惑。如果你查看

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
中的行。
    

© www.soinside.com 2019 - 2024. All rights reserved.