SQL (3)

6. JOIN

조인이란?

  • 다수의 table간에 공통된 데이터를 기준으로 검색하는 명령어
  • 다수의 table 이란?
    - 동일한 table을 논리적으로 다수의 table로 간주: self join
    - 물리적으로 다른 table간의 조인

table에 별칭 사용

  • 검색시 다중 table의 컬럼명이 다를 경우 table별칭 사용 불필요,
  • 서로 다른 table간의 컬럼명이 중복된 경우, 컬럼 구분을 위해 오라클 엔진에게 정확한 table 소속명을 알려줘야 함
  • 주의사항 : table별칭 as 사용 불가

종류

  1. EQUI JOIN: 공통 필드의 레코드를 가져오는 방법 (중복)
      - INNER JOIN (중복 제거)
  2. OUTER JOIN: INNER JOIN 확장 (INNER JOIN + 공통되지 않은 레코드도 가져옴)
      - LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN
  3. NON-EQUI JOIN: 공통되지 않는 필드의 레코드를 가져오는 방법
  4. SELF JOIN

SET 연산자:

  • UNION, UNION ALL, INTERSECT, EXCEPT


INNER JOIN

테이블에서 같은 조건이 존재할 경우의 값 검색

SELECT tableA.id, tableA.value 
FROM tableA INNER JOIN tableB 
ON tableA.id = tableB.id; 

-- 별명 주기! 
-- note: 별명주면 별명만 사용해야 함, table 별명 as 사용 불가
SELECT a.id, a.value 
FROM tableA a INNER JOIN tableB b 
ON a.id = b.id; 

-- mysql 자체 문법
SELECT a.id, a.value 
FROM tableA a, tableB b 
WHERE a.id = b.id; 
-- 직원의 사번, 이름, 업무, 부서번호, 부서명을 조회
SELECT empno, ename, job, scott_emp.deptno, dname 
FROM scott_emp INNER JOIN scott_dept 
ON scott_emp.deptno = scott_dept.deptno;
+-------+--------+-----------+--------+------------+
| empno | ename  | job       | deptno | dname      |
+-------+--------+-----------+--------+------------+
|  7782 | CLARK  | MANAGER   |     10 | ACCOUNTING |
|  7839 | KING   | PRESIDENT |     10 | ACCOUNTING |
|  7934 | MILLER | CLERK     |     10 | ACCOUNTING |
|  7369 | SMITH  | CLERK     |     20 | RESEARCH   |
|  7566 | JONES  | MANAGER   |     20 | RESEARCH   |
|  7788 | SCOTT  | ANALYST   |     20 | RESEARCH   |
|  7876 | ADAMS  | CLERK     |     20 | RESEARCH   |
|  7902 | FORD   | ANALYST   |     20 | RESEARCH   |
|  7499 | ALLEN  | SALESMAN  |     30 | SALES      |
|  7521 | WARD   | SALESMAN  |     30 | SALES      |
|  7654 | MARTIN | SALESMAN  |     30 | SALES      |
|  7698 | BLAKE  | MANAGER   |     30 | SALES      |
|  7844 | TURNER | SALESMAN  |     30 | SALES      |
|  7900 | JAMES  | CLERK     |     30 | SALES      |
+-------+--------+-----------+--------+------------+
-- salesman에 대해서 직원의 사번, 이름, 업무, 부서번호, 부서명을 조회
-- ! 조건을 조인하면서 같이줄 때
SELECT empno, ename, job, e.deptno, dname 
FROM scott_emp e INNER JOIN scott_dept d 
ON e.deptno = d.deptno AND job = 'salesman';

-- ! 조건을 조인 하고 나서 뒤에 줄 때
SELECT empno, ename, job, e.deptno, dname 
FROM scott_emp e INNER JOIN scott_dept d 
ON e.deptno = d.deptno 
WHERE job = 'salesman';
+-------+--------+----------+--------+-------+
| empno | ename  | job      | deptno | dname |
+-------+--------+----------+--------+-------+
|  7499 | ALLEN  | SALESMAN |     30 | SALES |
|  7521 | WARD   | SALESMAN |     30 | SALES |
|  7654 | MARTIN | SALESMAN |     30 | SALES |
|  7844 | TURNER | SALESMAN |     30 | SALES |
+-------+--------+----------+--------+-------+
-- New york에서 근무하는 직원의 이름, 업무, 부서명 조회
SELECT ename, job, scott_dept.dname 
FROM scott_dept INNER JOIN scott_emp 
ON scott_emp.deptno = scott_dept.deptno 
WHERE loc = 'NEW YORK';
+--------+-----------+------------+
| ename  | job       | dname      |
+--------+-----------+------------+
| CLARK  | MANAGER   | ACCOUNTING |
| KING   | PRESIDENT | ACCOUNTING |
| MILLER | CLERK     | ACCOUNTING |
+--------+-----------+------------+


