【问题标题】:SQL query to find locations with department names as well as locations without departmentsSQL查询以查找具有部门名称的位置以及没有部门的位置
【发布时间】: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,为什么?它确实产生了预期的结果。
  • @Massimo 进一步阅读“由于此示例中的列名在连接中的两个表中相同,因此您还可以通过指定连接语法的 USING 子句来使用公共列功能。”
  • 你说得对,我不知道...我链接到“传统”sql

标签: sql oracle


【解决方案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。我永远不会想到它。谢谢。
    猜你喜欢
    • 2021-06-20
    • 1970-01-01
    • 1970-01-01
    • 2021-05-25
    • 1970-01-01
    • 2023-01-16
    • 2022-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多