如何改进这种自连接
How to Improve This Self-Joins
我正在通过使用其原始 HR
模式来学习 Oracle SQL,其中有 EMPLOYEES
table 其中包含我主要感兴趣的三个列: MANAGER_ID
,这基本上是对 EMPLOYEES.EMPLOYEE_ID
、DEPARTMENT_ID
和 SALARY
的自我引用。 (您可以在此处找到 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
我正在通过使用其原始 HR
模式来学习 Oracle SQL,其中有 EMPLOYEES
table 其中包含我主要感兴趣的三个列: MANAGER_ID
,这基本上是对 EMPLOYEES.EMPLOYEE_ID
、DEPARTMENT_ID
和 SALARY
的自我引用。 (您可以在此处找到 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