■ JOIN
- 두개 이상의 테이블에서 내가 원하는 데이터를 가지고 오는 방법
SELECT employee_id, last_name, department_id
FROM hr.employees; -- 107행
+
SELECT department_id, department_name
FROM hr.departments; -- 27행
1. cartesian product
- 조인 조건이 생략되었을 경우
- 조인 조건이 잘 못 만든 경우
- 첫번째 테이블의 행수와 두번째 테이블 행수가 곱해진다.
SELECT employee_id, last_name, department_name
FROM hr.employees, hr.departments;
--> 107행 * 27행
2. eqi join, inner join, simple join, 등가조인
- 조인키 값이 일치하는 데이터만 추출하는 조인
- 조인 조건은 M쪽 집압의 행의수보다 작거나 같게 나와야 한다.
SELECT e.employee_id, e.last_name, d.department_name
FROM hr.employees e, hr.departments d
WHERE e.department_id = d.department_id; -- 조인조건술어
M쪽 1쪽 = M쪽
- join을 수행할 테이블이 n개면 조인조건술어는 n-1개 기술해야 한다.
SELECT e.employee_id, e.last_name, l.city
FROM hr.employees e, hr.departments d, hr.locations l -- 순서무관
WHERE e.department_id = d.department_id -- 조인조건술어
AND d.location_id = l.location_id; -- 조인조건술어, 순서무관
[문제] 사원들의 employee_id, country_name을 출력
SELECT employee_id, country_name
FROM hr.employees e, hr.departments d, hr.locations l, hr.countries c
WHERE e.department_id = d.department_id
AND d.location_id = l.location_id
AND l.country_id = c.country_id;
[문제] 80부서에 근무하는 사원들의 last_name, job_id, department_name, city 출력.
SELECT e.last_name, e.job_id, d.department_name, l.city
FROM hr.employees e, hr.departments d, hr.locations l
WHERE e.department_id = d.department_id
AND d.location_id = l.location_id
AND e.department_id = 80;
[문제] locations 테이블에 있는 city칼럼에 Toronto도시에서 근무하는 모든 사원의
last_name, job_id, department_id, department_name 출력.
SELECT e.last_name, e.job_id, e.department_id, d.department_name
FROM hr.employees e, hr.departments d, hr.locations l
WHERE e.department_id = d.department_id
AND d.location_id = l.location_id
AND l.city = 'Toronto';
SELECT e.last_name, e.job_id, d.department_name
FROM hr.employees e, hr.departments d
WHERE e.department_id = d.department_id
AND e.department_id = 80;
↓ 성능개선(9i 버전~)
SELECT e.last_name, e.job_id, d.department_name
FROM hr.employees e, hr.departments d
WHERE e.department_id = 80
AND d.department_id = 80;
SELECT e.last_name, e.job_id, d.department_name
FROM hr.employees e, hr.departments d
WHERE e.department_id = d.department_id;
3. outer join
- 키 값이 일치되는 데이터 또는 키값이 일치되지 않는 데이터도 출력하는 조언
- (+)를 이용해서 outer join 표현한다.
- 양쪽에 (+)를 수행하면 오류발생
SELECT e.last_name, e.job_id, d.department_name
FROM hr.employees e, hr.departments d
WHERE e.department_id = d.department_id(+);
SELECT e.last_name, e.job_id, d.department_name
FROM hr.employees e, hr.departments d
WHERE e.department_id(+) = d.department_id;
★
SELECT e.last_name, d.department_name, l.city
FROM hr.employees e, hr.departments d, hr.locations l
WHERE e.department_id(+) = d.department_id
AND d.location_id(+) = l.location_id;
join 알고리즘
① ② ③ 테이블
└ ─ ─ ─ ┘ │
□ │
└ ─ ─ ─ ─ ┘
○
WHERE e.department_id = d.department_id
AND d.location_id = l.location_id;
-- 106명 : (e O + d O) + 지역 O
WHERE e.department_id(+) = d.department_id
AND d.location_id = l.location_id;
-- 122명 : (이름 null 포함 + 부서명 O) + 지역 O
WHERE e.department_id = d.department_id(+)
AND d.location_id = l.location_id;
-- 106명 : (이름 O + 부서 null) + 지역 O
WHERE e.department_id = d.department_id
AND d.location_id(+) = l.location_id;
-- 106명 : (이름 O + 부서 O) + 지역 null
-- ① + ②
AND d.location_id = l.location_id(+);
-- 106명 : (이름 O + 부서 O) + 지역 null
WHERE e.department_id(+) = d.department_id
AND d.location_id(+) = l.location_id;
-- 138명 : (이름 null + 부서 O) + 지역 null
WHERE e.department_id(+) = d.department_id
AND d.location_id = l.location_id(+);
WHERE e.department_id = d.department_id(+)
AND d.location_id(+) = l.location_id;
WHERE e.department_id = d.department_id(+)
AND d.location_id = l.location_id(+);
[문제] commission_pct에 null 이 아닌 사원들의 last_name, commission_pct, department_name 출력
SELECT e.last_name, e.commission_pct, d.department_name
FROM hr.employees e, hr.departments d
WHERE e.department_id = d.department_id(+)
AND e.commission_pct is not null;
건수검증 ★
SELECT count(*)
FROM hr.employees e
WHERE e.commission_pct is not null; -- 35명
4. self join
- 자신의 테이블을 참조할 때 사용하는 조인
SELECT employee_id, last_name, manager_id
FROM hr.employees; -- 일반직원테이블(w)
+
SELECT employee_id, last_name
FROM hr.employees; -- 관리자(직속상관)테이블(m)
↓
SELECT w.employee_id, w.last_name, m.employee_id, m.last_name
FROM hr.employees w, hr.employees m
WHERE w.manager_id = m.employee_id;
SELECT w.employee_id, w.last_name, m.employee_id, m.last_name
FROM hr.employees w, hr.employees m
WHERE w.manager_id = m.employee_id(+);
5. non eqi join, 비등가조인
- eqi join(=) 할 수 없는 다른 비교연산자를 사용하는 조인의 유형
- 값을 범위로 조인하려는 경우 많이 사용한다.
SELECT e.employee_id, e.salary, j.grade_level
FROM hr.employees e, hr.job_grades j
WHERE e.salary >= j.lowest_sal
AND e.salary <= j.highest_sal;
||
SELECT e.employee_id, e.salary, j.grade_level
FROM hr.employees e, hr.job_grades j
WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal;
SELECT e.employee_id, e.salary, j.grade_level, d.department_name
FROM hr.employees e, hr.job_grades j, hr.departments d -- 다중 테이블
WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal
AND e.department_id = d.department_id(+);
[문제] 급여의 등급레이블의 빈도수를 출력해주세요.
SELECT j.grade_level, count(*)
FROM hr.employees e, hr.job_grades j
WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal
GROUP BY j.grade_level
ORDER BY j.grade_level;
■ ANSI(American Nationl Standard Institue)-SQL(Structured Query Language) JOIN 문법
1. cartesian product
- 조인 조건이 생략되었을 경우
- 조인 조건이 잘 못 만든 경우
- 첫번째 테이블의 행수와 두번째 테이블 행수가 곱해진다.
SELECT employee_id, last_name, department_name
FROM hr.employees CROSS JOIN hr.departments;
2. NATURAL JOIN
- equi join
- 조인조건술어를 자동으로 만들어 준다.
- 양쪽 테이블의 동일한 이름의 모든 컬럼을 기준으로 조인조건술어를 만든다.
- 양쪽 테이블의 동일한 이름의 컬럼 데이터 타입이 다를 경우 오류 발생
SELECT e.employee_id, d.department_name
FROM hr.employees e NATURAL JOIN hr.departments d;
||
SELECT e.employee_id, d.department_name
FROM hr.employees e, hr.departments d
WHERE e.department_id = d.department_id
AND e.manager_id = d.manager_id; -- 주의
SELECT d.department_name, l.city
FROM hr.departments d NATURAL JOIN hr.locations l;
||
SELECT d.department_name, l.city
FROM hr.departments d ,hr.locations l
WHERE d.location_id = l.location_id;
3. JOIN USING
- equi join
- 조인조건의 기준 컬럼을 지정한다.
- using절에 사용된 컬럼은 어느 테이블이라고 지정하면 오류 발생한다.
SELECT e.employee_id, d.department_name
FROM hr.employees e JOIN hr.departments d
USING(department_id);
SELECT e.employee_id, department_id, d.department_name, location_id, l.city
FROM hr.employees e JOIN hr.departments d
USING(department_id)
JOIN hr.locations l
USING(location_id)
WHERE department_id IN (20,30);
- 여러 컬럼을 기준컬럼을 사용 할 경우
SELECT e.employee_id, d.department_name
FROM hr.employees e JOIN hr.departments d
USING(department_id, manager_id);
4. JOIN ON
- equi join, self join, non equi join
- on 절을 이용해서 조인조건술어를 직접 만들어서 사용한다.
SELECT e.employee_id, e.department_id, d.department_name
FROM hr.employees e JOIN hr.departments d
ON e.department_id = d.department_id;
SELECT e.employee_id, e.department_id, d.department_name, l.city
FROM hr.employees e JOIN hr.departments d
ON e.department_id = d.department_id
JOIN hr.locations l
ON d.location_id = l.location_id
WHERE e.last_name LIKE '%a%'
AND e.salary >= 10000;
- self join
SELECT w.employee_id, w.last_name, m.employee_id, m.last_name
FROM hr.employees w JOIN hr.employees m
ON w.manager_id = m.employee_id;
- non equi join
SELECT e.employee_id, e.salary, j.grade_level
FROM hr.employees e JOIN hr.job_grades j
ON e.salary BETWEEN j.lowest_sal AND j.highest_sal;
SELECT e.employee_id, e.salary, j.grade_level, d.department_name
FROM hr.employees e JOIN hr.job_grades j
ON e.salary BETWEEN j.lowest_sal AND j.highest_sal
JOIN hr.departments d
ON e.department_id = d.department_id;
JOIN | ANSI-SQL JOIN |
5. OUTER JOIN | |
SELECT e.last_name, d.department_name FROM hr.employees e, hr.departments d WHERE e.department_id = d.department_id(+); |
SELECT e.last_name, d.department_name FROM hr.employees e LEFT OUTER JOIN hr.departments d ON e.department_id = d.department_id; |
SELECT e.last_name, d.department_name FROM hr.employees e, hr.departments d WHERE e.department_id(+) = d.department_id; |
SELECT e.last_name, d.department_name FROM hr.employees e RIGHT OUTER JOIN hr.departments d ON e.department_id = d.department_id; |
SELECT e.last_name, d.department_name, l.city FROM hr.employees e, hr.departments d, hr.locations l WHERE e.department_id = d.department_id(+) AND d.location_id = l.location_id(+); |
SELECT e.last_name, d.department_name, l.city FROM hr.employees e LEFT OUTER JOIN hr.departments d ON e.department_id = d.department_id LEFT OUTER JOIN hr.locations l ON d.location_id = l.location_id; |
SELECT e.last_name, d.department_name FROM hr.employees e, hr.departments d WHERE e.department_id(+) = d.department_id(+); -- 오류 : 양쪽에 (+) 표현 불가 |
SELECT e.last_name, d.department_name FROM hr.employees e FULL OUTER JOIN hr.departments d ON e.department_id = d.department_id; |
[문제] 2006년도에 입사한 사원들의 부서이름별 급여의 총액, 평균을 출력해주세요.
1) 오라클전용
SELECT d.department_name, sum(e.salary) 급여총액, round(avg(e.salary)) 급여평균
FROM hr.employees e, hr.departments d
WHERE e.hire_date >= to_date('2006-01-01','yyyy-mm-dd')
AND e.hire_date <= to_date('2006-12/31','yyyy-mm-dd')
AND e.department_id = d.department_id
GROUP BY d.department_name;
2) ANSI 표준
SELECT d.department_name, sum(e.salary) 급여총액, round(avg(e.salary)) 급여평균
FROM hr.employees e JOIN hr.departments d
ON e.department_id = d.department_id
WHERE e.hire_date BETWEEN to_date('2006-01-01','yyyy-mm-dd')
AND to_date('2006-12/31','yyyy-mm-dd')
GROUP BY d.department_name;
> 참고 <
WHERE e.hire_date >= to_date('2006-01-01','yyyy-mm-dd')
AND e.hire_date <= to_date('2006-12-31','yyyy-mm-dd');
★
WHERE e.hire_date >= to_date('2006-01-01 00:00:00','syyyy-mm-dd hh24:mi:ss')
AND e.hire_date <= to_date('2006-12-31 00:00:00','syyyy-mm-dd hh24:mi:ss')
>>> 주의!! 시간 기본값 0시로 되어있다.
↓
AND e.hire_date <= to_date('2006-12-31 23:59:59','yyyy-mm-dd');
AND e.hire_date < to_date('2007-01-01','yyyy-mm-dd'); ★
>>> 시간까지 지정하거나 날짜를 1일 추가하여 검색
rrrr 시간표시는 없지만 시간 기본값은 00:00:00
yyyy 사용 추천
SELECT to_char(to_date('-2000','syyyy'),'syyyy ad bc'),
to_char(to_date('2000','syyyy'),'syyyy ad bc')
FROM dual;
syyyy : 기원전 표기 필요 시
[문제] 2007년도에 입사한 사원들의 도시이름별 급여의 총액, 평균을 출력해주세요.
단 부서 배치를 받지 않은 사원들의 정보도 출력해주세요.
1) 오라클 전용
SELECT l.city, sum(e.salary) 급여총액, round(avg(e.salary)) 급여평균
FROM hr.employees e, hr.departments d, hr.locations l
WHERE e.hire_date >= to_date('2007-01-01','yyyy-mm-dd')
AND e.hire_date < to_date('2008-01-01','yyyy-mm-dd')
AND e.department_id = d.department_id(+)
AND d.location_id = l.location_id(+)
GROUP BY l.city;
2) ANSI 표준
SELECT l.city, sum(e.salary) 급여총액, round(avg(e.salary)) 급여평균
FROM hr.employees e LEFT OUTER JOIN hr.departments d
ON e.department_id = d.department_id
LEFT OUTER JOIN hr.locations l
ON d.location_id = l.location_id
WHERE e.hire_date >= to_date('2007-01-01','yyyy-mm-dd')
AND e.hire_date < to_date('2008-01-01','yyyy-mm-dd')
GROUP BY l.city;
[문제] last_name에 a문자가 2개 이상 포함되어 있는 사원들의
last_name,salary,grade_level, department_name을 출력해주세요.
1) 오라클 전용
SELECT e.last_name, e.salary, j.grade_level, d.department_name
FROM hr.employees e, departments d, job_grades j
WHERE instr(last_name,'a',1,2) > 1 -- > 1 : 위치값이 2이상인가
ANDe.department_id = d.department_id
AND e.salary BETWEEN j.lowest_sal AND j.highest_sal
2) ANSI 표준
SELECT e.last_name, e.salary, j.grade_level, d.department_name
FROM hr.employees e JOIN departments d
ON e.department_id = d.department_id
JOIN job_grades j
ON e.salary BETWEEN j.lowest_sal AND j.highest_sal
AND instr(e.last_name,'a',1,2) >= 2;
[문제] 담당관리자(직속상관)보다 먼저 입사한 사원의 이름과 입사일 및
해당 관리자의 이름과 입사일 출력해주세요.
1) 오라클 전용
SELECT e.last_name, e.hire_date, m.last_name, m.hire_date
FROM hr.employees e, hr.employees m
WHERE e.manager_id = m.employee_id
AND m.hire_date > e.hire_date;
2) ANSI 표준
SELECT e.last_name, e.hire_date, m.last_name, m.hire_date
FROM hr.employees e JOIN hr.employees m
ON e.manager_id = m.employee_id
WHERE m.hire_date > e.hire_date;
'Oracle SQL' 카테고리의 다른 글
07 PIVOT, UNPIVOT, SCALAR SUBQUERY (0) | 2023.10.16 |
---|---|
06 SUBQUERY (0) | 2023.10.13 |
04 Null, Case, Decode, 그룹함수, GROUP BY, HAVING (0) | 2023.10.11 |
03 숫자함수, 날짜함수, 형변환함수 (0) | 2023.10.10 |
02 DISTINCT, WHERE, BETWEEN, IN, LIKE, SORT, FUNCTION (0) | 2023.10.06 |