🔷 둘 이상의 테이블에서 데이터를 조회하기 위해서 사용
🔷 일반적으로 조인 조건은 PK(Primary Key)
및 FK(Foreign Key)
로 구성된다.
JOIN
이 가능하다.🔷 JOIN의 종류
INNER JOIN
: 조인 조건에 해당하는 칼럼 값이 양쪽 테이블에 모두 존재하는 경우에만 조회, 동등 조인(Equi-join)
이라고도 한다. N개의 테이블 조인 시 N-1개의 조인 조건이 필요
OUTER JOIN
: 조인 조건에 해당하는 칼럼 값이 한 쪽 테이블에만 존재하더라도 조회 기준 테이블에 따라 LEFT OUTER JOIN
, RIGHT OUTER JOIN
으로 구분
SELF JOIN
: 같은 테이블 2개를 조인
Non-Equi JOIN
: 조인 조건이 table의 PK, FK 등으로 정확히 일치하는 것이 아닐 때 사용
💡 두 테이블을 조인할 때 어떤 테이블을 먼저 읽느냐에 따라 작업량이 달라질 수 있다! 물론 요즘 DB는 알아서 최적화를 해주기 때문에 크게 신경 쓸 필요 없다.
🔷 카타시안 곱(Cartesian Product)
-- 2번의 조회를 합치자
SELECT empno, ename, job
FROM emp;
SELECT deptno, dname
FROM dept;
-- 카타시안 곱
-- WHERE 사용 없이는 단순히 두 테이블 행을 곱한만큼 나온다.
SELECT empno, ename, job, emp.deptno, dname
FROM emp, dept;
-- WHERE 사용하여 유의미하게 데이터를 뽑기
SELECT empno, ename, job, emp.deptno, dname
FROM emp, dept
WHERE emp.deptno = dept.deptno;
-- 사번 7788인 사원의 이름, 업무, 부서번호, 부서이름 조회
SELECT ename, job, deptno
FROM emp
WHERE empno = 7788;
SELECT dname
FROM dept
WHERE deptno = 20;
-- 두 번 조회할 필요 없이 조인!
-- 조인을 이용하여 작성 (INNER JOIN과 같다)
SELECT ename, job, emp.deptno, dname
FROM emp, dept
WHERE emp.deptno = dept.deptno
AND empno = 7788;
-- INNER JOIN
-- 두 테이블에서 일치하는 값을 가진 레코드를 조회
-- table alias를 지정해 SQL 작성시 쉽게 사용할 수 있다.
SELECT e.ename, e.job, e.deptno, d.dname
FROM emp e
INNER JOIN dept d
ON e.deptno = d.deptno
WHERE e.empno = 7788;
-- 위랑 같다 (USING 키워드는 두 개의 테이블에 동일한 컬럼명이 존재할 때만 사용)
SELECT ename, job, deptno, dname
FROM emp
INNER JOIN dept
USING (deptno)
WHERE empno = 7788;
-- 새로운 인원이 들어왔다고 했을 때
INSERT INTO emp
VALUES (1236, "박영규", "MANAGER", 7139, "2023-10-11", 2000, NULL, NULL);
-- 동등조인으로 이름, 부서번호, 부서 이름을 가져오기
-- 새로 추가한 인물은 부서가 없어서 나오지 않는다!
SELECT e.ename, e.deptno, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno;
-- OUTER JOIN
-- 한쪽에 기준을 주고 붙이기
-- 없으면 없는 데로
-- 모든 사원을 기준으로 (좌측 테이블)
SELECT e.ename, e.deptno, d.dname
FROM emp e LEFT OUTER JOIN dept d
ON e.deptno = d.deptno;
-- 부서 테이블을 기준으로 (우측 테이블)
SELECT e.ename, e.deptno, d.dname
FROM emp e RIGHT OUTER JOIN dept d
ON e.deptno = d.deptno;
-- 셀프 조인
-- 모든 사원번호, 이름, 매니저 번호, 매니저 이름
SELECT e1.empno, e1.ename, e1.mgr, e2.ename
FROM emp e1, emp e2
WHERE e1.mgr = e2.empno
ORDER BY e2.ename;
-- INNER JOIN 이라는 키워드를 사용해보자
SELECT e1.empno AS "사번", e1.ename AS 사원이름, e2.empno AS "매니저 번호", e2.ename AS "매니저 이름"
FROM emp e1
INNER JOIN emp e2
ON e1.mgr = e2.empno;
-- KING 이 안나오는 위의 쿼리문과 다르게 KING이 나온다
-- OUTER JOIN이니까!
SELECT e1.empno AS "사번", e1.ename AS 사원이름, e2.empno AS "매니저 번호", e2.ename AS "매니저 이름"
FROM emp e1 LEFT OUTER JOIN emp e2
ON e1.mgr = e2.empno;
-- 비 동등 조인(Non-Equi JOIN)
-- 조인조건이 table의 PK, FK 등으로 정확히 일치하는 것이 아닐 때 사용
-- 모든 사원의 사번, 이름, 급여, 급여등급을 조회하고 싶다면?
SELECT e.empno, e.ename, e.sal AS "급여", sg.grade AS "급여등급"
FROM emp e, salgrade sg
WHERE e.sal BETWEEN sg.LOSAL AND sg.HISAL
-- WHERE e.sal >= sg.LOSAL AND e.sal <= sg.HISAL
ORDER BY sg.grade DESC, e.sal DESC;
🔷 하나의 SQL 문안에 포함되어 있는 SQL문을 의미한다.
외부 쿼리(outer query)
또는 메인 쿼리라고 부르며, 서브 쿼리는 내부 쿼리(inner query)
라고도 부른다.🔷 서브 쿼리의 종류
🔷 서브 쿼리를 포함할 수 있는 SQL 문
SELECT
, FROM
, WHERE
, HAVING
, ORDER BY
INSERT
문의 VALUES
UPDATE
문의 SET
🔷 서브 쿼리의 사용 시 주의사항
💡 단일 행 비교연산자는 서브 쿼리 결과가 1건 이하이어야 하고, 복수 행 비교 연산자는 결과 건수와 상관없다.
🔷 단일 행
-- 1. 매니저의 이름이 KING인 사원의 사번, 이름, 부서번호, 업무
SELECT empno, ename, deptno, job
FROM emp
WHERE mgr = (SELECT empno
FROM emp
WHERE ename = "KING");
-- 2. 7566번 사원보다 급여를 많이 받는 사원의 이름, 급여를 조회
-- 7566번의 급여
SELECT sal
FROM emp
WHERE empno = 7566;
SELECT ename, sal
FROM emp
WHERE sal > (SELECT sal
FROM emp
WHERE empno = 7566);
-- 3. 20번 부서의 평균 급여보다 급여가 많은 사원의 사번, 이름, 업무, 급여조회
SELECT AVG(sal)
FROM emp
WHERE deptno = 20;
SELECT empno, ename, job, sal
FROM emp
WHERE sal > (SELECT AVG(sal)
FROM emp
WHERE deptno = 20);
-- 4. 업무가 TURNER와 같고/ 사번 7934인 직원보다 급여/가 많은 사원의 사번, 이름, 업무를 조회
SELECT job
FROM emp
WHERE ename = "TURNER";
SELECT sal
FROM emp
WHERE empno = 7934;
SELECT empno, ename, job, sal
FROM emp
WHERE job = (SELECT job
FROM emp
WHERE ename = "TURNER")
AND sal > (SELECT sal
FROM emp
WHERE empno = 7934);
🔷 다중 행
IN
, ANY
, ALL
연산자와 함께 사용-- 다중행 IN / ANY / ALL
-- 5. 업무가 SALESMAN 인 직원들 중 최소 한명 이상보다 많은 급여를 받는 사원의 이름, 급여, 업무를 조회하시오.
SELECT sal
FROM emp
WHERE job = 'SALESMAN';
-- > ANY (최소값보다는 큼) / < ANY (최대값보다는 작음)
SELECT ename, sal, job
FROM emp
WHERE sal > ANY (SELECT sal
FROM emp
WHERE job = 'SALESMAN');
-- 6. 업무가 'SALESMAN'인 모든 직원보다 급여(커미션포함)를 많이 받는 사원의 이름, 급여, 업무, 입사일, 부서번호를 조회하시오.
SELECT sal + IFNULL(comm, 0)
FROM emp
WHERE job = 'SALESMAN';
-- > ALL (최대값보다 큼) / < ALL (최소값보다 작음)
SELECT ename, sal, job, hiredate, deptno
FROM emp
WHERE sal > ALL (SELECT sal + IFNULL(comm, 0)
FROM emp
WHERE job = 'SALESMAN');
-- 7. 직원이 최소 한명이라도 근무하는 부서의 부서번호, 부서이름, 위치
SELECT DISTINCT deptno
FROM emp;
-- DISTINCT 키워드를 이용해 중첩되는 행은 제거
-- IN은 다중행에 하나라도 일치하면 조회, = ANY 와 같다
SELECT deptno, dname, loc
FROM dept
WHERE deptno IN (SELECT DISTINCT deptno
FROM emp);
🔷 다중 열
행 생성자(row constructor)
를 이용하여 다중 열 서브 쿼리를 비교-- 다중열
-- 8. 이름이 FORD인 사원과 매니저 및 부서가 같은 사원의 이름, 매니저번호, 부서번호를 조회
SELECT mgr, deptno
FROM emp
WHERE ename = 'FORD';
SELECT ename, mgr, deptno
FROM emp
WHERE (mgr, deptno) = (SELECT mgr, deptno
FROM emp
WHERE ename = 'FORD')
AND ename <> 'FORD';
-- AND ename != 'FORD';
-- 9. 각 부서별 / 입사일이 가장 빠른 / 사원의 사번, 이름, 부서번호, 입사일을 조회
SELECT IFNULL(deptno, '대기발령') , MIN(hiredate)
FROM emp
GROUP BY deptno;
-- 지난 포스팅에서도 언급했다시피 NULL은 연산이 불가능해 걸러줄 필요가 있다.
SELECT empno, ename, deptno, hiredate
FROM emp
WHERE (deptno, hiredate) IN (SELECT IFNULL(deptno, '대기발령') , MIN(hiredate)
FROM emp
GROUP BY deptno);
🔷 외부 쿼리에 있는 테이블에 대한 참조를 하는 서브 쿼리를 의미한다.
-- 상호연관 서브쿼리
-- 외부 쿼리의 e를 서브 쿼리가 참조하고 있다.
-- 10. 소속 부서의 평균 급여보다 많은 급여를 받는 사원의 이름, 급여, 부서번호, 입사일, 업무를 조회
SELECT ename, sal, deptno, hiredate, job
FROM emp e
WHERE sal > (SELECT AVG(sal)
FROM emp
WHERE deptno = e.deptno);
🔷 FROM절에서 사용되는 서브 쿼리
💡 인라인 뷰는 SQL 문이 실행될 때만 임시적으로 생성되는 뷰 이기 때문에 데이터베이스에 해당 정보가 저장되지 않는다. 그래서
동적 뷰(Dynamic View)
라고도 한다.
-- 인라인 뷰(FROM 절에 서브쿼리)
-- 11. 모든 사원의 평균급여보다 적게 받는 사원들과 같은 부서에서 근무하는 사원의 사번, 이름, 급여, 부서번호를 조회
SELECT AVG(sal)
FROM emp;
SELECT DISTINCT deptno
FROM emp
WHERE sal < (SELECT AVG(sal) FROM emp);
SELECT e.ename, e.empno, e.sal, e.deptno
FROM emp e, (SELECT DISTINCT deptno FROM emp WHERE sal < (SELECT AVG(sal) FROM emp)) AS d
WHERE e.deptno = d.deptno;
-- 12. 모든 사원에 대하여 사원의 이름, 부서번호, 급여, 사원이 소속된 부서의 평균 급여를 조회 (단, 이름 오름차순)
SELECT deptno, AVG(sal) AS avgsal
FROM emp
GROUP BY deptno;
SELECT e.ename, e.deptno, e.sal, d.avgsal
FROM emp e, (SELECT deptno, AVG(sal) AS avgsal
FROM emp
GROUP BY deptno) d
WHERE e.deptno = d.deptno
ORDER BY e.ename;
🔷 LIMIT
LIMIT
절을 사용SELECT empno, ename, sal
FROM emp
ORDER BY sal DESC limit 5, 5;
🔷 하나의 행에서 하나의 컬럼 값만 반환하는 서브 쿼리
🔷 다음과 같은 경우에 사용가능
-- 스칼라서브쿼리
-- SELECT 에 스칼라 서브 쿼리 사용
-- 13. 사원의 이름, 부서번호, 급여, 소속부서의 평균 급여를 조회
SELECT ename, deptno, sal, (SELECT AVG(sal) FROM emp WHERE deptno = e.deptno) AS avgsal
FROM emp e;
-- SELECT 에 스칼라 서브 쿼리 사용
-- 14. 부서번호가 10인 부서의 총 급여, 20인 부서의 평균 급여, 30인 부서의 최고, 최저 급여
SELECT (SELECT SUM(sal) FROM emp WHERE deptno = 10) AS SUM10,
(SELECT AVG(sal) FROM emp WHERE deptno = 20) AS AVG20,
(SELECT MAX(sal) FROM emp WHERE deptno = 30) AS MAX30,
(SELECT MIN(sal) FROM emp WHERE deptno = 30) AS MIN30
FROM dual;
-- ORDER BY 에 스칼라 서브 쿼리 사용
-- 15. 모든사원의 번호, 이름, 부서번호, 입사일을 조회 (단, 부서이름기준으로 내림차순)
UPDATE emp SET deptno = 40 WHERE empno = 4168;
SELECT empno, ename, deptno, hiredate
FROM emp e
ORDER BY (SELECT dname
FROM dept
WHERE deptno = e.deptno) DESC;
-- 서브 쿼리를 이용한 CREATE 문
-- 테이블을 카피
CREATE TABLE emp_copy
(SELECT * FROM emp);
-- emp table 구조만 emp_blank 라는 이름으로 복사하여 생성
-- WHERE 1 = 0 은 약속과도 같다.
CREATE TABLE emp_blank
(SELECT * FROM emp WHERE 1 = 0);
-- 서브 쿼리를 이용한 INSERT 문
-- 부서 번호가 30인 사원의 모든 정보를 emp_blank에 INSERT
INSERT INTO emp_blank
(SELECT * FROM emp WHERE deptno = 30);
SELECT * FROM emp_blank;
🔷 실생활을 DB로 모델링하는 작업을 일컫는다.
ERD
를 작성한다.🔷 개체 (Entity) : 사용자와 관계가 있는 주요 객체 (데이터로 관리 되어야 하는 것)
🔷 Entity 찾는법
Entity
는 Attribute
를 가져야 함.🔷 속성 (Attribute)
🔷 식별자 : 한 개체(Entity) 내에서 인스턴스를 구분할 수 있는 단일 속성 또는 속성 그룹
🔷 후보키 (Candidate Key) : 개체내에서 각각의 인스턴스를 구분할 수 있는 속성 (기본키가 될 수 있음)
🔷 기본키 (Primary Key) : 개체(Entity)에서 각 인스턴스를 유일하게 식별하는데 적합한 Key
🔷 대체키 (Alternate Key) : 후보키 중에서 기본키로 선정 되지 않은 Key
🔷 복합키 (Comosite Key) : 하나의 속성으로 기본키가 될 수 없는 경우 둘 이상의 컬럼을 묶어서 식별자로 정의
🔷 대리키 (Surrogate Key) : 식별자가 너무 길거나 여러 개의 속성으로 구성되어 있는 경우 인위적으로 추가
🔷 관계 (Relationship) : 두 Entity간의 업무적인 연관성 또는 관련 사실
🔷 ERD 관계를 설정하는 순서
mandatory
or optional
)🔷 개념적 데이터베이스 모델링 단계에서 정의된 ER-Diagram을 Mapping Rule을 적용하여 관계형 데이터베이스 이론에 입각한 스키마를 설계하는 단계와 이를 이용하여 필요하다면 정규화 하는 단계로 구성
🔷 기본키 (Primary Key)
🔷 참조키, 이웃키 (Foreign Key)
🔷 Mapping Rule
🔷 정규화 (Normalization)
🔷 정규화 (Normalization)의 목적
BCNF
도 있다.🔷 논리적 데이터베이스 모델링 단계에서 얻어진 데이터베이스 스키마를 좀더 효율적으로 구현하기 위한 작업
domain
설정(int, varchar, date, …)🔷 역정규화 (Denomalization)
🔷 역정규화 방법
서브 쿼리부터 지끈지끈하지만, 잘 익혀갑시다...