KoreanFoodie's Study
SQL 기초 - 12. 뷰 (임시 테이블, CTE) 본문
이 글은 '김상형의 SQL 정복'의 예제 코드를 설명하는 방식으로 SQL 쿼리 예제를 소개하고 있습니다.
SQL 기초 - 12. 뷰 (임시 테이블, CTE)
SELECT member, age, addr FROM tMember;
-- 간단한 VIEW를 생성하는 예제
-- VIEW는 쿼리문으로 생성하는 가상적인 테이블이다.
-- 뷰의 데이터를 수정하면 실제 테이블에도 영향을 끼친다
-- 뷰는 보기 전용으로 이용하는 것이 바람직하다.
CREATE VIEW vMember AS
SELECT member, age, addr FROM tMember;
SELECT * FROM vMember;
SELECT member, age FROM vMember;
SELECT * FROM vMember WHERE addr LIKE '%서울%';
SELECT * FROM vMember ORDER BY member;
SELECT name, email FROM vMember;
DROP VIEW vMember;
CREATE VIEW vMember AS SELECT member, age, addr, email FROM tMember;
오라클, 마리아 : CREATE OR REPLACE VIEW vMember AS SELECT member, age, addr, email FROM tMember;
MSSQL : ALTER VIEW vMember AS SELECT member, age, addr, email FROM tMember;
CREATE VIEW vMemberMirror AS SELECT * FROM tMember;
-- 원하는 열을 선택하면 VERTICAL하게 VIEW를 만들 수 있다
CREATE VIEW vStaffVirt AS SELECT depart, salary, name FROM tStaff;
-- WHERE을 통해 원하는 행을 뽑아낼 수 있다.
CREATE VIEW vStaffHorz AS SELECT * FROM tStaff WHERE depart = '총무부';
CREATE VIEW vStaffPart AS SELECT name, salary FROM tStaff WHERE depart = '총무부';
-- 원하는 별명으로 열을 지정해 줄 수 있다.
CREATE VIEW vStaffAlias(n, d, s) AS SELECT name, depart, salary FROM tStaff;
CREATE OR REPLACE VIEW vStaffAlias AS SELECT name n, depart d , salary s FROM tStaff;
-- 원하는 별명으로 열을 지정했으면, 기존 이름이 아닌 별명을 이용해야 한다.
SELECT * FROM vStaffAlias ORDER BY s; -- 맞음
SELECT * FROM vStaffAlias ORDER BY salary; -- 에러
CREATE VIEW vStaffBonus AS SELECT name, salary * score / 100 AS bonus FROM tStaff;
SELECT * FROM vStaffBonus WHERE bonus > 300;
CREATE VIEW vShopping AS
SELECT M.member, M.addr, O.item, O.num, O.orderDate FROM tMember M
INNER JOIN tOrder O ON M.member = O.member;
CREATE VIEW vUnion AS
SELECT name, salary FROM tStaff WHERE depart = '인사과'
UNION
SELECT name, salary FROM tEmployee;
CREATE VIEW vvStaffHorz AS
SELECT name, joindate, salary FROM vStaffHorz;
CREATE VIEW vOriginal AS SELECT a, b, c FROM tOriginal;
CREATE VIEW vOriginal(a, b, c) AS SELECT x, y, c FROM tOriginal;
UPDATE vMember SET addr = '서울 신사동' WHERE member = '춘향';
UPDATE vStaffBonus SET bonus = 500 WHERE name = '유관순';
INSERT INTO vStaffHorz VALUES ('김한슬', '총무부', '여', '2022/08/14', '사원', 520, 55);
-- vStaffHorz는 '총무부'에 해당하는 사원만 저장하는 뷰이다.
-- 여기에 '영업부' 사원을 넣으면 삽입은 되지만 뷰에서 확인할 수가 없다!
INSERT INTO vStaffHorz VALUES ('김한결', '영업부', '남', '2023/05/13', '사원', 490, 35);
-- 이를 해결하기 위해서 WITH CHECK OPTION을 넣어주면, 자동으로 넣을 수 없는 행의 삽입을 막는다.
CREATE VIEW vStaffHorzCheck AS
SELECT * FROM tStaff WHERE depart = '총무부' WITH CHECK OPTION;
DELETE FROM tStaff WHERE name IN ('김한슬','김한결');
INSERT INTO vStaffHorzCheck VALUES ('김한결', '영업부', '남', '2023/05/13', '사원', 490, 35);
UPDATE vStaffHorzCheck SET depart = '기획팀' WHERE name = '김유신';
INSERT INTO vStaffVirt (name, depart, salary) VALUES ('이완용', '영업부', 99);
-- WITH READ ONLY로 읽기 전용 뷰를 만들 수 있다.
CREATE VIEW vStaffReadOnly AS SELECT * FROM tStaff
WHERE depart = '인사과' WITH READ ONLY;
-- WITH ENCRYPTION으로 암호화도 가능하다.
CREATE VIEW vMemberEnc WITH ENCRYPTION AS
SELECT member, age, addr FROM tMember;
-- 임시 테이블은 뷰가 아닌 실제 테이블처럼 사용할 수 있다.
-- 임시 테이블을 활용하면 원본 데이터를 건드리지 않고 복잡한 작업을 할 수 있다.
-- 데이터 저장 수명에 대한 두 가지 옵션이 있다.
-- ON COMMIT DELETE ROWS : 커밋하면 데이터가 사라진다.
-- ON COMMIT PRESERVE ROWS : 세션 중에는 데이터를 유지하며 세션이 종료하면 사라진다.
CREATE GLOBAL TEMPORARY TABLE tGtt (
name VARCHAR(20) PRIMARY KEY,
score INT
);
INSERT INTO tGtt VALUES ('김한슬', 95);
INSERT INTO tGtt VALUES ('김한결', 80);
SELECT * FROM tGtt;
-- 세션 내부에서만 데이터 확인이 가능하다. (다른 세션에서는 확인 불가)
DROP TABLE tGtt;
CREATE GLOBAL TEMPORARY TABLE tGtt (
name VARCHAR(20) PRIMARY KEY,
score INT
) ON COMMIT PRESERVE ROWS;
INSERT INTO tGtt VALUES ('김규민', 70);
SELECT * FROM tGtt;
-- #... 은 SQL 문법이다. (CREATE와 INSERT를 한방에!)
CREATE TABLE #tTemp (
name VARCHAR(20) PRIMARY KEY,
score INT
);
INSERT INTO #tTemp VALUES ('김한슬', 95);
INSERT INTO #tTemp VALUES ('김한결', 80);
INSERT INTO #tTemp VALUES ('김규민', 70);
SELECT * FROM #tTemp;
CREATE TEMPORARY TABLE tTemp (
name VARCHAR(20) PRIMARY KEY,
score INT
);
INSERT INTO tTemp VALUES ('김한슬', 95);
INSERT INTO tTemp VALUES ('김한결', 80);
INSERT INTO tTemp VALUES ('김규민', 70);
SELECT * FROM tTemp;
CREATE GLOBAL TEMPORARY TABLE tTaskForce AS SELECT * FROM tStaff;
INSERT INTO tTaskForce SELECT * FROM (SELECT * FROM tStaff
WHERE joindate <= '20160101' ORDER BY salary DESC) WHERE rownum <= 10;
MSSQL : SELECT TOP 10 * INTO #tTaskForce FROM tStaff
WHERE joindate <= '20160101' ORDER BY salary DESC;
마리아 : CREATE TEMPORARY TABLE tTaskForce AS SELECT * FROM tStaff
WHERE joindate <= '20160101' ORDER BY salary DESC LIMIT 10;
DELETE FROM tTaskForce WHERE score < (SELECT AVG(score) FROM tTaskForce
WHERE gender = '남') AND gender = '남';
DELETE FROM tTaskForce WHERE salary <
(SELECT AVG(salary) FROM tStaff) AND gender = '여';
DELETE FROM tTaskForce WHERE salary > 300 AND grade = '대리';
INSERT INTO tTaskForce SELECT * FROM tStaff WHERE salary > 380 AND grade = '과장';
SELECT name, salary, score FROM tStaff WHERE depart = '영업부' AND gender = '남';
-- 중첩 서브쿼리를 사용할 수도 있지만...
SELECT * FROM
(
SELECT name, salary, score FROM tStaff WHERE depart = '영업부' AND gender = '남'
) A
WHERE salary >= (
SELECT avg(salary) FROM
(
SELECT name, salary, score FROM tStaff WHERE depart = '영업부' AND gender = '남'
) B
);
-- 임시 테이블을 이용하면 더 간단하게 표현할 수 있다.
CREATE GLOBAL TEMPORARY TABLE tBusiMan AS SELECT name, salary, score FROM tStaff;
INSERT INTO tBusiMan SELECT name, salary, score FROM tStaff
WHERE depart = '영업부' AND gender = '남';
MSSQL: SELECT name, salary, score INTO #tBusiMan FROM tStaff
WHERE depart = '영업부' AND gender = '남';
마리아 : CREATE TEMPORARY TABLE tBusiMan AS
SELECT name, salary, score FROM tStaff WHERE depart = '영업부' AND gender = '남';
SELECT * FROM tBusiMan WHERE salary >= (SELECT avg(salary) FROM tBusiMan);
CREATE VIEW vBusiMan AS SELECT name, salary, score FROM tStaff
WHERE depart = '영업부' AND gender = '남';
SELECT * FROM vBusiMan WHERE salary >= (SELECT avg(salary) FROM vBusiMan);
-- CTE : 뷰와 임시 테이블은 결국 DB에 저장되지만, CTE는 쿼리를 호출할 때
-- 메모리에만 잠시 저장되어 더 빠른 속도를 보인다.
-- 정의와 조회 등의 쿼리를 한번에 실행해야 한다.
WITH tBusiMan AS
(SELECT name, salary, score FROM tStaff WHERE depart = '영업부' AND gender = '남')
SELECT * FROM tBusiMan WHERE salary >= (SELECT avg(salary) FROM tBusiMan);
WITH tBusiMan(이름, 월급, 성취도) AS
(SELECT name, salary, score FROM tStaff WHERE depart = '영업부' AND gender = '남')
SELECT * FROM tBusiMan WHERE 월급 >= (SELECT avg(월급) FROM tBusiMan);
WITH tBusiMan AS
(SELECT name, salary, score FROM tStaff WHERE depart = '영업부' AND gender = '남'),
tBusiGirl AS
(SELECT name, salary, score FROM tStaff WHERE depart = '영업부' AND gender = '여')
SELECT * FROM tBusiGirl WHERE salary >= (SELECT avg(salary) FROM tBusiMan);
WITH tBusiMan AS
(SELECT name, salary, score FROM tStaff WHERE depart = '영업부' AND gender = '남'),
tBusiManGod AS
(SELECT name, salary, score FROM tBusiMan WHERE score > 70)
SELECT * FROM tBusiManGod;
WITH Shopping AS
(SELECT M.member, M.addr, O.item, O.num, O.orderDate FROM tMember M
INNER JOIN tOrder O ON M.member = O.member)
SELECT * FROM Shopping WHERE num >= (SELECT AVG(num) FROM Shopping);
-- CTE는 내부 동작을 파악하기 어렵기 때문에 VIEW를 이용해서 생김새를 파악하는 방법도 있다.
CREATE VIEW vTemp AS
(SELECT M.member, M.addr, O.item, O.num, O.orderDate FROM tMember M
INNER JOIN tOrder O ON M.member = O.member);
SELECT * FROM vTemp WHERE num >= (SELECT AVG(num) FROM vTemp);
-- CTE는 재귀적으로 활용 가능하다. (중첩 CTE의 특수한 예)
WITH tFact(num, sum) AS (
SELECT 1 AS num, 1 AS sum FROM dual
UNION ALL
SELECT num + 1, sum * (num + 1) FROM tFact T WHERE T.num < 10
)
SELECT * FROM tFact;
WITH tTree(id, name, parent, depth) AS
(
SELECT id, name, parent, 0 FROM tDirectory WHERE parent = 0
UNION ALL
SELECT D.id, D.name, D.parent, T.depth + 1 FROM tDirectory D
INNER JOIN tTree T ON D.parent = T.id
)
SELECT * FROM tTree;
-- 다음은 디렉터리를 표현하는 SQL CTE 쿼리이다. (VER.1)
WITH tTree(id, name, parent, depth, fullpath) AS
(
SELECT id, name, parent, 0, CAST(name AS VARCHAR(256))
FROM tDirectory WHERE parent = 0
UNION ALL
SELECT D.id, D.name, D.parent, T.depth + 1,
CAST(CONCAT(CONCAT(T.fullpath, '/'), D.name) AS VARCHAR(256))
FROM tDirectory D
INNER JOIN tTree T ON D.parent = T.id
)
SELECT * FROM tTree;
-- 다음은 디렉터리를 표현하는 SQL CTE 쿼리이다. (VER.2)
WITH tTree(id, name, parent, depth, fullpath) AS
(
SELECT id, name, parent, 0, CAST(name AS VARCHAR(256))
FROM tDirectory WHERE parent = 0
UNION ALL
SELECT D.id, D.name, D.parent, T.depth + 1,
LPAD('L ', (T.depth + 1) * 4) || D.name
FROM tDirectory D
INNER JOIN tTree T ON D.parent = T.id
)
SELECT fullpath FROM tTree;
CAST(SPACE((T.depth + 1) * 4) + 'L ' + D.name AS VARCHAR(256))
'Database' 카테고리의 다른 글
SQL 기초 - 14. 테이블 관리 (테이블 수정, 필드 수정, SQL ALTER) (0) | 2021.10.21 |
---|---|
SQL 기초 - 13. 분석과 통계(소계, 순위, 통계, 피봇) (0) | 2021.10.21 |
SQL 기초 - 11. 함수 (스칼라 함수, 문자열 함수, 데이터 타입, 날짜와 시간) (0) | 2021.10.19 |
SQL 기초 - 10. 조인 (단순 조인, 외부 조인, 다중 조인 등 SQL 조인 예제) (0) | 2021.10.18 |
SQL 기초 - 9. 서브쿼리 (서브쿼리 연산자, 인라인 뷰, 테이블 조합, 테이블 병합) (1) | 2021.10.18 |
Comments