1. 결과 집합 결합과 테이블 연결 (SET Operator & JOIN)
개념앞에서는 한 테이블 안에서 데이터를 조회하고, 조건을 걸고, 정렬하고, 집계하는 흐름을 봤다.
이제는 한 단계 더 나아가서 여러 조회 결과를 합치거나, 여러 테이블을 연결해서 함께 보는 방법을 알아야 한다.
이 파트에서 먼저 구분해야 할 것은 두 가지다.
UNION / UNION ALL
→ 두 쿼리 결과를 세로로 합치는 방식
JOIN
→ 두 개 이상의 테이블을 가로로 연결하는 방식
UNION / UNION ALL은 두 쿼리 결과를 행으로 합치는 것이고,JOIN은 두 개 이상의 테이블을 가로로 묶어서 하나의 결과 집합으로 만드는 것이다.
즉, 이 파트의 핵심은
결과를 합치는 것과, 테이블을 연결하는 것을 구분해서 이해하는 것이다.
1. UNION과 UNION ALL
개념
UNION과UNION ALL은 두 개의SELECT결과를 하나로 합치는 구문이다.
이때 중요한 점은 결과를 옆으로 붙이는 것이 아니라, 아래로 이어 붙인다는 것이다.
즉,
- 첫 번째
SELECT결과- 두 번째
SELECT결과를 하나의 결과 집합으로 세로 방향으로 합친다.
UNION / UNION ALL은 두 쿼리 결과를 행으로 합치는 것, 즉 세로로 묶는 방식이다.
UNION / UNION ALL로 결과를 합치려면, 두SELECT결과의 컬럼 개수와 순서는 같아야 하고, 각 위치의 데이터도 서로 비교 가능한 형식이어야 한다.
처음에는 같은 컬럼 하나를 조회하는 예제로 보면
UNION의 핵심인 결과를 세로로 합친다는 점이 가장 잘 보인다.
UNION과 UNION ALL의 차이
둘의 차이는 중복 처리 방식이다.
UNION
→ 양쪽 쿼리 결과를 합치되, 중복 결과는 한 번만 남긴다
UNION ALL
→ 양쪽 쿼리 결과를 합칠 때, 중복 결과도 그대로 모두 보여준다즉,
UNION은 중복을 정리해서 보여주는 방식이고,
UNION ALL은 결과를 있는 그대로 전부 이어 붙이는 방식이다.
세로로 합친다는 뜻 이해하기
UNION / UNION ALL은 테이블끼리 관계를 따라 연결하는 문장이 아니다.
이미 만들어진 두SELECT결과를 하나의 결과표로 이어 붙이는 문장이다.
예를 들어 첫 번째SELECT가 4행, 두 번째SELECT가 4행을 반환하면,
결과는 그 8행이 아래로 이어진 하나의 표가 된다.
즉,UNION은
결과와 결과를 합치는 방식이라고 이해하면 된다.
예제: 부서가 다른 직원 이름을 하나의 결과로 합치기
select ename from emp where deptno = 10 union select ename from emp where deptno = 20;
이 쿼리는 부서
10직원 이름 결과와
부서20직원 이름 결과를 하나의 결과로 합쳐서 보여준다.
여기서UNION을 쓰면
두 결과에 같은 값이 있더라도 한 번만 남는다.
예제: 중복까지 그대로 보고 싶을 때
select ename from emp where deptno = 10 union all select ename from emp where deptno = 20;
이번에는
UNION ALL을 썼기 때문에
중복이 있어도 제거하지 않고 그대로 보여준다.
즉,
- 결과를 정리해서 보고 싶으면
UNION- 결과를 있는 그대로 다 보고 싶으면
UNION ALL이렇게 구분하면 된다.
헷갈리기 쉬운 부분
UNION은JOIN과 다르다.
UNION
→ 결과를 세로로 합친다
JOIN
→ 테이블을 가로로 연결한다또
UNION은 중복을 제거하고,
UNION ALL은 중복도 그대로 포함한다.
즉,UNION / UNION ALL은
결과 집합을 합치는 문장이라고 이해하면 된다.
2. JOIN이 왜 필요한가
개념관계형 데이터베이스는 보통 모든 정보를 한 테이블에 몰아넣지 않는다.
왜냐하면 그렇게 하면 중복이 많아지고, 빈칸이 늘어나고, 수정도 불편해지기 때문이다.
데이터베이스의 테이블은 중복과 공간 낭비를 줄이고 데이터 무결성을 지키기 위해 여러 개로 나누어 저장되며, 이렇게 분리된 테이블은 서로 관계를 가진다.1:N관계가 가장 보편적이다.
즉, 먼저 테이블을 나누고,
필요할 때 다시 연결해서 함께 보는 방식이 관계형 데이터베이스의 기본 흐름이다.
하나의 표에 다 넣으면 왜 불편한가
처음에는 고객 정보와 구매 정보를 한 표에 다 넣는 것이 편해 보일 수 있다.
하지만 이렇게 되면 고객 정보와 구매 정보가 한 줄에 같이 섞이게 된다.
고객 이름, 주소, 연락처 같은 정보는 고객이 바뀌지 않는 이상 그대로인데,
구매 내역이 추가될 때마다 이 정보가 계속 반복될 수 있다.
공간 낭비가 왜 생기는가
이렇게 한 표에 다 넣으면
구매 내역이 없는 고객은 오른쪽 구매 관련 칸이 비게 된다.
즉, 데이터가 채워진 부분과 비어 있는 부분이 섞이면서
불필요한 빈칸이 많아지고, 표 구조도 비효율적이 된다.
그래서 테이블을 나눈다
그래서 관계형 데이터베이스에서는
고객 정보와 구매 정보를 역할에 따라 테이블을 나눈다.
예를 들면
- 고객 정보는
고객 테이블- 구매 정보는
구매 테이블이렇게 분리한다.
이렇게 하면
- 고객 정보 중복이 줄고
- 공간 낭비가 줄고
- 수정도 쉬워진다
즉, 테이블을 나누는 이유는
중복 제거와 구조 정리에 있다.
나눈 뒤에는 다시 연결해야 한다
테이블을 나누면 정보가 정리되는 대신,
필요할 때는 다시 연결해서 봐야 한다.
이때 연결 기준이 되는 것이PK와FK다.
PK
→ 각 행을 구분하는 기준값
FK
→ 다른 테이블의 기준값을 참조하는 연결값즉, 고객 테이블과 구매 테이블을 나눈 뒤에는
PK와FK를 통해 “누가 무엇을 샀는지”를 다시 연결해서 볼 수 있다.
이때JOIN이 필요해진다.
3. JOIN이란 무엇인가
개념
JOIN은 두 개 이상의 테이블을 가로로 연결해서 하나의 결과로 보는 구문이다.
예를 들어 직원 정보는emp테이블에 있고, 부서 정보는dept테이블에 있을 때,
직원 이름과 부서명을 함께 보려면 두 테이블을 연결해야 한다.
JOIN은 서로 관련 있는 컬럼을 기준으로
두 테이블의 정보를 한 줄에서 함께 보게 만드는 방식이라고 이해하면 된다.
가로로 연결한다는 뜻
이 그림에서 봐야 할 포인트는 단순하다.
UNION
→ 아래로 붙음
JOIN
→ 옆으로 붙음즉,
JOIN은
서로 관련 있는 컬럼을 기준으로
왼쪽 테이블과 오른쪽 테이블의 정보를 한 줄에 같이 보여주는 방식이다.
실제로 어떤 테이블들이 연결되는가
JOIN을 이해할 때는
실제로 어떤 테이블이 어떤 키로 연결되는지 보는 것도 중요하다.
예를 들어
emp
→ 직원 정보
dept
→ 부서 정보
locations
→ 지역 정보이렇게 나뉘어 있다면,
직원이 어느 부서에 속해 있는지,
그 부서가 어느 지역에 있는지까지
테이블을 따라가며 연결해서 볼 수 있다.
즉,ER 다이어그램은
나중에 어떤 컬럼으로JOIN해야 하는지 알려 주는 구조도라고 보면 된다.
4. INNER JOIN
개념
INNER JOIN은 두 테이블에서 조인 조건에 맞는 행만 남기는 조인이다.
이때 조인 조건은 보통ON이나USING()으로 작성한다.
ON은 어떤 컬럼끼리 연결할지 조건을 직접 적는 방식이다.
- 예를 들면
emp.deptno = dept.deptno처럼 쓰면서, 두 테이블의 어떤 값을 기준으로 연결할지 분명하게 보여준다.
USING()은 양쪽 테이블에 같은 이름의 공통 컬럼이 있을 때 더 짧게 쓰는 방식이다.
- 예를 들어 두 테이블에 모두
deptno가 있다면USING(deptno)처럼 작성할 수 있다.
또
INNER JOIN은INNER를 생략할 수 있다.
그래서INNER JOIN이라고 써도 되고, 그냥JOIN이라고만 써도 같은 의미다.
예를 들어 직원 테이블의deptno와 부서 테이블의deptno를 연결하면,
양쪽 테이블에 같은deptno가 있는 경우만 결과에 포함된다.
반대로 한쪽에는 값이 있는데 다른 쪽에는 대응되는 값이 없으면 결과에서 빠진다.
조인 기준으로 사용하는 컬럼 값이 서로 다르거나NULL이어도 연결되지 않는다.
즉,INNER JOIN은
두 테이블에서 공통으로 연결되는 데이터만 보는 방식이라고 이해하면 된다.예제: 직원과 부서를 연결해서 보기
select * from emp inner join dept on emp.deptno = dept.deptno;
이 쿼리는 직원 정보와 부서 정보를
deptno로 연결해서 함께 보여준다.
즉, 직원이 어느 부서에 속해 있는지 한 줄에서 같이 확인할 수 있다.
연습 단계에서는 전체 구조를 보기 위해select *를 써도 되지만,
실제로는 필요한 컬럼만 골라 쓰는 쪽이 더 읽기 쉽다.
예제: 같은 이름 컬럼이면
USING()도 가능하다select * from emp join dept using(deptno);
이 코드는 양쪽 테이블에 있는
deptno를 공통 기준으로 연결한 것이다.
같은 이름의 컬럼이 있을 때는ON대신USING()으로도 표현할 수 있다.
여기서 봐야 할 포인트
INNER JOIN은 연결에 성공한 행만 남긴다.
그래서
- 양쪽 테이블에 공통 값이 없거나
- 조인 기준 컬럼 값이 다르거나
- 조인 기준 컬럼이
NULL이면결과에서 제외될 수 있다.
또 문법도 함께 기억하면 좋다.
JOIN만 써도 기본은INNER JOINON은 조인 조건을 직접 쓰는 방식USING()은 같은 이름의 공통 컬럼이 있을 때 쓰는 방식즉,
INNER JOIN은
두 테이블에서 실제로 연결되는 데이터만 조회하는 가장 기본적인 조인 방식이다.
5. OUTER JOIN
개념
OUTER JOIN은 조건이 맞지 않는 행도 포함해서 보는 조인이다.
INNER JOIN은 연결되는 행만 남기기 때문에 조건이 맞지 않으면 결과에서 아예 사라진다. 하지만 실제로는 연결되지 않은 데이터도 함께 확인해야 할 때가 있다.
예를 들어 부서가 없는 직원도 같이 보고 싶을 수 있고, 반대로 소속 직원이 없는 부서도 함께 보고 싶을 수 있다.
이처럼 한쪽 테이블을 기준으로 조건이 맞지 않는 행도 결과에 남기고 싶을 때OUTER JOIN을 사용한다.
즉,OUTER JOIN은 조건이 맞지 않아도 기준에 따라 데이터를 남겨서 보는 방식이라고 이해하면 된다.
OUTER JOIN의 종류
OUTER JOIN은 어느 쪽을 기준으로 남길지에 따라 나뉜다.
LEFT OUTER JOIN
→ 왼쪽 테이블의 행은 모두 남긴다
RIGHT OUTER JOIN
→ 오른쪽 테이블의 행은 모두 남긴다
FULL OUTER JOIN
→ 양쪽 테이블의 행을 모두 남긴다즉, 왼쪽을 남길지, 오른쪽을 남길지, 양쪽 모두를 남길지에 따라 종류가 달라진다.
LEFT OUTER JOIN처음에는 가장 자주 보는
LEFT OUTER JOIN부터 이해하면 쉽다.
LEFT OUTER JOIN은 왼쪽 테이블의 행은 모두 남기고, 오른쪽 테이블에서 조건이 맞는 데이터만 붙이는 방식이다.
예를 들어emp를 왼쪽에 두고dept를 오른쪽에 두면, 직원 정보는 모두 남고 부서 정보는 연결되는 경우만 오른쪽에 붙는다.예제: 왼쪽 테이블 기준으로 모두 보기
select * from emp left outer join dept on emp.deptno = dept.deptno;
이 쿼리는
emp를 기준으로 보기 때문에 직원 정보는 모두 남고, 부서 정보는 연결되는 경우만 오른쪽에 붙는다.
즉,LEFT OUTER JOIN은 왼쪽 테이블 기준으로 결과를 유지하는 방식이다.
RIGHT OUTER JOIN
RIGHT OUTER JOIN은LEFT OUTER JOIN과 반대다.
즉, 오른쪽 테이블의 행은 모두 남기고, 왼쪽 테이블에서 조건이 맞는 데이터만 붙이는 방식이다.
예를 들어emp를 왼쪽에 두고dept를 오른쪽에 두면, 부서 정보는 모두 남고 직원 정보는 연결되는 경우만 왼쪽에 붙는다.예제: 오른쪽 테이블 기준으로 모두 보기
select * from emp right outer join dept on emp.deptno = dept.deptno;
이 쿼리는
dept를 기준으로 보기 때문에 부서 정보는 모두 남고, 직원 정보는 연결되는 경우만 왼쪽에 붙는다.
즉,RIGHT OUTER JOIN은 오른쪽 테이블 기준으로 결과를 유지하는 방식이다.
FULL OUTER JOIN
FULL OUTER JOIN은 왼쪽과 오른쪽 양쪽 테이블의 행을 모두 남기는 방식이다.
즉, 한쪽에만 있는 데이터도 빠지지 않고 결과에 포함한다.
다만,MySQL은FULL OUTER JOIN을 지원하지 않는다.
헷갈리기 쉬운 부분
INNER JOIN과OUTER JOIN의 차이는 조건이 맞지 않는 행을 결과에 남기느냐, 제외하느냐다.
INNER JOIN
→ 양쪽에 모두 연결되는 데이터만 남김
OUTER JOIN
→ 기준에 따라 연결되지 않는 데이터도 남길 수 있음
그리고
OUTER JOIN안에서도 기준이 다시 나뉜다.
LEFT OUTER JOIN
→ 왼쪽 기준
RIGHT OUTER JOIN
→ 오른쪽 기준
FULL OUTER JOIN
→ 양쪽 기준즉,
OUTER JOIN은 하나의 단일 방식이 아니라 어느 쪽 데이터를 남길지에 따라 갈라지는 조인 묶음이라고 이해하면 된다.
6. CROSS JOIN
개념
CROSS JOIN은 두 테이블의 가능한 조합을 모두 만드는 조인이다.
앞에서 본INNER JOIN이나LEFT OUTER JOIN은 보통 어떤 컬럼끼리 연결할지 기준이 있었다.
하지만CROSS JOIN은 그런 연결 조건 없이, 왼쪽 테이블의 각 행을 오른쪽 테이블의 모든 행과 하나씩 전부 짝지어 본다.
즉, 이 조인은
“서로 맞는 값만 연결한다”가 아니라
“가능한 경우를 전부 만들어 본다”에 가깝다.
예를 들어 왼쪽 테이블에 행이3개, 오른쪽 테이블에 행이4개있으면
결과는3 × 4 = 12개가 된다.
그래서CROSS JOIN은 결과 행 수가 빠르게 많아질 수 있다는 점도 같이 기억해야 한다.
핵심 특징
CROSS JOIN은ON절로 연결 조건을 주지 않는다.
조건으로 행을 걸러서 연결하는 조인이 아니라, 두 테이블의 행을 전부 조합해서 결과를 만들기 때문이다.
그래서 이 조인을 볼 때는 항상
왼쪽 행 수 × 오른쪽 행 수
이 흐름을 먼저 떠올리면 된다.
이 조인은 보통 테이블 관계를 따라 연결하는 용도보다는
가능한 조합을 만들어야 할 때 사용한다.
반대로, 관계형 조인처럼 생각하고 쓰면 필요 이상으로 많은 행이 나와서 결과가 이상해질 수 있다.
즉,CROSS JOIN은 관계를 따라 데이터를 연결하는 조인이 아니라,
두 테이블의 행을 조건 없이 전부 조합하는 조인이라고 이해하면 된다.
예제: 직원과 부서를 가능한 조합으로 모두 만들기
select ename, dname from emp cross join dept;
이 코드는
emp의 각 직원을dept의 모든 부서와 하나씩 짝지어 결과를 만든다.
여기서는emp.deptno = dept.deptno같은 연결 조건이 없기 때문에,
실제 소속 부서와 맞는 경우만 남기는 것이 아니라 가능한 조합 전체가 나온다.
예를 들어emp가14행이고dept가4행이면
결과는14 × 4 = 56행이 된다.
이처럼CROSS JOIN은 행 수가 곱으로 늘어난다는 점이 핵심이다.
헷갈리기 쉬운 부분
CROSS JOIN은INNER JOIN과 완전히 다르다.
INNER JOIN은
조건이 맞는 행만 연결한다.
반면CROSS JOIN은
조건 없이 전부 조합한다.
그래서 아래 두 쿼리는 겉보기는 비슷해도 의미가 완전히 다르다.select ename, dname from emp join dept on emp.deptno = dept.deptno;이 쿼리는 실제로 연결되는 직원과 부서만 출력한다.
select ename, dname from emp cross join dept;이 쿼리는 가능한 조합을 전부 출력한다.
즉, 하나는 관계대로 연결하는 것이고
다른 하나는 전체 조합을 만드는 것이다.
CROSS JOIN은
연결 조건이 없는 조인,
가능한 조합을 모두 만드는 조인이라고 이해하면 된다.
7. SELF JOIN
개념
SELF JOIN은 하나의 테이블을 자기 자신과 다시 연결해서 보는 조인이다.
말이 조금 낯설지만, 실제로는 한 테이블 안에 들어 있는 두 역할을 서로 연결하는 것이라고 생각하면 쉽다.
예를 들어emp테이블에는 직원 정보가 들어 있다.
그런데 이 테이블 안에는 직원 자신의 번호인empno도 있고,
그 직원의 매니저 번호인mgr도 함께 들어 있다.
즉, 한 테이블 안에 이런 식의 관계가 들어 있는 것이다.
empno: 자기 자신의 사번mgr: 나를 관리하는 매니저의 사번여기서 “직원의 매니저 이름”을 알고 싶다면,
직원 한 명의mgr값을 가지고
다시emp테이블의empno를 찾아야 한다.
결국 직원도emp테이블에 있고, 매니저도emp테이블에 있기 때문에
같은 테이블을 두 번 보는 방식이 필요하다.
이럴 때 사용하는 것이SELF JOIN이다.
왜 별칭이 꼭 필요한가
SELF JOIN에서는 같은 테이블을 두 번 사용하므로
각각의 역할을 구분하기 위해 별칭(alias)을 꼭 붙여야 한다.
예를 들어 아래처럼 보면 된다.
e: 직원 역할m: 매니저 역할실제 테이블은 둘 다
emp이지만,
쿼리 안에서는 역할이 다르기 때문에 이름을 따로 붙여 구분하는 것이다.
별칭이 없으면ename,empno같은 열이
어느 쪽emp를 가리키는지 알 수 없어서 헷갈리게 된다.
그래서SELF JOIN에서는 별칭이 거의 필수다.
예제: 직원의 매니저 이름 찾기
select e.ename as 직원이름, m.ename as 매니저이름 from emp e join emp m on e.mgr = m.empno;
이 코드는
emp테이블을 두 번 사용한다.
emp e: 직원 정보를 보는 쪽emp m: 매니저 정보를 보는 쪽그리고
on e.mgr = m.empno는
직원의 매니저 번호(mgr)와 매니저의 사번(empno)를 연결하라는 뜻이다.
즉, 흐름은 이렇게 이해하면 된다.
1. 먼저 직원 한 명을 본다.
2. 그 직원의mgr값을 확인한다.
3. 그 번호와 같은empno를 가진 사람을 같은emp테이블에서 다시 찾는다.
4. 그 사람이 바로 매니저다.그래서
e.ename은 직원 이름이 되고,
m.ename은 그 직원의 매니저 이름이 된다.
핵심 특징
SELF JOIN은 다른 테이블과 연결하는 조인이 아니라
같은 테이블 안의 서로 다른 행끼리 연결하는 조인이다.
주로 한 테이블 안에 다음과 같은 구조가 있을 때 사용한다.
- 직원 ↔ 매니저
- 부모 글 ↔ 답글
- 상위 부서 ↔ 하위 부서
즉, 한 테이블 안에 참조 관계나 계층 구조가 들어 있을 때
SELF JOIN이 필요하다.
헷갈리기 쉬운 부분
SELF JOIN은 테이블이 두 개 있는 것이 아니다.
테이블은 하나인데, 그 하나를 두 역할로 나눠서 보는 것이다.
또한INNER JOIN으로 작성하면
mgr값이 없는 직원은 결과에서 빠질 수 있다.
예를 들어 최고 관리자처럼 매니저가 없는 직원은 연결할 대상이 없기 때문이다.
여기서 봐야 할 포인트
SELF JOIN의 핵심은
같은 테이블을 두 번 쓰는 것 자체가 아니라, 같은 테이블 안에 있는 두 역할을 연결해서 보는 것이다.
그래서SELF JOIN을 볼 때는
“왜 같은 테이블을 두 번 썼지?”보다
“이 테이블 안에서 어떤 역할과 어떤 역할을 연결하려는 거지?”를 먼저 보면 이해가 쉬워진다.
8. JOIN 종류 한눈에 보기
이 그림은 지금까지 본 내용을 한 번에 다시 정리할 때 보는 참고 이미지다.
앞에서는 조인을 하나씩 따로 봤다면, 여기서는 각 조인이 무엇을 남기고 어떻게 연결하는지만 다시 정리해서 보면 된다.
지금 단계에서는 아래 흐름으로 다시 보면 된다.
INNER JOIN
→ 양쪽 테이블에 모두 연결되는 데이터만 남긴다
OUTER JOIN
→ 조건이 맞지 않는 데이터도 기준에 따라 함께 남길 수 있다
LEFT OUTER JOIN
→ 왼쪽 테이블 기준으로 남긴다
RIGHT OUTER JOIN
→ 오른쪽 테이블 기준으로 남긴다
FULL OUTER JOIN
→ 양쪽 테이블의 데이터를 모두 남기는 방식이다
다만,MySQL은FULL OUTER JOIN을 지원하지 않는다
CROSS JOIN
→ 가능한 조합을 모두 만든다
SELF JOIN
→ 같은 테이블을 자기 자신과 다시 연결한다즉,
JOIN은 전부 같은 방식이 아니라
무엇을 기준으로 남길지,
조건이 맞지 않는 데이터를 포함할지,
아예 가능한 조합을 전부 만들지에 따라 종류가 나뉜다고 이해하면 된다.
헷갈리기 쉬운 부분첫째,
UNION과JOIN은 완전히 다르다.
UNION
→ 결과를 세로로 합친다
JOIN
→ 테이블을 가로로 연결한다
둘째,UNION과UNION ALL의 차이는 중복 처리다.UNION
→ 중복 제거
UNION ALL
→ 중복도 그대로 포함
셋째,INNER JOIN은 연결되는 행만 남긴다.
조건이 맞지 않거나 조인 컬럼 값이 없으면 결과에서 빠질 수 있다.
넷째,OUTER JOIN은 조건이 맞지 않는 행도 기준에 따라 남길 수 있다.
그리고 그 안에서 다시 기준이 나뉜다.LEFT OUTER JOIN
→ 왼쪽 기준
RIGHT OUTER JOIN
→ 오른쪽 기준
FULL OUTER JOIN
→ 양쪽 기준
다만,MySQL은FULL OUTER JOIN을 지원하지 않는다
다섯째,CROSS JOIN은 관계를 따라 연결하는 조인이 아니다.
조건 없이 가능한 조합을 전부 만드는 조인이다.
여섯째,SELF JOIN은 같은 테이블을 두 번 쓰는 것이지, 테이블이 두 개 있는 것이 아니다.
같은 테이블 안에 들어 있는 관계를 역할로 나눠서 연결하는 방식이다.
짧게 흐름으로 다시 보기
UNION / UNION ALL
→ 두SELECT결과를 세로로 합치기
JOIN
→ 두 개 이상의 테이블을 가로로 연결하기
INNER JOIN
→ 연결되는 데이터만 보기
OUTER JOIN
→ 조건이 맞지 않는 데이터도 기준에 따라 함께 보기
LEFT OUTER JOIN
→ 왼쪽 테이블 기준으로 모두 보기
RIGHT OUTER JOIN
→ 오른쪽 테이블 기준으로 모두 보기
FULL OUTER JOIN
→ 양쪽 데이터를 모두 보는 방식이지만,MySQL은 지원하지 않음
CROSS JOIN
→ 가능한 조합을 모두 만들기
SELF JOIN
→ 같은 테이블을 자기 자신과 다시 연결하기즉, 이 파트의 핵심은
결과를 합치는 방식과, 테이블을 연결하는 방식을 구분해서 이해하는 것,
그리고 조인 종류마다 무엇을 남기고 무엇을 제외하는지가 다르다는 점이다.
문제 해석은 이렇게 한다
UNION,UNION ALL,JOIN,OUTER JOIN,ROLLUP문제는 문장을 한 번에 보면 헷갈리기 쉽다.
그래서 바로 SQL을 쓰지 말고, 먼저 문제 문장을 그대로 잘라서 읽어야 한다.
순서는 보통 이렇다.
1. 결과를 세로로 합치는 문제인지
2. 테이블을 가로로 연결하는 문제인지
3. 어느 테이블을 기준으로 남겨야 하는지
4. 어떤 컬럼으로 연결해야 하는지
5. 원본 행을 먼저 거를 조건인지
6. 출력 결과를 가공해야 하는지
7. 정렬까지 필요한지즉, 이런 문제는 보통
세로 결합인지 가로 연결인지 → 어떤 테이블을 써야 하는지 → 무엇으로 연결해야 하는지 → 먼저 거를 조건인지 → 출력 모양을 어떻게 만들지 → 어떻게 정렬할지
이 순서로 읽으면 된다. 마지막 실습 파일도1~2번은course1,course2,3번은 집계,4번 이후는emp,dept,locations,salgrade를 활용하는 구조다.
1. course1 을 수강하는 학생들과 course2 를 수강하는 학생들의 이름, 나이를 출력하는데 나이가 적은 순으로 출력하시오. 단, 두 코스를 모두 수강하는 학생들의 정보는 한 번만 출력한다.
course1 을 수강하는 학생들과 course2 를 수강하는 학생들의
→course1결과와course2결과를 함께 봐야 한다는 뜻이다.
이름, 나이를 출력하는데
→ 출력해야 하는 컬럼은name,age다.
즉, 양쪽SELECT의 컬럼 개수와 순서를 같게 맞춰야 한다.
나이가 적은 순으로 출력하시오
→ 마지막에age기준 오름차순 정렬이 필요하다.
단, 두 코스를 모두 수강하는 학생들의 정보는 한 번만 출력한다
→ 양쪽 결과에 같은 학생 정보가 있더라도 한 번만 남겨야 한다는 뜻이다.
즉, 여기서는UNION을 떠올리면 된다.
두 결과를 세로로 합치되 중복은 제거하는 문제다. 마지막 실습 파일의 예시 결과도고길동, 듀크, 둘리, 또치, 마이콜, 도우너, 유니코순으로 한 번씩만 나온다.
정답 코드select name, age from course1 union select name, age from course2 order by age;
2. course1 을 수강하는 학생들과 course2 를 수강하는 학생들의 이름, 전화 번호 그리고 나이를 출력하는데 나이가 많은 순으로 출력하시오. 단, 두 코스를 모두 수강하는 학생들의 정보를 중복해서 출력한다.
course1 을 수강하는 학생들과 course2 를 수강하는 학생들의
→course1결과와course2결과를 함께 봐야 한다는 뜻이다.
이름, 전화 번호 그리고 나이를 출력하는데
→ 출력해야 하는 컬럼은name,phone,age다.
즉, 양쪽SELECT의 컬럼 개수와 순서를 같게 맞춰야 한다.
나이가 많은 순으로 출력하시오
→ 마지막에age기준 내림차순 정렬이 필요하다.
단, 두 코스를 모두 수강하는 학생들의 정보를 중복해서 출력한다
→ 이번에는 중복을 제거하면 안 된다는 뜻이다.
즉, 여기서는UNION ALL을 떠올리면 된다.
두 결과를 세로로 합치되 중복도 그대로 남기는 문제다. 마지막 실습 파일 예시도또치,둘리처럼 두 코스에 모두 있는 학생 정보가 중복되어 출력된다.
정답 코드select name, phone, age from course1 union all select name, phone, age from course2 order by age desc;
3. 직무별 그리고 입사년도별 직원들 수를 출력하는데 직무별 직원수(소합계)와 전체 직원수(전체 합계)도 함께 출력한다.
직무별 그리고 입사년도별
→job과year(hiredate)를 기준으로 묶어야 한다는 뜻이다.
직원들 수를 출력하는데
→ 각 그룹마다count(*)를 세면 된다.
직무별 직원수(소합계)
→ 직무별로 한 번 더 묶인 소합계 행도 같이 나와야 한다는 뜻이다.
전체 직원수(전체 합계)도 함께 출력한다
→ 마지막에 전체 합계 행까지 같이 나와야 한다는 뜻이다.
이 문제는 단순GROUP BY가 아니라
상세 그룹 + 소합계 + 전체 합계를 같이 구하는ROLLUP문제다.
마지막 실습 파일 안에는 여기 SQL이group by job;로 들어가 있지만, 문제 예시처럼 직무별 소합계와 전체 합계를 함께 내려면with rollup이 필요하다.
정답 코드select job as 직무, year(hiredate) as 입사년도, count(*) as 직원수 from emp group by job, year(hiredate) with rollup;
4. RESEARCH 부서에서 근무하는 직원의 이름, 직무, 부서이름을 출력하시오.
RESEARCH 부서에서 근무하는
→ 부서이름이RESEARCH인 직원만 골라야 한다는 뜻이다.
직원의 이름, 직무
→emp테이블에 있다.
부서이름을 출력하시오
→dept테이블에 있다.
즉,emp와dept를 조인해야 한다.
정답 코드select ename as 이름, job as 직무, dname as 부서이름 from emp join dept on emp.deptno = dept.deptno where dname = 'RESEARCH';
5. 이름에 'A'가 들어가는 직원들의 이름과 부서이름을 출력하시오.
이름에 'A'가 들어가는 직원들의
→ 직원 이름에A가 포함된 행만 찾아야 한다는 뜻이다.
즉,ename like '%A%'조건을 떠올리면 된다.
이름
→emp테이블 정보다.
부서이름을 출력하시오
→dept테이블 정보다.
즉,emp와dept를 조인해야 한다.
정답 코드select ename as 이름, dname as 부서이름 from emp join dept on emp.deptno = dept.deptno where ename like '%A%';
6. 직원이름과 그 직원이 속한 부서의 부서명, 그리고 급여를 출력하는데 급여가 3000이상인 직원을 출력하시오.
직원이름
→emp테이블에서 가져와야 한다.
그 직원이 속한 부서의 부서명
→dept테이블에서 가져와야 한다.
즉,emp와dept를 연결해야 한다.
그리고 급여를 출력하는데
→ 급여sal도 같이 출력해야 한다.
급여가 3000이상인 직원을 출력하시오
→ 집계 결과 조건이 아니라 원본 행 조건이다.
즉,where sal >= 3000이다. 실습 예시도SCOTT,KING,FORD세 명만 나오고 급여는3,000원,5,000원,3,000원형태다.
정답 코드select ename as 직원이름, dname as 부서명, concat(format(sal, 0), '원') as 급여 from emp join dept on emp.deptno = dept.deptno where sal >= 3000;
7. 커미션이 책정된 직원들의 직원번호, 이름, 연봉, 연봉커미션, 급여등급을 출력하되, 각각의 컬럼명을 '직원번호', '직원이름', '연봉','실급여', '급여등급'으로 하여 출력하시오. 또한 실급여가 적은 순으로 출력하시오.
커미션이 책정된 직원들의
→comm이NULL이 아닌 직원만 보라는 뜻이다.
즉,where comm is not null조건이 필요하다.
직원번호, 이름
→emp테이블에서 가져온다.
연봉
→sal * 12로 계산하면 된다.
연봉커미션
→ 문제 문장 표현은 조금 어색하지만, 출력 예시를 보면 실제로는 연봉 + 커미션 값, 즉실급여를 의미한다.
예를 들어WARD는 연봉15000, 실급여15200,KING은 연봉60000, 실급여63500으로 제시되어 있다.
급여등급
→salgrade테이블을 조인해서 구해야 한다.
즉,sal between losal and hisal조건으로 연결해야 한다.
또한 실급여가 적은 순으로 출력하시오
→ 마지막에 계산식 기준 오름차순 정렬이 필요하다.
정답 코드select empno as 직원번호, ename as 직원이름, sal * 12 as 연봉, sal * 12 + ifnull(comm, 0) as 실급여, grade as 급여등급 from emp join salgrade on sal between losal and hisal where comm is not null order by sal * 12 + ifnull(comm, 0);
8. 부서번호가 10번인 직원들의 부서번호, 부서이름, 직원이름, 급여, 급여등급을 출력하시오.
부서번호가 10번인 직원들의
→ 먼저deptno = 10인 직원만 남기라는 뜻이다.
부서번호, 부서이름
→ 부서번호는emp에도 있지만, 부서이름은dept에 있다.
즉,emp와dept를 연결해야 한다.
직원이름, 급여
→emp에서 가져오면 된다.
급여등급을 출력하시오
→salgrade테이블을 한 번 더 조인해야 한다.
정답 코드select emp.deptno as 부서번호, dname as 부서이름, ename as 직원이름, sal as 급여, grade as 급여등급 from emp join dept on emp.deptno = dept.deptno join salgrade on sal between losal and hisal where emp.deptno = 10;
9. 직무가 'SALESMAN'인 직원들의 직무와 그 직원이름, 그리고 그 직원이 속한 부서 이름을 출력하시오.
직무가 'SALESMAN'인 직원들의
→ 원본 행 조건이므로where job = 'SALESMAN'이다.
직무와 그 직원이름
→emp테이블 정보다.
그 직원이 속한 부서 이름
→dept테이블 정보다.즉,
emp와dept를 조인해야 한다.
정답 코드select job as 직무, ename as 직원이름, dname as 부서이름 from emp join dept using(deptno) where job = 'SALESMAN';
10. 부서번호가 10번, 20번인 직원들의 부서번호, 부서이름, 직원이름, 급여, 급여등급을 출력하시오. 그리고 그 출력된 결과물을 부서번호가 낮은 순으로, 급여가 많은 순으로 정렬하시오.
부서번호가 10번, 20번인 직원들의
→deptno in (10, 20)조건이 필요하다는 뜻이다.
부서번호, 부서이름, 직원이름, 급여
→emp와dept를 조인해야 한다는 뜻이다.
급여등급을 출력하시오
→salgrade테이블도 함께 조인해야 한다.
부서번호가 낮은 순으로
→ 먼저deptno asc정렬이 필요하다.
급여가 많은 순으로 정렬하시오
→ 같은 부서 안에서는sal desc정렬이 필요하다. 실습 파일도 10번 부서가 먼저 나오고, 그 안에서KING,CLARK,MILLER순으로 정렬되어 있다.
정답 코드select emp.deptno as 부서번호, dname as 부서이름, ename as 직원이름, sal as 급여, grade as 급여등급 from emp join dept using(deptno) join salgrade on sal between losal and hisal where emp.deptno in (10, 20) order by emp.deptno, sal desc;
11. 사원들의 이름, 부서번호, 부서이름을 출력하시오. 단, 직원이 없는 부서도 출력하며 이경우 이름을 '없음'이라고 출력한다. 부서번호별로 정렬한다.
사원들의 이름, 부서번호, 부서이름을 출력하시오
→ 기본적으로 직원과 부서를 함께 봐야 하므로emp와dept를 연결해야 한다.
단, 직원이 없는 부서도 출력하며
→ 이번에는 부서 쪽 데이터가 반드시 남아야 한다는 뜻이다.
즉,dept를 기준으로 결과를 유지해야 한다.
이경우 이름을 '없음'이라고 출력한다
→ 연결되는 직원이 없어서ename이NULL이면ifnull(ename, '없음')으로 바꿔야 한다.
부서번호별로 정렬한다
→dept.deptno기준 정렬이 필요하다. 실습 예시도40 OPERATIONS,50 INSA가 직원 없이없음으로 출력된다.
부서 기준OUTER JOIN문제다.dept를 왼쪽에 두고left outer join으로 쓰면, 초보자가 “부서를 남기는 조인”이라는 흐름을 더 바로 읽기 쉽다. 기존JOIN정리도LEFT OUTER JOIN은 왼쪽 테이블 기준 유지라고 설명하고 있다.
정답 코드select ifnull(ename, '없음') as 이름, dept.deptno as 부서번호, dname as 부서이름 from dept left outer join emp using(deptno) order by dept.deptno;
12. 직원들의 이름, 부서번호, 부서이름을 출력하시오. 단, 아직 부서 배치를 못받은 직원도 출력하며 이경우 부서번호와 부서명은 null 로 출력한다. 또한 직원들의 이름순으로 정렬한다.
직원들의 이름, 부서번호, 부서이름을 출력하시오
→ 직원과 부서를 함께 봐야 하므로emp와dept를 연결해야 한다.
단, 아직 부서 배치를 못받은 직원도 출력하며
→ 이번에는 직원 쪽 데이터가 반드시 남아야 한다는 뜻이다.
즉,emp를 기준으로 결과를 유지해야 한다.
이경우 부서번호와 부서명은 null 로 출력한다
→ 연결되는 부서가 없으면 그대로NULL로 두면 된다.
또한 직원들의 이름순으로 정렬한다
→ename기준 정렬이 필요하다. 실습 예시에서도ADAMS가NULL, NULL로 포함되어 있다.
직원 기준OUTER JOIN문제다.
정답 코드select ename as 이름, dept.deptno as 부서번호, dname as 부서이름 from emp left outer join dept on emp.deptno = dept.deptno order by ename;
13번부터는dept,locations,salgrade까지 같이 보는 문제다.
여기서는dept테이블의loc_code와locations테이블의loc_code를 연결해서
부서가 어느 도시에 속하는지 함께 확인하면 된다.
13. 커미션이 정해진 모든 직원의 이름, 커미션, 부서이름, 도시명을 출력하시오.
커미션이 정해진 모든 직원의
→comm이NULL이 아닌 직원만 보라는 뜻이다.
출력 예시를 보면0도 포함되므로,0은 제외하면 안 되고NULL만 제외해야 한다.
이름, 커미션
→emp테이블 정보다.
부서이름
→dept테이블 정보다.
도시명을 출력하시오
→locations테이블 정보다.
즉,emp → dept → locations순서로 연결해야 한다. 실습 예시도KING-SEOUL,JONES-DALLAS,ALLEN/WARD/MARTIN/TURNER-CHICAGO형태다.
정답 코드select ename as 직원명, comm as 커미션, dname as 부서명, city as 도시명 from emp join dept using(deptno) join locations using(loc_code) where comm is not null;
14. DALLAS에서 근무하는 사원의 이름, 급여, 등급을 출력하시오.
DALLAS에서 근무하는 사원의
→ 근무 도시가DALLAS인 사원만 골라야 한다는 뜻이다.
즉,locations테이블까지 연결해야 한다.
이름, 급여
→emp테이블 정보다.
등급을 출력하시오
→ 급여 등급은salgrade테이블 정보다.
즉,sal between losal and hisal조건으로 연결해야 한다. 실습 예시도SMITH 800 1,JONES 2975 4,SCOTT 3000 4,FORD 3000 4로 제시되어 있다.
정답 코드select ename as 이름, sal as 급여, grade as 등급 from emp join dept using(deptno) join locations using(loc_code) join salgrade on sal between losal and hisal where city = 'DALLAS' order by sal;
15. 사원들의 이름, 부서번호, 부서명을 출력하시오. 단, 직원이 없는 부서도 출력하며 이경우 직원 이름을 '누구?'라고 출력한다. 아직 부서 배치를 못받은 직원도 출력하며 부서 번호와 부서 이름을 '어디?' 이라고 출력한다. (16행) 부서명을 기준으로 정렬한다.
사원들의 이름, 부서번호, 부서명을 출력하시오
→ 기본적으로 직원과 부서를 함께 봐야 한다는 뜻이다.
단, 직원이 없는 부서도 출력하며
→ 부서 쪽에만 있는 데이터도 남겨야 한다는 뜻이다.
아직 부서 배치를 못받은 직원도 출력하며
→ 직원 쪽에만 있는 데이터도 남겨야 한다는 뜻이다.
즉, 이 문제는
양쪽에서 연결되지 않는 데이터까지 모두 남겨야 하는 문제다.
개념으로 보면FULL OUTER JOIN이 가장 잘 맞는다.
하지만 기존 정리에서 본 것처럼MySQL은FULL OUTER JOIN을 지원하지 않는다. 그래서 이런 문제는 보통LEFT OUTER JOIN결과와RIGHT OUTER JOIN결과를UNION으로 합쳐서 해결한다.
이경우 직원 이름을 '누구?'라고 출력한다
→ 직원 이름이NULL이면누구?로 바꿔야 한다.
부서 번호와 부서 이름을 '어디?' 이라고 출력한다
→ 부서가 연결되지 않으면 부서번호와 부서명을어디?로 바꿔야 한다.
부서명을 기준으로 정렬한다
→ 마지막에 부서명 기준 정렬이 필요하다.
여기서UNION ALL이 아니라UNION을 쓰는 이유는,
양쪽 조인 결과에서 겹치는 행은 한 번만 남겨야 하기 때문이다. 기존 정리에서도UNION은 중복 제거,UNION ALL은 중복 유지로 설명하고 있다.
정답 코드select ifnull(ename, '누구?') as 직원명, ifnull(cast(dept.deptno as char), '어디?') as 부서번호, ifnull(dname, '어디?') as 부서명 from emp left outer join dept on emp.deptno = dept.deptno union select ifnull(ename, '누구?') as 직원명, ifnull(cast(dept.deptno as char), '어디?') as 부서번호, ifnull(dname, '어디?') as 부서명 from emp right outer join dept on emp.deptno = dept.deptno order by 부서명;
16. 'CHICAGO' 에서 근무하는 직원들의 이름, 입사일, 급여를 출력한다. (서브쿼리로 해결한다.)
'CHICAGO' 에서 근무하는 직원들의
→CHICAGO라는 도시 정보에서 시작해서, 그 도시에 있는 부서를 찾고, 그 부서에 속한 직원을 찾아야 한다는 뜻이다.
이름, 입사일, 급여를 출력한다
→ 이 값들은 최종적으로emp테이블에서 가져와야 한다.
(서브쿼리로 해결한다.)
→ 이번에는 조인으로 직접 연결하지 말고,
지역 → 부서 → 직원 흐름을 서브쿼리로 풀라는 뜻이다. 실습 파일도 16번은 서브쿼리, 17번은 조인으로 나눠 제시하고 있다.
정답 코드select ename, hiredate, sal from emp where deptno in ( select deptno from dept where loc_code in ( select loc_code from locations where city = 'CHICAGO' ) );
17. 'CHICAGO' 에서 근무하는 직원들의 이름, 입사일, 부서명을 출력한다. (조인로 해결한다.)
'CHICAGO' 에서 근무하는 직원들의
→ 역시 도시 조건은locations에서 확인해야 한다는 뜻이다.
이름, 입사일
→emp테이블 정보다.
부서명을 출력한다
→dept테이블 정보다.
(조인로 해결한다.)
→ 16번처럼 서브쿼리로 풀지 말고,
이번에는emp,dept,locations를 직접 연결해서 풀라는 뜻이다. 실습 파일 예시도 결과는SALES부서 직원 6명이다.
정답 코드select ename, hiredate, dname from emp join dept using(deptno) join locations using(loc_code) where city = 'CHICAGO';
18. 'DALLAS' 에서 근무하는 직원들의 이름, 연봉, 부서명을 연봉이 큰 순으로 출력하는데 연봉의 계산은 (급여+커미션)*12 을 적용하는데 커미션이 정해지지않은 직원은 0으로 계산한다.
'DALLAS' 에서 근무하는 직원들의
→ 도시가DALLAS인 직원만 보라는 뜻이다.
즉,locations까지 연결해야 한다.
이름, 연봉, 부서명을
→ 이름은emp, 부서명은dept에서 가져온다.
연봉은 계산해서 만들어야 한다.
연봉의 계산은 (급여+커미션)*12 을 적용하는데
→ 여기서는sal * 12 + comm이 아니라
반드시(sal + ifnull(comm, 0)) * 12흐름으로 읽어야 한다는 뜻이다.
실습 예시에서JONES의 연봉이36060으로 제시된 것도(2975 + 30) * 12로 계산해야 맞는다.
커미션이 정해지지않은 직원은 0으로 계산한다
→ifnull(comm, 0)이 필요하다.
연봉이 큰 순으로 출력
→ 마지막에 계산된 연봉 기준 내림차순 정렬이 필요하다.
정답 코드select ename as 이름, (sal + ifnull(comm, 0)) * 12 as 연봉, dname as 부서명 from emp join dept using(deptno) join locations using(loc_code) where city = 'DALLAS' order by (sal + ifnull(comm, 0)) * 12 desc;
19 도시명 'SEOUL' 에서 근무중인 직원들의 인원을 출력하시오.
도시명 'SEOUL' 에서 근무중인 직원들의
→ 근무 도시가SEOUL인 직원만 골라야 한다는 뜻이다.
즉,locations까지 연결해야 한다.
인원을 출력하시오
→ 직원 수를 세야 하므로count(*)를 떠올리면 된다.
실습 예시도 결과를3명으로 제시하고 있다.
정답 코드select concat(count(*), '명') as 인원수 from emp join dept using(deptno) join locations using(loc_code) where city = 'SEOUL';