【问题标题】:SQL - Output every possible relation of 2 tables?SQL - 输出 2 个表的所有可能关系?
【发布时间】:2021-03-05 14:24:02
【问题描述】:

对于标题的含糊之处,我深表歉意。如果有人在阅读本文后有更好的建议,我会优雅地更改它。

我有 2 张桌子:

DEPT 表(部门):

|IDDEPT |  DEPTNAME  |
--------+------------+
| 10    | ACCOUNTING |
| 20    | RESEARCH   |    
| 30    | OPERATIONS |
--------+------------+

EMP 表(员工):

| IDEMP |  EMPNAME  |  MGR  | IDDEPT |
--------+-----------+-------+--------+
| 7001  |   SMITH   | 7007  | 10     |
| 7002  |   ALLEN   | 7006  | 10     |
| 7003  |   WARD    | 7006  | 20     |
| 7004  |   JONES   | 7006  | 20     |
| 7005  |   MARTIN  | 7007  | 10     |
| 7006  |   BLAKE   | 7008  | 20     |
| 7007  |   FORD    | 7008  | 10     |
| 7008  |   KING    | NULL  | 10     |
--------+-----------+-------+--------+

MGR 列是员工的经理标识符。 KING 为 NULL,因为他是公司的董事。 EMP 表上的 IDDEPT 是员工所属的部门。

我想要做的是,对于每个经理(在这种情况下为 KING、FORD 和 BLAKE),显示他们在每个部门(10、20 和 30)监督的员工数量。

对于这个给定的场景,我试图获得的输出(不是严格的这个顺序)如下:

| IDEMP |  EMPNAME  | IDDEPT |  N_EMPLOYEES_SUPERVISED  |
--------+-----------+--------+--------------------------+
| 7006  |   BLAKE   | 10     | 1                        |
| 7006  |   BLAKE   | 20     | 2                        |
| 7006  |   BLAKE   | 30     | 0                        |
| 7007  |   FORD    | 10     | 2                        |
| 7007  |   FORD    | 20     | 0                        |
| 7007  |   FORD    | 30     | 0                        |
| 7008  |   KING    | 10     | 1                        |
| 7008  |   KING    | 20     | 1                        |
| 7008  |   KING    | 30     | 0                        |
--------+-----------+--------+--------------------------+

但是,我无法输出计数为 0 的行。

【问题讨论】:

  • 定义:如果对于基础集合中的每个 xx 都与自身相关,则关系是 自反 .显然你的关系是不是自反的。除此之外 - 通常部门负责人“监督”该部门的所有员工,而不仅仅是那些直接向他或她报告的员工。您是否只需要每个部门的直接报告数量?
  • 对。是的,这是我自己为改进 SQL 所做的一项个人任务。我是这么想的,但可能完全像你现在的样子。

标签: sql oracle count left-join inner-join


【解决方案1】:

您可以使用分层查询来降低EMP 层次结构2 级,然后在聚合之前对DEPT 表使用分区外连接(并且只能从EMPDEPT 中选择一次):

SELECT e.idemp,
       e.empname,
       d.iddept,
       COUNT( e.iddept )
FROM   DEPT d
       LEFT OUTER JOIN (
         SELECT CONNECT_BY_ROOT( IDEMP   ) AS IDEMP,
                CONNECT_BY_ROOT( EMPNAME ) AS EMPNAME,
                IDDEPT
         FROM   EMP e
         WHERE  LEVEL = 2
         CONNECT BY PRIOR IDEMP = MGR
       ) e
       PARTITION BY ( e.IDEMP, e.EMPNAME )
       ON ( e.IDDEPT = d.IDDEPT )
GROUP BY
       e.idemp,
       e.empname,
       d.iddept;

其中,对于样本数据:

CREATE TABLE DEPT ( IDDEPT, DEPTNAME ) AS
SELECT 10, 'ACCOUNTING' FROM DUAL UNION ALL
SELECT 20, 'RESEARCH'   FROM DUAL UNION ALL
SELECT 30, 'OPERATIONS' FROM DUAL;

CREATE TABLE EMP ( IDEMP, EMPNAME, MGR, IDDEPT ) AS
SELECT 7001, 'SMITH',  7007, 10 FROM DUAL UNION ALL
SELECT 7002, 'ALLEN',  7006, 10 FROM DUAL UNION ALL
SELECT 7003, 'WARD',   7006, 20 FROM DUAL UNION ALL
SELECT 7004, 'JONES',  7006, 20 FROM DUAL UNION ALL
SELECT 7005, 'MARTIN', 7007, 10 FROM DUAL UNION ALL
SELECT 7006, 'BLAKE',  7008, 20 FROM DUAL UNION ALL
SELECT 7007, 'FORD',   7008, 10 FROM DUAL UNION ALL
SELECT 7008, 'KING',   NULL, 10 FROM DUAL;

输出:

IDEMP |企业名称 |身份证 |计数(E.IDDEPT) ----: | :-------- | -----: | --------------: 7006 |布莱克 | 10 | 1 7006 |布莱克 | 20 | 2 7006 |布莱克 | 30 | 0 7007 |福特 | 10 | 2 7007 |福特 | 20 | 0 7007 |福特 | 30 | 0 7008 |国王 | 10 | 1 7008 |国王 | 20 | 1 7008 |国王 | 30 | 0

db小提琴here

【讨论】:

  • 如果层次结构是三层深度,则此方法有效,就像在 OP 的示例数据中一样。如果比这更深,查询将返回各级经理的直接下属人数,而不仅仅是“主管”和部门负责人的人数。
  • @mathguy OP 希望“为每个经理”提供它;他们没有说要将其限制在管理链的前 2 层。
  • 非常感谢您提供的示例 MT0,我什至不知道有 CONNECT_BY_ROOT 这样的东西...
【解决方案2】:

您可以cross join 带有部门列表的员工表,然后带上带有left join 的原始表。要将结果限制为经理员工,我们可以使用exists

select m.idemp, m.empname, d.iddept, 
    count(e.idemp) as n_employees_supervised
from emp m
cross join dept d 
left join emp e on e.mgr = m.idemp and e.iddept = d.iddept
where exists (select 1 from emp e1 where e1.mgr = m.idemp)
group by m.idemp, m.empname, d.iddept

避免exists 子查询的替代方法是条件聚合:

select m.idemp, m.empname, d.iddept, 
    sum(case when e.iddept = d.iddept then 1 else 0 end) as n_employees_supervised
from emp m
cross join dept d 
inner join emp e on e.mgr = m.idemp
group by m.idemp, m.empname, d.iddept

【讨论】:

  • 效果很好。现在我明白为什么我使用了错误的交叉连接,谢谢!出于您的好奇,我在发布之前的查询与您的第一个查询非常相似,但我没有将 'emp e' 与 iddept 列一起加入......另外,我使用子查询来指定员工经理:'m.idemp 在哪里(从 emp 中选择经理)'
猜你喜欢
  • 2017-11-26
  • 1970-01-01
  • 2022-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多