Chap.7-3 SQL 고급[조인] - 조인(join), INNER JOIN(내부 조인), OUTER JOIN(외부조인), CROSS JOIN(상호 조인), SELF JOIN(자체 조인), UNION / UNION ALL / NOT IN / IN
조인
조인(join), INNER JOIN(내부 조인), OUTER JOIN(외부조인), CROSS JOIN(상호 조인), SELF JOIN(자체 조인), UNION / UNION ALL / NOT IN / IN
조인(join)
-
두 개 이상의 테이블을 서로 묶어서 하나의 결과 집합으로 만들어 내는 것
-
관계형 테이블의 가장 큰 특징
-
테이블 간의 관계
▪ 1:1 관계
▪ 1:N 관계 - ☆
▪ N:M 관계
-
가장 많이 사용되는 조인
-
공통 컬럼을 기반으로 결합
▪ 부모 테이블 : Primary Key
▪ 자식 테이블 : Foreign Key(부모 테이블의 PK 값 참조)
[형식]
SELECT <열 목록>
FROM <첫 번째 테이블>
INNER JOIN <두 번째 테이블>
ON <조인될 조건>
[WHERE 검색 조건]
INNER JOIN(내부 조인)
USE sqlDB;
SELECT *
FROM buyTBL
INNER JOIN userTBL
ON buyTBL.userID = userTBL.userID
WHERE buyTBL.userID = 'BBK'; -- 이름이 중복되면 테이블 명과 같이 줘야한다.
결과
메커니즘
전부 출력
USE sqlDB;
SELECT *
FROM buyTBL
INNER JOIN userTBL -- 있는 것만 출력된다.
ON buyTBL.userID = userTBL.userID;
결과
SELECT userID, name, prodName, addr, CONCAT(mobile1, mobile2) AS '연락처'
FROM buytbl
INNER JOIN usertbl
ON buyTBL.userID = userTBL.userID ;
결과
오류 (userID가 중복된다.)
필요한 것만 출력
SELECT buyTBL.userID, name, prodName, addr, CONCAT(mobile1,mobile2) AS '연락처'
-- 어디서 충돌할지 몰라 다 나열한 것
FROM buyTBL
INNER JOIN userTBL
ON buyTBL.userID = userTBL.userID ;
결과
Table을 포함 시켜야 중복되지 않는다.
Table 모두 지정
SELECT buyTBL.userID, userTBL.name, buyTBL.prodName, userTBL.addr,
CONCAT(userTBL.mobile1, userTBL.mobile2) AS '연락처'
FROM buyTBL
INNER JOIN userTBL
ON buyTBL.userID = userTBL.userID;
결과
Table 간략하게
SELECT B.userID, U.name, B.prodName, U.addr,
CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM buyTBL B
INNER JOIN userTBL U
ON B.userID = U.userID;
WHERE절 이용
SELECT B.userID, U.name, B.prodName, U.addr,
CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM buyTBL B
INNER JOIN userTBL U
ON B.userID = U.userID
WHERE B.userID = 'JYP';
결과
ORDER BY 이용
SELECT B.userID, U.name, B.prodName, U.addr,
CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM buyTBL B
INNER JOIN userTBL U
ON B.userID = U.userID
WHERE B.userID = 'JYP'
ORDER BY U.userID;
결과
구매 이력이 있는 회원만 출력됨
JOIN 이용 확인
SELECT DISTINCT U.userID, U.name, U.addr
FROM userTBL U
INNER JOIN buyTBL B
ON U.userID = B.userID
ORDER BY U.userID ;
결과
SubQuery 이용 확인
SELECT U.userID, U.name, U.addr
FROM userTBL U
WHERE EXISTS (
SELECT *
FROM buyTBL B
WHERE U.userID = B.userID );
결과
예제
지역별 배송 수량, 매출액 출력
SELECT U.addr, SUM(B.amount) '수량', SUM(B.amount*B.price) '매출액'
FROM usertbl U
INNER JOIN buytbl B
ON B.userID = U.userID
GROUP BY U.addr;
결과
-
3개의 테이블 조인
테스트 데이터
USE sqlDB;
CREATE TABLE stdTBL(
stdName VARCHAR(10) NOT NULL PRIMARY KEY, -- NULL을 허용하지 않음
addr CHAR(4) NOT NULL
);
CREATE TABLE clubTBL(
clubName VARCHAR(10) NOT NULL PRIMARY KEY,
roomNo CHAR(4) NOT NULL
);
CREATE TABLE stdclubTBL(
num int AUTO_INCREMENT NOT NULL PRIMARY KEY,
stdName VARCHAR(10) NOT NULL,
clubName VARCHAR(10) NOT NULL, -- 컬럼이 다 정의되어야 한다.
FOREIGN KEY(stdName) REFERENCES stdTBL(stdName), -- ☆☆☆
FOREIGN KEY(clubName) REFERENCES clubTBL(clubName) -- ☆☆☆
);
INSERT INTO stdTBL VALUES
(N'김범수', N'경남'), (N'성시경', N'서울'), (N'조용필', N'경기'),
(N'은지원', N'경북'), (N'바비킴', N'서울');
INSERT INTO clubTBL VALUES
(N'수영', N'101호'), (N'바둑', N'102호'), (N'축구', N'103호'),
(N'봉사', N'104호');
INSERT INTO stdclubTBL VALUES
(NULL, N'김범수', N'바둑'), (NULL, N'김범수', N'축구'),
(NULL, N'조용필', N'축구'), (NULL, N'은지원', N'축구'),
(NULL, N'은지원', N'봉사'), (NULL, N'바비킴', N'봉사');
3개의 테이블 조인
SELECT S.stdName, S.addr, C.clubName, C.roomNo
FROM stdTBL S
INNER JOIN stdclubTBL SC
ON S.stdName = SC.stdName
INNER JOIN clubTBL C
ON SC.clubName = C.clubName
ORDER BY S.stdName;
결과
OUTER JOIN(외부조인)
-
내부 조인은 값이 있는 행 만을 대상으로 함
-
OUTER JOIN은 모든 행에 대해 조인을 함
[형식]
SELECT <열 목록>
FROM <첫 번째 테이블(LEFT 테이블)>
<LEFT | RIGHT | FULL> OUTER JOIN <두 번째 테이블(RIGHT 테이블)>
ON <조인될 조건>
[WHERE 검색 조건];
- LEFT JOIN : 왼쪽 테이블의 모든 행을 기준으로 조인
- RIGHT JOIN : 오른쪽 테이블의 모든 행을 기준으로 조인
- FULL : 두 테이블의 모든 행에 대해 조인
Outer Join은 조인의 조건에 만족되지 않는 행까지도 포함시킨다.
LEFT OUTER JOIN
USE sqlDB;
SELECT U.userID, U.name, B.prodName, U.addr,
CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM userTBL U
LEFT OUTER JOIN buyTBL B
ON U.userID = B.userID
ORDER BY U.userID;
결과
RIGHT OUTER JOIN
SELECT U.userID, U.name, B.prodName, U.addr,
CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM buyTBL B
RIGHT OUTER JOIN userTBL U
ON U.userID = B.userID
ORDER BY U.userID;
결과
구매이력이 없는 사용자만 출력
SELECT U.userID, U.name, B.prodName, U.addr, CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM userTBL U
LEFT OUTER JOIN buyTBL B
ON U.userID = B.userID
WHERE B.prodName IS NULL -- B.prodName = NULL 이렇게 하면 안된다!
ORDER BY U.userID;
-- IS NOT 이었으면 구매이력이 있는 사람들
결과
예제
모든 사용자에 대한 구매액
SELECT U.userID, U.name, SUM(B.price*B.amount) AS '총구매액'
FROM userTBL U
LEFT OUTER JOIN buyTBL B
ON U.userID = B.userID
GROUP BY U.userID
ORDER BY U.userID;
결과
NULL 값에 대한 산술연산 : NULL
NULL 값에 대한 비교연산 : Unknown
NULL 부분 보기 좋게 처리
SELECT U.userID, U.name, IFNULL(SUM(B.price*B.amount), 0) AS '총구매액'
FROM userTBL U
LEFT OUTER JOIN buyTBL B
ON U.userID = B.userID
GROUP BY U.userID
ORDER BY U.userID;
결과
CROSS JOIN(상호 조인)
-
두 테이블의 가능한 모든 조합을 구성 (M X N)
[형식]
USE sqlDB;
SELECT *
FROM buyTBL
CROSS JOIN userTBL;
SELF JOIN(자체 조인)
-
한 개의 테이블에 대해 자체 조인을 수행하는 것
-
한 개의 테이블을 두 개의 테이블로 간주 - ☆ 별칭으로 관리
▪ Alias를 두개 배정하여 구분
-
대표적인 예
▪ 조직도
USE sqlDB;
CREATE TABLE empTbl (emp CHAR(3), manager CHAR(3), empTel VARCHAR(8));
INSERT INTO empTbl VALUES
(N'나사장',NULL,'0000'),
(N'김재무',N'나사장','2222'),
(N'김부장',N'김재무','2222-1'),
(N'이부장',N'김재무','2222-2'),
(N'우대리',N'이부장','2222-2-1'),
(N'지사원',N'이부장','2222-2-2'),
(N'이영업',N'나사장','1111'),
(N'한과장',N'이영업','1111-1'),
(N'최정보',N'나사장','3333'),
(N'윤차장',N'최정보','3333-1'),
(N'이주임',N'윤차장','3333-1-1');
우대리 상관의 연락처 확인
SELECT A.emp AS '부하직원' , B.emp AS '직속상관', B.empTel AS '직속상관연락처'
FROM empTbl A
INNER JOIN empTbl B
ON A.manager = B.emp
WHERE A.emp = '우대리';
결과
UNION / UNION ALL / NOT IN / IN
[형식]
SELECT 문장1
UNION [ALL]
SELECT 문장2
UNION : 중복 제거, UNION ALL : 중복 모두 포함
USE sqlDB;
SELECT stdName, addr FROM stdTBL
UNION ALL
SELECT clubName, roomNo FROM clubTBL;
결과
차 집합
SELECT name, CONCAT(mobile1, mobile2) AS '전화번호' FROM userTBL
WHERE name NOT IN ( SELECT name FROM userTBL WHERE mobile1 IS NULL) ;
결과
교 집합
SELECT name, CONCAT(mobile1, mobile2) AS '전화번호' FROM userTBL
WHERE name IN ( SELECT name FROM userTBL WHERE mobile1 IS NULL) ;
결과
챕터, 데이터베이스당 하나의 쿼리를 써보자
Ctrl + Space : 자동완성
예제
-
employees 데이터 베이스
- employees TABLE에는 부서명이 없다.- departments TABLE에 dept_no과 dept_name이 있다.
- dept_emp TABLE에 dept_no와 부서에 있었던 기간 정보가 있다.
- dept_emp TABLE에 dept_no와 departments TABLE의 dept_no가 연결된다.
employees를 출력하는데 부서명, 부서에 있었던 기간을 출력하라.
USE employees;
SELECT E.first_name, E.last_name, D.dept_name, DE.from_date, DE.to_date
FROM employees E
INNER JOIN dept_emp DE
ON E.emp_no = DE.emp_no
INNER JOIN departments D
ON DE.dept_no = D.dept_no
ORDER BY E.first_name, E.last_name; -- 이걸 해주어야 사람별로 결과가 나온다.
결과
현재 부서명을 출력하라.
SELECT E.first_name, E.last_name, D.dept_name, DE.from_date, DE.to_date
FROM employees E
INNER JOIN dept_emp DE
ON E.emp_no = DE.emp_no
INNER JOIN departments D
ON DE.dept_no = D.dept_no
WHERE DE.to_date = '9999-01-01'
ORDER BY E.first_name, E.last_name;
결과
댓글