[2022.10.19.수] 조인
1. 조인
1-1. 조인
- 조인은 두 개 이상의 테이블을 연결지어서 데이터를 조회하는 것이다.
- 오라클과 같은 관계형데이터베이스는 모든 정보를 하나의 테이블에 저장하는 것이 아니라, 데이터가 정규화되어 여러 테이블에 분산되어 있다.
- 데이터가 여러 테이블에 흩어져 있기 때문에 사용자가 원하는 데이터를 찾기위해서는 여러 테이블을 다 조회해야 한다.
- 조인은 여러 테이블에 흩어져 있는 정보중에서 사용자가 원하는 정보만 가져와서 가상의 테이블처럼 만들어서 데이터를 조회하는 것이다.
- 조인을 위해 알아야 할 것
1. 내가 필요로 하는 정보가 어느 테이블에 있는지 알아야 함
2. 조인조건 작성
예시)
SELECT EMP.ENAME, DEPT.DNAME -- 나타내고싶은 정보
FROM EMP, DEPT -- 테이블 조인
WHERE EMP.DEPTNO = DEPT.DEPT -- 조인 조건
SELECT EMPLOYEES.EMPLOYEE_ID, EMPLOYEES.FIRST_NAME, DEPARTMENTS.DEPARTMENT_NAME
FROM EMPLOYEES, DEPARTMENTS ( 원하는 정보가 있는 테이블들 )
WHERE EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID; ( 조인조건 )
1-2. 조인의 종류
참고) 카티션곱(Cartesian product)
- 카티션곱은 조인조건을 지정하지 않고, 두 개이상의 테이블을 조인하는 것이다.
- 조인조건을 지정하지 않으면 조인된 테이블의 모든 데이터를 전부 가져오게된다.
- 조인 작업에 참여하는 테이블의 행수를 모두 곱한 값만큼의 행이 조회되는 행의 개수가 된다.
-- employees 테이블과 departments 테이블 조인하기 (조인조건 없이 조인하기)
-- employees 테이블의 모든 행과 departments 테이블의 모든 행이 각각 한번씩 조인된다.
select *
from employees, departments;
1) 등가조인 (Eui Join)
- 조인에서 가장 많이 사용되는 조인이다.
- 등가조인은 선행 테이블에서 데이터를 가져온 후 조인 조건절을 검사해서 동일한 값을 가진 데이터를 후행 테이블에서 꺼내오는 방법이다.
- 조인하는 테이블에서 같은 값을 가지는 행끼리 조인하는 것
- 조인조건에서 Equal(=) 연산자를 사용해서 EQUI Join이라고 한다.
--------------------------------------------------------------------------------
-- 등가조인
--------------------------------------------------------------------------------
-- 직원아이디, 직원이름, 직종아이디, 급여, 소속부서아이디, 소속부서명 조회하기
-- employees, employees, employees, employees, employees,
-- dapartments, dapartments
select e.employee_id, e.first_name, e.job_id, e.salary, d.department_id, d.department_name
from employees e, departments d
where e.department_id = d.department_id
order by employee_id asc;
-- 직원아이디, 직원이름, 직종아이디, 직종제목, 직종 최저급여, 직종 최고급여, 급여 조회하기
-- employees, employees, employees, employees
-- jobs jobs jobs jobs
select e.employee_id, e.first_name, e.job_id, j.job_title, j.max_salary, j.min_salary, e.salary
from employees e, jobs j
where e.job_id = j.job_id
order by employee_id asc;
select e.employee_id, e.first_name, e.job_id, j.job_title, j.max_salary, j.min_salary, e.salary
from employees e join jobs j
on e.job_id = j.job_id;
-- 직원아이디, 직원이름, 직종아이디, 직종제목, 급여, 소속부서아이디, 소속부서명 조회하기
-- employees employees employees employees employees
-- jobs jobs
-- dapartments departments
select e.employee_id, e.first_name, j.job_id, j.job_title, e.salary, d.department_id, d.department_name
from employees e, jobs j, departments d
where e.job_id = j.job_id and e.department_id = d.department_id;
select e.employee_id, e.first_name, j.job_id, j.job_title, e.salary, d.department_id, d.department_name
from employees e join jobs j
on e.job_id = j.job_id
join departments d
on e.department_id = d.department_id;
-- 부서관리자가 지정된 부서들의 부서아이디, 부서이름, 부서관리자 아이디, 부서관리자 이름 조회
-- departments departments departments(manager_id)
-- employees(employee_id) employees
-- 열 이름이 같다고 조인하는것 아님!! 연결관계를 생각해야 함 (부서의 관리자 = 직원 아이디)-직원 중 누군가가 부서의 관리자니까
select d.department_id, d.department_name, d.manager_id, e.first_name
from departments d, employees e
where d.manager_id is not null --필터링 조건
and d.manager_id = e.employee_id; --조인 조건
select d.department_id, d.department_name, d.manager_id, e.first_name
from departments d join employees e
on d.manager_id = e.employee_id
where d.manager_id is not null;
-- 부서아이디, 부서명, 소재지 우편번호, 소재지 주소, 소재지 도시명, 소재지 나라이름 조회하기
-- departments departments
-- locations locations locations
-- countries
-- 공통적으로 갖고있는 부분을 찾기
select d.department_id, d.department_name, l.postal_code, l.street_address, l.city, c.country_name
from departments d, locations l, countries c
where d.location_id = l.location_id and l.country_id = c.country_id;
select d.department_id, d.department_name, l.postal_code, l.street_address, l.city, c.country_name
from departments d join locations l
on d.location_id = l.location_id
join countries c
on l.country_id = c.country_id;
* 열 이름 같은 것끼리 조인하는 것 아님 !
* 등가조인은 같은 열을 찾아서 equal로 비교
비등가조인은 범위
2) 비등가조인 (Non-Eui Join)
- 조인조건을 지정할 때 조인 대상테이블에서 같은 값을 가진 데이터를 가져오는 대신, 크거나 작은 경우의 조건으로 데이터를 조회하는 조인 방식이 비등가 조인이다.
--------------------------------------------------------------------------------
-- 비등가조인
--------------------------------------------------------------------------------
create table salary_grades(
salary_grade char(1) primary key,
min_salary number(8) not null,
max_salary NUMBER(8) not null
);
insert into salary_grades(salary_grade, min_salary, max_salary) values('A', 0, 2499);
insert into salary_grades(salary_grade, min_salary, max_salary) values('B', 2500, 4999);
insert into salary_grades(salary_grade, min_salary, max_salary) values('C', 5000, 9999);
insert into salary_grades(salary_grade, min_salary, max_salary) values('D', 10000, 19999);
insert into salary_grades(salary_grade, min_salary, max_salary) values('E', 20000, 40000);
COMMIT;
-- 직원 아이디, 직원이름, 급여, 급여등급 조회하기
-- employees employees employees
-- salary_grades
select e.employee_id, e.first_name, e.salary, g.salary_grade
from employees e, salary_grades g
where e.salary >= g.min_salary and e.salary <= g.max_salary -- 비등가 조인 조건
order by e.employee_id ;
select e.employee_id, e.first_name, e.salary, g.salary_grade
from employees e join salary_grades g
on e.salary >= g.min_salary and e.salary <= g.max_salary -- 비등가 조인 조건
order by e.employee_id;
-- 직원아이디, 직원이름, 직종아이디, 직종제목, 급여, 급여등급, 소속부서명 조회하기
-- employees employees employees employees
-- jobs jobs
-- salary_grades
-- departments
select e.employee_id, e.first_name, j.job_id, j.job_title, e.salary, g.salary_grade, d.department_name
from employees e, jobs j, salary_grades g, departments d
where e.job_id = j.job_id and e.department_id = d.department_id
and e.salary >= g.min_salary and e.salary <= g.max_salary
order by employee_id;
SELECT E.EMPLOYEE_ID, E.FIRST_NAME, J.JOB_ID, J.JOB_TITLE, E.SALARY, G.SALARY_GRADE, D.DEPARTMENT_NAME
FROM EMPLOYEES E JOIN JOBS J
ON E.JOB_ID = J.JOB_ID
JOIN SALARY_GRADES G
ON E.SALARY >= G.MIN_SALARY AND E.SALARY >= G.MAX_SALARY
JOIN DEPARTMENTS D
ON E.DEPARTMENT_ID = D.DEPARTMENT_ID
ORDER BY E.EMPLOYEE_ID ASC;
-- 직원중에서 급여등급이 'B'등급인 직원의 아이디, 이름, 직종아이디, 급여 조회하기
-- salary_grades employees employees employees employees
select e.employee_id, e.first_name, e.job_id, e.salary
from employees e, salary_grades g
where e.salary >= g.min_salary and e.salary <= g.max_salary
and g.salary_grade = 'B';
select e.employee_id, e.first_name, e.job_id, e.salary
from employees e join salary_grades g
on e.salary >= g.min_salary and e.salary <= g.max_salary
where g.salary_grade = 'B';
3) 포괄조인 (Outer Join)
- 등가조인, 비등가조인은 조회하려는 데이터들이 조인에 참여하는 모든 테이블에 데이터가 존재하는 경우에만 조회된다.
- 포괄조인은 한쪽 테이블에는 데이터가 있고, 다른쪽 테이블에는 데이터가 없는 경우에 데이터가 있는 쪽 테이블의 내용을 전부 조회하는 조인방법이다.
* departments 테이블의 manager_id가 null인 것들은 employees 테이블의 employee_id와 일치하는게 없어서 조인이 안돼 (-> 값 출력이 안됨)
-> employees 테이블에 null값을 가지는 행을 추가시켜서 depalrtments 테이블에서 manager_id가 null인 것과 조인되게 함
-> where d.manager_id = e.employee_id(+) ---> oracle join
-> null이 없는 쪽에 (+)를 붙이는 것
--------------------------------------------------------------------------------
-- 포괄조인 : 테이블에서 조인
--------------------------------------------------------------------------------
-- 부서아이디, 부서명, 부서관리자 직원아이디, 직원이름 조회하기
-- dapartments departments departments(manager_id)
-- employees (employee_id) employees
-- 포괄조인을 사용해서 부서관리자 직원아이디가 null인 부서도 조회되게 하기
select d.department_id, d.department_name, d.manager_id, e.first_name
from departments d, employees e
where d.manager_id = e.employee_id(+)
order by department_id;
-- left outer join은 왼쪽에 있는 테이블의 모든 행이 조회되게 하는 포괄조인 연산자 (ANSI join)
select d.department_id, d.department_name, d.manager_id, e.first_name
from departments d left outer join employees e
on d.manager_id = e.employee_id;
-- 직원아이디, 직원이름, 소속부서 아이디, 소속부서명 조회하기
-- 포괄조인을 사용해서 소속부서 아이디가 null인 직원도 조회되게 하기
select e.employee_id, e.first_name, e.department_id, d.department_name
from employees e, departments d
where e.department_id = d.department_id(+)
order by e.employee_id asc;
-- right outer join은 오른쪽에 있는 테이블의 모든 행이 조회되게 하는 포괄조인 연산자 (ANSI join)
select e.employee_id, e.first_name, e.department_id, d.department_name
from departments d right outer join employees e
on e.department_id = d.department_id
order by e.employee_id asc;
4) 셀프조인 (Self Join)
- 하나의 테이블을 이용해서 조인을 구성하는 것이다.
- 하나의 테이블에 안에 상위데이터, 하위데이터가 있는 경우 상위데이터와 하위데이터를 서로 연관지어서 조회할 때 셀프조인이 필요하다.
- 하나의 테이블을 역할을 각각 나누어서 조인에 참여시켜야 한다
--------------------------------------------------------------------------------
-- 셀프조인 : 하나의 테이블로 조인을 구성하는 것
--------------------------------------------------------------------------------
-- 직원의 아이디, 직원이름, 관리자 아이디, 관리자 이름 조회하기
select emp.employee_id emp_id, emp.first_name emp_name, emp.job_id job_id,
mgr.employee_id mgr_id, mgr.first_name mgr_name
from employees emp, employees mgr
where emp.manager_id = mgr.employee_id(+)
order by emp.employee_id asc;
-- 직원의 아이디, 직원이름, 직원의 소속부서아이디, 직원의 소속부서명, 관리자 이름, 관리자의 소속부서명
-- employees employees employees
-- departments departments
-- employees
-- departments
select e.employee_id 직원아이디, e.first_name 직원이름, ed.department_name 직원의소속부서,
m.first_name 관리자이름, md.department_name 관리자소속부서
from employees e, employees m, departments ed, departments md
where e.manager_id = m.employee_id(+)
and e.department_id = ed.department_id(+)
and m.department_id = md.department_id(+)
order by e.employee_id asc;
* where e.department_id = ed.department_id(+)
and e.manager_id = m.employee_id(+)
and m.department_id = md.department_id(+)
--employees 테이블에 department_id가 null인게 있음 -> departments 테이블의 department_id에도 null행을 추가 (+)
--employees 테이블에 manager_id가 null인게 있음 -> manager 테이블의 employee_id에도 null행을 추가 (+)
--직원테이블의 매니저아이디가 null -> 매니저테이블의 직원아이디의 null을 가리킴
-> 이 매니저테이블과 매니저부서테이블을 연결할 때, 매니저테이블의 null행이 가리키는 것이 있어야 완전한 조인
-> 마지막 문장에도 (+)를 해줘야 함
--------------------------------------------------------------------------------
-- 퀴즈
--------------------------------------------------------------------------------
-- 급여를 5000이상 10000이하로 받는 직원들의 직원아이디, 이름, 직종아이디, 직종제목, 급여 조회하기
-- 급여에 대한 오름차순 정렬
select e.employee_id, e.first_name, j.job_id, j.job_title, e.salary
from employees e, jobs j
where salary >= 5000 and salary <= 10000
order by salary asc;
-- 직원의 아이디, 이름, 직종아이디, 직종최저급여와 직종최고급여의 평균값, 급여 조회하기
-- employees employees employees salary
-- jobs jobs
-- 급여에 대한 오름차순 정렬
select e.employee_id, e.first_name, j.job_id, j.min_salary, j.max_salary, (j.min_salary + j.max_salary)/2 average, e.salary
from employees e, jobs j
where e.job_id = j.job_id
order by salary asc;
-- 2007년에 입사한 직원들의 직원아이디, 이름, 입사일, 소속부서명 조회하기
-- 직원아이디에 대한 오름차순 정렬
select e.employee_id, e.first_name, e.hire_date, d.department_name
from employees e, departments d
where e.department_id = d.department_id(+)
and e.hire_date >= '2007-01-01' and e.hire_date < '2008-01-01'
order by e.employee_id asc;
-- 소속부서 아이디가 50이나 80이고, 급여가 5000이상 10000이하인 직원들의 직원아이디, 이름, 직종아이디, 소속부서명 조회하기
-- 직원아이디에 대한 오름차순 정렬
select e.employee_id, e.first_name, e.job_id, e.salary, d.department_name
from employees e, departments d
where e.department_id = d.department_id
and e.department_id in (50, 80)
and e.salary >= 5000 and e.salary <= 10000
order by e.employee_id;
-- 100번 관리자에게 보고하는 직원들의 직원아이디, 이름, 직종아이디, 급여, 급여등급, 소속부서명을 조회하기
select e.employee_id, e.first_name, e.job_id, e.salary, g.salary_grade, d.department_name
from employees e, salary_grades g, departments d
where e.manager_id = 100
and e.salary >= g.min_salary and e.salary <= g.max_salary
and e.department_id = d.department_id
order by employee_id;
-- 급여등급이 'A', 'B'에 해당하는 직원들의 직원아이디, 이름, 급여, 급여등급, 직종아이디를 조회하기
-- 급여등급에 대한 오름차순으로 정렬하고, 급여 등급이 동일한 경우 급여에 대한 오름차순으로 정렬한다.
select e.employee_id, e.first_name, e.salary, g.salary_grade, e.job_id
from employees e, salary_grades g
where e.salary >= g.min_salary and e.salary <= g.max_salary
and g.salary_grade in ('A', 'B')
order by g.salary_grade asc, e.salary asc;
-- 커미션을 받은 직원들의 아이디, 이름, 급여, 커미션, 급여등급을 조회하기
-- 급여에 대한 오름차순으로 정렬한다.
select e.employee_id, e.first_name, e.salary, e.commission_pct, g.salary_grade
from employees e, salary_grades g
where e.commission_pct is not null
and e.salary >= g.min_salary and e.salary <= g.max_salary
order by salary asc;
-- 'Toronto'에서 근무중인 직원의 아이디, 이름을 조회하기
select e.employee_id, e.first_name
from employees e, departments d, locations l
where e.department_id = d.department_id
and d.location_id = l.location_id
and l.city = 'Toronto';
-- 직원들 중에서 자신의 관리자보다 먼저 입사한 직원의 아이디, 이름, 입사일, 관리자이름, 관리자의 입사일으로 조회하기
-- 직원입사일에 대한 오름차순으로 정렬하기
select e.employee_id e_id,
e.first_name e_name,
e.hire_date e_hire_date,
m.first_name m_name,
m.hire_date m_hire_date
from employees e, employees m
where e.manager_id = m.employee_id
and e.hire_date < m.hire_date
order by e.hire_date;
-- 모든 부서의 부서아이디, 부서명, 부서 관리자 이름, 소재지 우편번호, 소재지 주소, 소재지 도시명을 조회하기
-- 부서관리자가 지정되지 않은 부서도 조회하고, 부서아이디에 대한 오름차순으로 정렬하기
select d.department_id, d.department_name, e.first_name, l.postal_code, l.street_address, l.city
from departments d, employees e, locations l
where d.manager_id = e.employee_id(+)
and d.location_id = l.location_id -- departments테이블의 location_id에는 null이 없으니까 locations테이블과 모두 연결 가능
order by d.department_id; -- ( d-> e (null값 있음) d-> l (연결해야 할 null값이 없음) )
-- 모든 직원들의 직원아이디, 이름, 직종아이디, 급여, 급여등급, 관리자이름, 소속부서아이디, 소속부서명, 소재도시명을 조회하기
-- 직원아이디에 대한 오름차순으로 정렬한다.
select e.employee_id e_id,
e.first_name e_name,
e.job_id e_job_id,
e.salary e_salary,
g.salary_grade e_salary_grade,
m.first_name m_name,
d.department_id e_dept_id,
d.department_name e_dept_name,
l.city e_city
from employees e, salary_grades g, employees m, departments d, locations l
where e.salary >= g.min_salary and e.salary <= g.max_salary
and e.manager_id = m.employee_id(+)
and e.department_id = d.department_id(+) -- 178의 department_id가 null, departments테이블에 null행을 추가
and d.location_id = l.location_id(+) -- departments의 null행도 locations를 조인해야 완전한 조인이기 때문에 locations에도 null행 추가
order by e.employee_id asc; -- ( e -> d -> l 로 null값도 연결 )