OUTER JOIN

두개 이상의 테이블이 조인될때 특정 데이터가 모든 테이블에 존재하지 않고 컬럼은 존재하나 null값을 보유한 경우 검색되지 않는 문제를 해결하기 위해 사용되는 조인

SELECT a.id, a.value 
FROM tableA a LEFT OUTER JOIN tableB b 
ON a.id = b.id;
+------+-------+
| id   | value |
+------+-------+
|    1 |    10 |
|    2 |    20 |
|    5 |    50 |
|    3 |    30 |
|    7 |    70 |
+------+-------+
SELECT b.id, b.value 
FROM tableA a RIGHT OUTER JOIN tableB b 
ON a.id = b.id;
+------+-------+
| id   | value |
+------+-------+
|    1 |    10 |
|    2 |    20 |
|    5 |    50 |
|    4 |    40 |
|    8 |    80 |
+------+-------+
-- 직원들의 이름, 급여, 근무지를 조회. 단, 부서명과 근무지는 모두 출력할 수 있도록 하시오
SELECT ename, sal, dname, loc 
FROM scott_emp RIGHT OUTER JOIN scott_dept 
ON scott_emp.deptno = scott_dept.deptno;
+--------+------+------------+----------+
| ename  | sal  | dname      | loc      |
+--------+------+------------+----------+
| CLARK  | 2450 | ACCOUNTING | NEW YORK |
| KING   | 5000 | ACCOUNTING | NEW YORK |
| MILLER | 1300 | ACCOUNTING | NEW YORK |
| SMITH  |  800 | RESEARCH   | DALLAS   |
| JONES  | 2975 | RESEARCH   | DALLAS   |
| SCOTT  | 3000 | RESEARCH   | DALLAS   |
| ADAMS  | 1100 | RESEARCH   | DALLAS   |
| FORD   | 3000 | RESEARCH   | DALLAS   |
| ALLEN  | 1600 | SALES      | CHICAGO  |
| WARD   | 1250 | SALES      | CHICAGO  |
| MARTIN | 1250 | SALES      | CHICAGO  |
| BLAKE  | 2850 | SALES      | CHICAGO  |
| TURNER | 1500 | SALES      | CHICAGO  |
| JAMES  |  950 | SALES      | CHICAGO  |
| NULL   | NULL | OPERATIONS | BOSTON   |
+--------+------+------------+----------+


NON-EQUI JOIN

100% 일치하지 않고 특정 범위내의 데이터 조인시에 사용

---직원들의 사번, 이름, 급여, 급여등급 조회
SELECT empno, ename, sal, grade, losal, hisal 
FROM scott_emp e INNER JOIN scott_salgrade s 
ON e.sal >= s.losal AND e.sal <= s.hisal;
+-------+--------+------+-------+-------+-------+
| empno | ename  | sal  | grade | losal | hisal |
+-------+--------+------+-------+-------+-------+
|  7369 | SMITH  |  800 |     1 |   700 |  1200 |
|  7499 | ALLEN  | 1600 |     3 |  1401 |  2000 |
|  7521 | WARD   | 1250 |     2 |  1201 |  1400 |
|  7566 | JONES  | 2975 |     4 |  2001 |  3000 |
|  7654 | MARTIN | 1250 |     2 |  1201 |  1400 |
|  7698 | BLAKE  | 2850 |     4 |  2001 |  3000 |
|  7782 | CLARK  | 2450 |     4 |  2001 |  3000 |
|  7788 | SCOTT  | 3000 |     4 |  2001 |  3000 |
|  7839 | KING   | 5000 |     5 |  3001 |  9999 |
|  7844 | TURNER | 1500 |     3 |  1401 |  2000 |
|  7876 | ADAMS  | 1100 |     1 |   700 |  1200 |
|  7900 | JAMES  |  950 |     1 |   700 |  1200 |
|  7902 | FORD   | 3000 |     4 |  2001 |  3000 |
|  7934 | MILLER | 1300 |     2 |  1201 |  1400 |
+-------+--------+------+-------+-------+-------+


SELF JOIN

동일 테이블 내에서 상이한 칼럼 참조

