如何改进这种自连接

How to Improve This Self-Joins

我正在通过使用其原始 HR 模式来学习 Oracle SQL,其中有 EMPLOYEES table 其中包含我主要感兴趣的三个列: MANAGER_ID,这基本上是对 EMPLOYEES.EMPLOYEE_IDDEPARTMENT_IDSALARY 的自我引用。 (您可以在此处找到 schema diagram and schema objects)。

我希望为每个员工检索 his/her SALARY,以及员工经理的部门平均工资。例如,如果我们有以下内容(EMPLOYEE_ID = 140 是这里的相关方):

+-------------+--------+---------------+------------+
| EMPLOYEE_ID | SALARY | DEPARTMENT_ID | MANAGER_ID |
+-------------+--------+---------------+------------+
| 140         | 12000  | 50            | 110        |
| 110         | 20000  | 60            | 101        |
| 156         | 18000  | 60            | 101        |
| 175         | 15000  | 60            | 105        |
| 320         | 24000  | 60            | 105        |
+-------------+--------+---------------+------------+

我有兴趣获得部门中所有经理(不是所有其他非管理员工)的平均工资,其中员工的经理 工作(在本例中,DEPARTMENT_ID =60),并将其与 employee 的(在本例中,140)。在上面的示例数据中,输出应为:

+-------------+--------+-------------+-------------+------------+
| EMPLOYEE_ID | SALARY | AVG_MGR_SAL | MGR_DEPT_ID | MANAGER_ID |
+-------------+--------+-------------+-------------+------------+
| 140         | 12000  | 19250       | 60          | 110        |
+-------------+--------+-------------+-------------+------------+

我们有四 (4) 名经理在 60 部门工作,19250 美元 计算为 (20000 + 18000 + 15000 + 24000) / 4. 我提出了以下似乎有效的查询(并排除了那些没有经理的员工):

select
    employee_id
    , salary employee_salary
    , trunc(mgr_info.avg_manager_salary_per_dept, 0) emp_manager_avg_sal_dept
    , mgr_info.manager_dept_id
    , mgr_info.manager_id
from employees
join (
      select
          e1.employee_id manager_id
          , e1.department_id manager_dept_id
          , e1.salary manager_salary
          , avg(e1.salary) over (partition by e1.department_id) avg_manager_salary_per_dept
      from employees e1
      join (
            select distinct manager_id 
            from employees 
            where manager_id is not null
           ) mgr_ids
          on e1.employee_id = mgr_ids.manager_id
      ) mgr_info
    on employees.manager_id = mgr_info.manager_id
order by employee_id

但是,我觉得应该有一种更好的方法,可以用更少的自连接获得相同的结果。有没有办法获得更好的性能?

类似这样的事情...您只需要一个连接,就可以计算 table 的 "manager" 副本上经理部门的平均工资。我只包含了几个专栏,您可能需要更多或更少,但我相信您想要的核心内容已经涵盖。

(注意:编辑是因为我意识到我遗漏了要求中的一个细节)

select e.employee_id   as employee_id,
       e.salary        as employee_salary,
       m.employee_id   as manager_id,
       m.department_id as manager_dept_id,
       m.avg_salary    as avg_sal_of_mgr_dept
from hr.employees e inner join
       ( select employee_id, department_id, 
                avg(salary) over (partition by department_id) as avg_salary
         from   hr.employees
         where  employee_id in (select manager_id from hr.employees)
       ) m
on e.manager_id = m.employee_id
;

这是一个使用一系列连接来获得结果的选项:

SELECT DISTINCT t1.EMPLOYEE_ID,
                t1.SALARY,
                t1.DEPARTMENT_ID,
                COALESCE(t2.SALARY, 0.0) AS ManagerAvgSal
FROM employees t1
LEFT JOIN
(
    SELECT e1.DEPARTMENT_ID, AVG(e1.SALARY) AS SALARY
    FROM employees e1
    WHERE e1.EMPLOYEE_ID IN (SELECT DISTINCT MANAGER_ID FROM employees)
    GROUP BY e1.DEPARTMENT_ID
) t2
    ON t1.DEPARTMENT_ID = t2.DEPARTMENT_ID