【问题标题】:Querying with Subquery and Joins- Giving different results on the same table -- MySQL使用子查询和连接查询——在同一张表上给出不同的结果——MySQL
【发布时间】:2021-05-18 14:38:29
【问题描述】:

我正在尝试查询此表:

https://www.w3resource.com/mysql-exercises/subquery-exercises/find-the-names-of-the-employees-who-have-a-manager-who-works-for-a-department-based-in-united-states.php

问题是:

编写查询以查找拥有经理并在美国部门工作的员工的姓名(名字、姓氏)。

除了给出的解决方案之外,我尝试使用 Join 进行查询,另一个使用临时表进行查询。虽然这两个查询返回相同的结果,但子查询显示更多行。这是为什么呢?

使用连接:

select
    employees.MANAGER_ID,
    employees.FIRST_NAME,
    employees.LAST_NAME,
    locations.COUNTRY_ID
from employees
inner join departments on
    employees.DEPARTMENT_ID = departments.DEPARTMENT_ID
inner join locations on
    departments.LOCATION_ID = locations.LOCATION_ID
where locations.COUNTRY_ID = 'US';

使用临时表:

create temporary table deptwithloc
select
    departments.DEPARTMENT_ID,
    departments.LOCATION_ID,
    locations.COUNTRY_ID
from departments
left join locations
    on departments.LOCATION_ID = locations.LOCATION_ID
where locations.COUNTRY_ID = 'US';

子查询:

select
    MANAGER_ID,
    FIRST_NAME,
    LAST_NAME
from employees
where MANAGER_ID 
in (select 
    EMPLOYEE_ID
from employees
where DEPARTMENT_ID

in (select 
    DEPARTMENT_ID 
from departments 
where LOCATION_ID 
in (select LOCATION_ID from locations where COUNTRY_ID='US' )));

请帮助我理解。数据库系统新手。

【问题讨论】:

  • 你为什么使用左连接?您正在以一种可以被内部连接替换的方式使用它。 LEFT JOIN 返回由 NULL 扩展的 INNER JOIN 行 UNION ALL 不匹配的左表行。作为 OUTER JOIN 的一部分,始终知道您想要什么 INNER JOIN。在 LEFT JOIN 之后,需要右 [sic] 表列不为 NULL 的 WHERE、INNER JOIN 或 HAVING 删除任何引入了 NULL 的行,即只留下 INNER JOIN 行,即“将 OUTER JOIN 转换为 INNER JOIN”。 PS 总是切到最小的代码是好的代码扩展到不返回你期望的代码&说出你期望的&为什么。
  • 感谢@philipxy 的回复。你能解释一下你陈述的最后4行吗?我发现很难跟上。对不起,我是新手!!! :)
  • 我不知道你的最后 4 行,因为它取决于屏幕宽度。后记是重新询问代码问题。 minimal reproducible example Re“将 OUTER JOIN 转变为 INNER JOIN”:通过您的查询表达式跟踪最小的代表性数据并查看我的评论如何应用。如果您不明白,请考虑发布问题。但首先要研究问题。这是一个常见问题解答。只需在谷歌上搜索引用的短语可能会出现许多问答。 Left Outer Join doesn't return all rows from my left table? PS 这与您的帖子问题是分开的。

标签: mysql join rdbms


【解决方案1】:

您的第一个查询只是查看在美国设有办事处的所有员工,而不仅仅是经理。您的第二个查询是应用 MANAGER 的部门/位置与美国相关的条件。现在,问题的措辞可能不明确,“谁”在美国工作......它可能是一个在美国工作的人,但是经理在加拿大,但根据他们的 sql 回答,是在美国工作的 MANAGER。

现在,话虽如此,我喜欢对我的查询进行缩进,以查看我正在寻找的所有内容以及我如何从 A - Z 点一路走来。通过缩进,您可以看到表之间的关系。所以让我们反过来看一下。你关心美国以外的经理。所以,员工 -> 部门 -> 位置 -> '美国'

从只考虑经理开始,您不需要所有员工(目前)

select distinct Manager_ID from Employees where Manager_ID > 0

现在,把它带到与“美国”位置相关的部门

select distinct
      e.Manager_ID
   from
      employees e
         JOIN departments d
            on e.department_id = d.department_id
            JOIN locations l
               on d.location_id = l.locationID
               AND l.country_id = 'US'
   where
      e.Manager_id > 0

现在,我们有一个有限的不同经理列表,无论他们分配了多少员工。因此,现在您可以将此查询作为主要查询并获取与他们相关联的任何员工——无论员工实际工作在哪里——例如与经理在不同国家工作的远程员工。

select
      e2.*
   from
      ( select distinct
             e.Manager_ID
          from
             employees e
                JOIN departments d
                   on e.department_id = d.department_id
                   JOIN locations l
                      on d.location_id = l.locationID
                      AND l.country_id = 'US'
          where
             e.Manager_id > 0 ) JustMgrs
         JOIN employees e2
            on JustMgrs.Manager_id = e2.manager_id

因此,第二个查询使用第一个查询作为其记录基础。通过将其包装在 from with (query) 别名中,您可以测试查询的每个单独部分,一旦它工作,现在可以在不完全写入临时表的情况下应用它。我可以在剩下的时间里使用“别名”。所以“JustMgr”只给我一次经理,不管有多少员工。然后它重新加入到“美国”合格列表中的那个经理的员工表中。

这有助于澄清原因吗?此选项是否还可以帮助您了解如何部分剖析问题,先获得一个部分,然后转到下一个。

反馈/澄清

首先,我更正了尝试添加经理姓氏/名字的查询,该部分不正确并且可能导致结果出现偏差,但现在更新后可以了。

尝试澄清您的评论以解释第二部分。这样想,你是否完全理解第一个查询的基础,即只获取位于美国位置的 MANAGERS。通过执行 DISTINCT,无论其下有多少员工,您都只会获得一个经理实例。这就像走进一家公司所有员工的房间,请所有经理举手,然后你会得到几个人。现在你问他们如果在美国办公室的工作能上台就更不用说那些经理了。所以现在你有了你的有限列表。

现在你告诉为其中一位经理工作的所有员工也加入。第二个查询使用唯一经理的整个第一个结果集并仅提取与他们关联的员工。

在进行查询时,您可以编写一个查询并确保一切正常并将其放入“from”子句中。通过将它包装在括号中并放置一个结果表/别名,您现在可以像使用实际表一样使用它。你可以在我包装( the entire manager query ) JustMgrs 时看到这一点。所以现在这是一个别名,我可以使用它来加入员工,以仅获取那些经理下的员工。这有帮助吗?

【讨论】:

  • 感谢@DRapp 如此详尽的回复。但是,请理解,我学习 SQL 才一个月。你能再解释一下第二个代码sn-p吗?我不明白表 e2 和 JustMgrs 的由来。
  • @nonsensical_coder,修改了答案并提供了澄清和查询更正。
猜你喜欢
  • 2014-08-17
  • 2013-07-19
  • 2021-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-05
  • 2018-09-07
相关资源
最近更新 更多