-- 직원의 사번, 이름, 업무, 관리자, 관리자이름 조회
-- 별명주기 필수
SELECT e.empno, e.ename, e.job, e.mgr, m.ename 
FROM scott_emp e INNER JOIN scott_emp m 
ON e.mgr = m.empno;
+-------+--------+----------+------+-------+
| empno | ename  | job      | mgr  | ename |
+-------+--------+----------+------+-------+
|  7369 | SMITH  | CLERK    | 7902 | FORD  |
|  7499 | ALLEN  | SALESMAN | 7698 | BLAKE |
|  7521 | WARD   | SALESMAN | 7698 | BLAKE |
|  7566 | JONES  | MANAGER  | 7839 | KING  |
|  7654 | MARTIN | SALESMAN | 7698 | BLAKE |
|  7698 | BLAKE  | MANAGER  | 7839 | KING  |
|  7782 | CLARK  | MANAGER  | 7839 | KING  |
|  7788 | SCOTT  | ANALYST  | 7566 | JONES |
|  7844 | TURNER | SALESMAN | 7698 | BLAKE |
|  7876 | ADAMS  | CLERK    | 7788 | SCOTT |
|  7900 | JAMES  | CLERK    | 7698 | BLAKE |
|  7902 | FORD   | ANALYST  | 7566 | JONES |
|  7934 | MILLER | CLERK    | 7782 | CLARK |
+-------+--------+----------+------+-------+


세개 이상 JOIN

SELECT a.id, a.value, b.id, b.value, c.id, c.value
FROM tableA a INNER JOIN tableB b
ON a.id = b.id
INNER JOIN tableC c
ON b.id = c.id;

-- mysql 자체 문법
SELECT a.id, a.value, b.id, b.value, c.id, c.value
FROM tableA a, tableB b, tableC c
WHERE a.id = b.id AND b.id = c.id;


SET 연산자

SELECT deptno FROM scott_emp 
UNION 
SELECT deptno FROM scott_dept;
+--------+
| deptno |
+--------+
|     10 |
|     20 |
|     30 |
|     40 |
+--------+
SELECT deptno FROM scott_emp 
UNION ALL 
SELECT deptno FROM scott_dept;
+--------+
| deptno |
+--------+
|     10 |
|     10 |
|     10 |
|     20 |
|     20 |
|     20 |
|     20 |
|     20 |
|     30 |
|     30 |
|     30 |
|     30 |
|     30 |
|     30 |
|     10 |
|     20 |
|     30 |
|     40 |
+--------+


7. 데이터 무결성 (Integrity)

제약: Constraint What? How?

  1. 실체 (Entity: 주로 table을 의미) 무결성
      - 데이터 중복 방지
      - PRIMARY KEY, UNIQUE
  2. 영역 (Domain) 무결성
      - CHECK (mysql은 x)
  3. 참조 (Reference) 무결성
      - FOREIGN KEY
      - 부모: 참조 당하는 table, 자식: 참조하는 table

컬럼의 속성

  1. NN (Not Null)
      - PRIMARY KEY
  2. ND (Not Duplicate)
      - PRIMARY KEY
      - UNIQUE
  3. NC (Not Change)
      - FOREIGN KEY


PRIMARY KEY (NN & ND)

CREATE TABLE tableEXAM( 
	id	int, 
    name	varchar(10) 
);

INSERT INTO tableEXAM(id) VALUES(1);
INSERT INTO tableEXAM(id) VALUES(1);

-- PRIMARY KEY: 한 테이블 당 한개만 소유
ALTER TABLE tableEXAM ADD CONSTRAINT pk_id PRIMARY KEY(id);
DELETE FROM tableEXAM;
ALTER TABLE tableEXAM ADD CONSTRAINT pk_id PRIMARY KEY(id);

-- ND
INSERT INTO tableEXAM(id) VALUES(1);
INSERT INTO tableEXAM(id) VALUES(1); -- ERROR

-- NN
INSERT INTO tableEXAM(name) VALUES('kimmy'); -- ERROR
-- PRIMARY KEY 지정 방법
DROP TABLE tableEXAM;

--하나의 field에만 primary key
CREATE TABLE tableEXAM(
	id	int	PRIMARY KEY, 
	name varchar(10)
);

DROP TABLE tableEXAM;

-- 여러 field를 하나의 primary key로 가능 (권장)
CREATE TABLE tableEXAM( 
	id	int, 
	name varchar(10), 
	CONSTRAINT pk_id PRIMARY KEY(id) 
);
-- NN
CREATE TABLE tableEXAM( 
	id	int	not null, 
    name	varchar(10)	not null 
);

-- name을 null 로
ALTER TABLE tableEXAM MODIFY name varchar(10) null;
ALTER TABLE tableEXAM ADD age int null;


UNIQUE (ND)

