오라클 DBMS 정리 07
07 Nov 2018
Reading time ~7 minutes
오라클 DBMS 정리 07 - UNION, JOIN, SEQUENCE
테이블간 부모자식 관계를 만들려면
- PK, FK 설정
- 데이터의 중복을 최소화하기 위해서
- 정규화
- 용량 = 비용
- 학생정보 테이블과 매니저정보 테이블이 붙어있다면 학생들에 중복된 매니저 정보가 저장됨
- 테이블을 쪼개서 줄이자 = 정규화
- 매니저.매니저번호(PK) 1 - N 학생.매니저번호(FK)
- FK는 매니저테이블의 매니저번호의 도메인만 값으로 가질 수 있음 또는 NULL을 가짐
UNION
- 여러개의 테이블을 아래로 붙여서 조회
- UNION, UNION ALL
- UNION
- 여러 테이블 내에 중복값이 있으면 출력하지 않음
- UNION ALL
- 중복값도 출력
- UNION
- SQL Injection(취약점)이 발생할 수 있음
- 주의사항
- 조회되는 쿼리의 컬럼의 수와 데이터형이 일치해야 함
- 이름은 달라도 괜찮음
- 조회되는 쿼리의 컬럼의 수와 데이터형이 일치해야 함
SELECT 컬럼명, ...
FROM 테이블명
UNION [ALL]
SELECT 컬럼명, ...
FROM 테이블명
-- UNION : 중복데이터가 조회되지 않음, PK가 있다면 정렬해서 보여줌
-- emp : 미국본사, cp_emp3 : 한국지사라 가정했을 때
-- 미국본사와 한국지사 연봉이 2000이하인 사원급여를 올려주고 싶어
-- 2000이하인 사원의 사원번호, 사원명, 연봉, 직무를 조회
SELECT empno, ename, sal, job
FROM emp
WHERE sal <= 2000
UNION
SELECT empno, ename, sal, job
FROM cp_emp3
WHERE sal <=2000;
-- cp_emp3에 emp테이블에서 30번 부서 사원정보를 모두 추가
INSERT INTO cp_emp3(empno, ename, deptno, sal, job)
(SELECT empno, ename, deptno, sal, job
FROM emp
WHERE deptno=30);
-- UNION ALL : 중복데이터가 조회됨, 정렬 안된채로 조회
SELECT empno, ename, sal, job
FROM emp
WHERE sal <= 2000
UNION ALL
SELECT empno, ename, sal, job
FROM cp_emp3
WHERE sal <=2000;
-- 컬럼의 갯수가 일치하지 않으면 에러
SELECT empno, ename, sal
FROM emp
WHERE sal <= 2000
UNION
SELECT empno, ename
FROM cp_emp3
WHERE sal <= 2000;
-- 데이터형이 일치하지 않으면 에러
SELECT empno, ename, sal
FROM emp
WHERE sal <= 2000
UNION ALL
SELECT empno, ename, job
FROM cp_emp3
WHERE sal <=2000;
INTERSECT
- 교집합
- 서로다른 테이블의 중복된 값을 조회
- 사용방법은 UNION과 같음
-- 미국본사의 사원정보와 한국지사의 사원정보 중 일치하는 정보만 조회
SELECT empno, ename, sal, job
FROM emp
WHERE sal <= 2000
INTERSECT
SELECT empno, ename, sal, job
FROM cp_emp3
WHERE sal <=2000;
MINUS
- 차집합
- 상위 SELECT 조회된 결과에서 하위 SELECT의 결과를 뺀 결과를 조회
- 조회되는 테이블의 순서가 중요!
- 상위 SELECT는 레코드의 수가 하위 SELECT 레코드 수보다 많아야 한다
-- MINUS : 상위 SELECT에서 조회된 결과에서 하위 SELECT에서 조회된 결과를 뺀 나머지를 조회
SELECT empno, ename, sal, job
FROM emp
MINUS
SELECT empno, ename, sal, job
FROM cp_emp3
WHERE sal <=2000;
JOIN
- 서로 다른 테이블을 합쳐 조회할 때 사용
- 옆으로 붙는다
- ANSI 쿼리 지원(길다), Oracle 쿼리 지원
- INNER JOIN, OUTERJOIN, CROSS JOIN, SELF JOIN 존재
- ORACLE 문법은 FULL OUTER JOIN을 지원하지 않는다(ANSI로만 가능)
- EQUI JOIN, NON EQUI JOIN 두가지 형태로 사용
- 조인조건을 잘못 설정하면 카티션프로덕트(Cartesian Product)가 발생하고 나온 결과를 사용할 수 없다
- DRIVING TABLE 선정에 따라 결과가 나오는 속도가 달라짐
- DRIVING TABLE : 조인되는 키로 사용되는 테이블
INNER JOIN
- 조인하는 모든 테이블에 동일한 값이 있는 것만 조회
- EQUI JOIN/ NON EQUI JOIN은 조인조건에 따라 결정됨
-- 테이블간 같은 이름의 컬럼이 없으면 그냥 컬럼명으로 조회 -> 어느 테이블의 컬럼인지 확인이 힘들어 권장하지 않음!
-- 테이블간 같은 이름의 컬럼명이 있을 땐 테이블명.컬럼명 으로 조회
-- 테이블명이 길다면 alias 붙일 수 있다 - 권장
-- 테이블은 8개 이상 조인하지 않는다(엄청 느려짐)
-- ANSI INNER JOIN
SELECT 테이블1.컬럼명, 조인할테이블.컬럼명, ...
FROM 테이블1 alias
INNER JOIN 조인할테이블 alias
ON 조인조건;
-- Oracle INNER JOIN
SELECT 테이블1.컬럼명, 조인할테이블.컬럼명,...
FROM 테이블1 alias, 조인할테이블 alias
WHERE (조인조건) AND 검색조건
-- 사원번호, 사원명, 부서번호, 부서명, 부서위치, 연봉 조회
-- ANSI INNER JOIN
SELECT e.empno, e.ename, e.deptno,
d.dname, d.loc, e.sal
FROM emp e
INNER JOIN dept d
ON e.deptno = d.deptno;
-- ORACLE INNER JOIN
SELECT e.empno, e.ename, e.deptno,
d.dname, d.loc, e.sal
FROM emp e, dept d
WHERE e.deptno=d.deptno;
DRIVING TABLE 선정
- DRIVING TABLE을 잘 정하면 많은 양의 데이터를 가져올 때 속도가 빠름
- DRIVING TABLE : 조인되는 키로 사용되는 테이블
- PK와 FK의 관계가 있을 때
- PK가 DRIVING TABLE이 되면 좋다
- 관계가 없는 테이블을 조회할 때
- 레코드가 적거나, 레코드의 다양성이 적은 컬럼이 DRIVING TABLE로 선정되는게 좋다.
SELECT e.empno, e.ename, e.deptno,
d.dname, d.loc, e.sal
FROM emp e
INNER JOIN dept d
ON e.deptno = d.deptno;
- d.deptno(PK), e.deptno(FK)
- ’=’ 오른쪽이 key, 얘를 기준으로 같은걸 뽑아냄
- 검색시 사용되는게 KEY
- 예) e.deptno=d.deptno
- d.deptno 10, 20, 30, 40. 4개의 레코드
- 10을 갖고 e.dept와 비교, 20을 갖고 비교, 30을 갖고 비교, 40을 갖고 비교
- 레코드 수 만큼 4번 비교
- 예) d.deptno=e.deptno
- e.deptno가 키가 되면 20,10,20,10,30,20,10,… 이런식으로 14개의 레코드
- 20가져와서 d.dept와 비교, 10갖고 비교, 20갖고 비교, …
- 레코드 수 만큼 14번 비교
- 결국 레코드의 수 만큼 비교를함
- DRIVING TABLE로 레코드가 적은 테이블을 ‘키’로 선정해야 검색 속도가 빨라진다!
OUTER JOIN
- 어느 한 쪽에만 레코드가 있어도 조인결과를 보여줄 때 사용
- LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN이 존재
- ORACLE JOIN 문법은 FULL OUTER JOIN을 지원하지 않음
- 레코드의 구조를 잘 알아야 원하는 결과를 얻을 수 있음
-- ANSI
SELECT 컬럼명, 테이블명.컬럼명, alias.컬럼명, ...
FROM 테이블명
LEFT OUTER JOIN 조인할테이블명
ON 조인조건
--RIGHT OUTER JOIN / FULL OUTER JOIN 바꿔서 사용할 수 있음
-- LEFT, RIGHT로 모두 보고 싶은 테이블(데이터가 있는 방향을) 설정
-- ORACLE
-- 조인조건에 레코드가 없는 테이블에 (+)를 붙여줌
-- FK에 붙여주면 됨
-- 조인조건 양쪽에 (+)를 붙이면 error 발생
SELECT 컬럼명, 테이블명.컬럼명, alias.컬럼명, ...
FROM 테이블명 alias, 테이블명2 alias, ...
WHERE alias.컬럼명(+) = alias.컬럼명;
-- OUTER JOIN : 어느 한쪽 테이블에만 레코드가 존재하더라도 조회
-- 모든 부서의 부서번호, 부서명, 사원번호, 사원명, 입사일 조회
-- ANSI) LEFT나 RIGHT를 쓰되 레코드가 있는 쪽 테이블을 지정
-- 레코드의 구조를 잘 모른다면 FULL OUTER JOIN을 하여 양쪽 테이블 모두 조회가능
SELECT d.deptno, d.dname, e.empno, e.ename, e.hiredate
FROM dept d
LEFT OUTER JOIN emp e
ON e.deptno = d.deptno;
-- ORACLE)
SELECT d.deptno, d.dname, e.empno, e.ename, e.hiredate
FROM dept d, emp e
WHERE e.deptno(+) = d.deptno;
-- FULL OUTER JOIN은 ANSI문법만 지원(ORACLE 문법은 지원 안함)
SELECT d.deptno, e.deptno, d.dname, e.empno, e.ename, e.hiredate
FROM dept d
FULL OUTER JOIN emp e
ON e.deptno = d.deptno;
헷갈리는 조인 시 포함되는 내용 재정리..
- ANSI
- LEFT OUTER JOIN - 기존테이블을 기준으로 왼쪽으로 OUTER JOIN을 할거
- RIGHT OUTER JOIN - 기존테이블을 기준으로 오른쪽으로 OUTER JOIN을 할거
- ORACLE
- (+) 붙은 쪽이 레코드가 없고 OUTER JOIN으로 붙을거
- (+) 가 추가될 테이블
SELF JOIN
- 서브쿼리로 대체해서 사용 가능(하지만 길어짐)
- 테이블 하나를 사용하여 JOIN 할 때 사용
- 테이블을 조회목적과 비교목적 두가지로 구분하여 사용
- NON EQUI JOIN을 주로 사용
-- SELF JOIN은 두 테이블명이 같음, alias로 구분해서 사용
SELECT 컬럼명, 테이블명.컬럼명, alias.컬럼명, ...
FROM 테이블명 alias, 조인할테이블명 alias
WHERE 조인조건
-- 사원테이블에서 'SCOTT'인 사원보다 연봉을 작게 받는 사원의
-- 사원번호, 사원명, 입사일, 연봉을 조회
-- 서브쿼리를 사용하는 방법
SELECT empno, ename, hiredate, sal
FROM emp
WHERE sal < (SELECT sal
FROM emp
WHERE ename='SCOTT');
-- SELF JOIN을 사용하는 방법
-- 테이블 한개는 조회용으로만, 또다른 테이블은 비교(조건)용
SELECT e1.empno, e1.ename, e1.hiredate, e1.sal
FROM emp e1, emp e2
WHERE (e1.sal < e2.sal) AND e2.ename='SCOTT';
-- 조인한 복수행 서브쿼리로 얻어진 결과(INLINE VIEW)를 조회할 땐 컬럼명을 그냥 쓴다
SELECT ename, sal, dname
FROM(SELECT e.ename, e.sal, d.dname
FROM emp e, dept d);
SEQUENCE
- 증가하는 수를 반환하는 객체
- user_sequences 딕셔너리에서 시퀀스를 조회가능
- nextval, currval 가상 컬럼(pseudo column) 사용
- nextvar
- File 영역에 존재하는 번호를 Server Instance 영역에 올리고 현재번호의 다음 번호를 얻는 일을 함
- currval
- Server Instance 영역에 존재하는 시퀀스의 현재번호를 얻는 일을 함
- Server Instance에 sequence가 올라와 있지 않으면 error 발생
- nextvar이 먼저 사용되고 사용되어야 됨
- nextvar
- 서버가 종료되면 일정개수의 수(CACHE)를 잃어버림
- 기본적으로 20개
- 쿼리문이 실행 실패하더라도 번호는 소진됨
- 순차적인 번호를 얻을 순 있지만 넣을 순 없다.
- 증가하는 수를 가지고 PK를 사용해야할 때 좋음
- 생성
- CACHE - 메모리에 올려놓을 수(기본 20개)
- CYCLE은 끝값까지 사용하면 처음부터 반복실행할 것인지 결정(NO CYCLE이 기본)
CREATE SEQUENCE 시퀀스명
INCREMENT BY 증가값
START WITH 시작값
MAXVALUE 끝값
CACHE 메모리에올려놓을수
CYCLE[NOCYCLE]
- 사용
시퀀스명.nextval
시퀀스명.currval
-- SEQUENCE : 순차적으로 증가하는 수를 사용하는 객체
-- 1에서부터 9999999까지 1씩 증가하는 시퀀스를 생성
CREATE SEQUENCE seq_test
INCREMENT BY 1
START WITH 1
MAXVALUE 999999;
-- nextval 안하고 먼저 currval 사용하면 error!
-- currval은 메모리에 올라와 있는 시퀀스의 현재 번호를 얻는 일을 함
SELECT seq_test.currval
FROM DUAL;
-- 서버가 종료되면 메모리에 존재하던 시퀀스는 사라짐
-- 정의된 LAST_NUMBER부터 시작
-- 쿼리문의 실행 실패에도 시퀀스는 사라진다
INSERT INTO cp_emp(empno, ename, hiredate, sal)
VALUES(seq_test.nextval, '안기모씨', SYSDATE, 300);
-- ename의 크기가 10이라 한글4글자가 안들어감, 위에 INSERT 쿼리 여러 시도 후 (실패)
-- 제대로 넣으면 실패한 만큼 시퀀스 수가 올라가 있는걸 확인 가능하다
-- 시퀀스 삭제
DROP SEQUENCE seq_test;