SQL查询以查找具有部门名称的位置以及没有部门的位置

Posted

技术标签:

【中文标题】SQL查询以查找具有部门名称的位置以及没有部门的位置【英文标题】:SQL query to find locations with department names as well as locations without departments 【发布时间】:2016-11-01 14:10:09 【问题描述】:

我遇到了以下关于 Oracle 附带的著名 HR 架构的 SQL 问题:

编写一个 SQL 查询来查找每个位置的所有位置和部门,以及没有部门的位置。不要使用外连接。

OUTER JOIN 很容易:

SELECT department_name, city 
FROM locations
LEFT OUTER JOIN departments USING (location_id);

我得到 43 个结果。我试过这个:

SELECT department_name, city 
FROM locations l, departments d
WHERE l.location_id = d.location_id OR 
      l.location_id NOT IN (SELECT DISTINCT location_id FROM departments);

但我只得到 27 行,就好像我这样做:

SELECT department_name, city  
FROM locations 
JOIN departments USING (location_id);

为什么OR 不起作用?谢谢!


编辑:

正如@mathguy 所指出的,我在departments 表的location_id 列中有一个NULL 部门。这就是NOT IN 不返回任何行的原因。否则我会有更多的行从departments 表中寻找位置ID。

【问题讨论】:

可能错误的查询是第一个,而不是第二个。左外连接应该使用 ON,而不是 USING @Massimo,为什么?它确实产生了预期的结果。 docs.oracle.com/database/121/SQLRF/… @Massimo 进一步阅读“由于此示例中的列名在连接中的两个表中相同,因此您还可以通过指定连接语法的 USING 子句来使用公共列功能。” 你说得对,我不知道...我链接到“传统”sql 【参考方案1】:

从内部连接中获取city, department_name,然后像这样获取union all

select city, department_name 
  from <inner join>
union all
select city, NULL
  from locations
 where location_id not in (select location_id from departments);

union all 的第二个分店会给你 16 个没有部门的城市。

注意:如果location_id 列中有NULL 的部门,NOT IN 条件将不起作用。如果可能,条件可以更改为not in (select location_id from departments where location_id is not null)

【讨论】:

嗯,select city, NULL from locations where location_id not in (select location_id from departments); 返回 0 行。 一定是同一个问题:你必须有一个location_id为NULL的部门。这(我的意思是你的副本)不是标准的 HR 模式。 是的。我愿意。非常感谢! 或者 HR 架构在不同版本的 Oracle(我有 12.1)之间发生了变化,但我对此表示怀疑。 @MartinDimitrov - 我编辑了答案以解释如何修改 NOT IN 条件,以防该列中可能存在 NULL。【参考方案2】:
 select l.city, d.department_name from locations l, departments d
 where l.location_id=d.location_id
 UNION
 select l2.city, null department_name
 from locations l2
where not exists (select 1 from depertments d2 where   d2.location_id=l2.location_id) 

【讨论】:

虽然看起来很复杂,但它仍然有效。谢谢。【参考方案3】:

如果您尝试想象一些中间步骤,这很简单:

让我们想象表的交叉连接,然后用你的 where 子句过滤结果。在该行集中,不会显示任何具有非空位置和空部门的行。你可以用下一个查询来证明:

SELECT department_name, city 
FROM locations l, departments d
WHERE d.location_id is null;

no row selected

但你指定l.location_id NOT IN (SELECT DISTINCT location_id FROM departments);。这种情况不会影响表之间的交叉连接。 因此,您应该在没有部门的情况下获得额外的行数。

这就是为什么你需要left joinunion 子句。

【讨论】:

【参考方案4】:

使用相关子查询:

SELECT l.*,
       (SELECT d.department_name
        FROM departments d
        WHERE d.location_id = l.location_id
       ) as department_name
FROM locations l;

如果担心一个位置的多个部门,只需将它们聚合在一起即可:

SELECT l.*,
       (SELECT LISTAGG(d.department_name, ', ') WITHIN GROUP (ORDER BY d.department_name) as department_names
        FROM departments d
        WHERE d.location_id = l.location_id
       ) as department_name
FROM locations l;

【讨论】:

这不起作用,因为一个位置可能有多个部门,因此从子查询返回多行。 @MartinDimitrov 连接也将返回多行?这向您展示了如何在没有 OUTER JOIN 的情况下编写查询,我已经解释了为什么 OR 在我的答案中不起作用。我不相信他们在找你使用隐式语法。 你试过了吗?它返回ORA-01427: single-row subquery returns more than one row 我喜欢使用LISTAGG。我永远不会想到它。谢谢。

以上是关于SQL查询以查找具有部门名称的位置以及没有部门的位置的主要内容,如果未能解决你的问题,请参考以下文章

员工部门的唯一名称

SQL:至少有 10 名员工的部门名称

用SQL语句查询员工名称顺序生成10位长度的序号,前4位以部门编码(为空,则以0000补足)补足,后6位以0补足

从多个表中获取数据

SQL 语句递归查询 With AS 查找所有子节点

SQL 语句递归查询 With AS 查找所有子节点