-- UNIQUE: ND 속성을 지켜줌
DROP TABLE tableEXAM;
CREATE TABLE tableEXAM( 
	id	int, 
	name varchar(10), 
	age int
);
ALTER TABLE tableEXAM ADD CONSTRAINT uk_id UNIQUE(id); 
ALTER TABLE tableEXAM ADD CONSTRAINT uk_name UNIQUE(name);

INSERT INTO tableEXAM(id, name, age) VALUES(1, 'kimmy', 20);

-- ND
INSERT INTO tableEXAM(id, name, age) VALUES(1, 'lee', 20);
	--> ERROR: Duplicate entry '1' for key 'uk_id'
INSERT INTO tableEXAM(id, name, age) VALUES(2, 'kimmy', 20);
	--> ERROR: Duplicate entry 'kimmy' for key 'uk_name'

-- NN 속성은 방지 x
INSERT INTO tableEXAM(id, name, age) VALUES(null, 'jimmy', 20);
	--> Query OK, 1 row affected
    
-- null값 중복은 ok
INSERT INTO tableEXAM(id, name, age) VALUES(null, 'elsa', 20);
	--> Query OK, 1 row affected
/* NN & ND 선처리 방식 (제약 조건은 x)
NN: DEFAULT
ND: AUTO_INCREMENT
*/
DROP TABLE tableEXAM;

CREATE TABLE tableEXAM( 
	id	int	AUTO_INCREMENT, 
	name varchar(10) DEFAULT 'no name', 
	age int	DEFAULT 20,
	CONSTRAINT uk_id UNIQUE(id)
);

INSERT INTO tableEXAM(name, age) VALUES ('scott', 20);
INSERT INTO tableEXAM(age) VALUES (30);
INSERT INTO tableEXAM(name) VALUES ('miller');


FOREIGN KEY (NC)

CREATE TABLE tableDEPT(
	deptno int,
	dname varchar(10)
);

INSERT INTO tableDEPT VALUES(100, 'SALES');
INSERT INTO tableDEPT VALUES(101, 'MARKETING');
INSERT INTO tableDEPT VALUES(102, 'RESEARCH');

CREATE TABLE tableEMP(
	empno int,
	ename varchar(20),
	hiredate datetime,
	deptno int
);

-- deptno에 제약이 없으면 참조키를 달 수 없음
ALTER TABLE tableDEPT ADD CONSTRAINT pk_deptno PRIMARY KEY(deptno); 

ALTER TABLE tableEMP 
ADD CONSTRAINT fk_deptno FOREIGN KEY(deptno) 
REFERENCES tableDEPT(deptno);

INSERT INTO tableEMP(empno, ename, hiredate, deptno) VALUES(1, 'scott', now(), 103);
	--> ERROR: Cannot add or update a child row: a foreign key constraint fails

INSERT INTO tableEMP(empno, ename, hiredate, deptno) VALUES(1, 'scott', now(), 102);
	--> Query OK, 1 row affected

-- NC
UPDATE tableDEPT SET deptno = 1002 WHERE deptno = 102;
	--> ERROR: Cannot delete or update a parent row: a foreign key constraint fails

-- NC (삭제 x)
DROP TABLE tableDEPT;
	--> ERROR: Cannot delete or update a parent row: a foreign key constraint fails


Example

column name post1 post2 addr
key type PK PK  
nulls/unique     NN
max length 3 3 60
sample     seoul, south korea


column name id name sex jumin1 jumin2 tel post1 post2 addr
key type PK           FK FK  
nulls/unique   NN   UK UK        
FK ref table             POST    
FK ref column             post1 post2  
data type INT VARCHAR CHAR CHAR CHAR VARCHAR CHAR CHAR VARCHAR
max length 4 10 1 6 7 15 3 3 60
sample 1234 hgd 1 990101 1232344 712-1234 100 010  
CREATE TABLE POST(
	post1 char(3),
	post2 char(3),
	addr varchar(60) DEFAULT 'no addr',
	CONSTRAINT pk_post PRIMARY KEY(post1, post2)
);

CREATE TABLE MEMBER(
	id int(4) PRIMARY KEY,
	name varchar(10) DEFAULT 'kimmy',
	sex char(1), 
	jumin1 char(6),
	jumin2 char(7),
	tel varchar(15),
	post1 char(3),
	post2 char(3),
	addr varchar(60)
);
ALTER TABLE MEMBER ADD CONSTRAINT uk_jumin UNIQUE(jumin1, jumin2);
ALTER TABLE MEMBER ADD CONSTRAINT fk_post FOREIGN KEY(post1, post2) REFERENCES POST(post1, post2);