- 뷰생성
CREATE VIEW EMP_VIEW (deptno,h_yymm, sel_tot, comm_tot)
AS SELECT deptno, TO_CHAR(hiredate,'yymm'),
SUM(sal), SUM(comm)
FROM EMP
WHERE job = 'MANAGER'
GROUP BY deptno, TO_CHAR(hiredate,'YYMM');
SELECT *
FROM EMP_VIEW x, DEPT y
WHERE x.deptno = y.deptno
AND x.deptno BETWEEN 10 AND 50;
=> EMP_VIEW가 먼저 처리되고 DEPT 테이블이 연결되는 방식으 조인 최적의 수행속도 보장
=> 'AND x.deptno BETWEEN 10 AND 50'의 저건이 VIEW내에 파고들어 해당범위만 액세스
=> GROUP BY 된 로우에 대해서만 DEPT테이블을 연결하는 실행
=> 결과
SQL> alter session set optimizer_mode=all_rows; , CHOOSE, RULE 모두동일함.
세션이 변경되었습니다.
SQL> select * from EMP_VIEW x, dept y
2 where x.deptno = y.deptno
3 and x.deptno between 10 and 50;
Execution Plan
----------------------------------------------------------
Plan hash value: 2491490417
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 219 | 5 (20)| 00:00:01 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 3 | 219 | 5 (20)| 00:00:01 |
| 3 | VIEW | EMP_VIEW | 3 | 129 | 4 (25)| 00:00:01 |
| 4 | HASH GROUP BY | | 3 | 240 | 4 (25)| 00:00:01 |
|* 5 | TABLE ACCESS FULL | EMP | 3 | 240 | 3 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 30 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - filter("JOB"='MANAGER' AND "DEPTNO">=10 AND "DEPTNO"<=50)
6 - access("X"."DEPTNO"="Y"."DEPTNO")
filter("Y"."DEPTNO">=10 AND "Y"."DEPTNO"<=50)
Note
-----
- dynamic sampling used for this statement
SELECT *
FROM EMP_VIEW x, DEPT y
WHERE x.deptno = y.deptno
AND y.deptno BETWEEN 10 AND 50;
=> DEPT테이블이 드라이빙 테이블이 된다면 각각의 y.deptno가 뷰의 조건으로 파고들어 여러번 'GROUP BY'가 일어나는 불리한 실행
=> 결과
SQL> SELECT * FROM EMP_VIEW x, dept y
2 WHERE x.deptno = y.deptno
3 AND y.deptno BETWEEN 10 AND 50;
Execution Plan
----------------------------------------------------------
Plan hash value: 2491490417
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 219 | 5 (20)| 00:00:01 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 3 | 219 | 5 (20)| 00:00:01 |
| 3 | VIEW | EMP_VIEW | 3 | 129 | 4 (25)| 00:00:01 |
| 4 | HASH GROUP BY | | 3 | 240 | 4 (25)| 00:00:01 |
|* 5 | TABLE ACCESS FULL | EMP | 3 | 240 | 3 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 30 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - filter("JOB"='MANAGER' AND "DEPTNO">=10 AND "DEPTNO"<=50)
6 - access("X"."DEPTNO"="Y"."DEPTNO")
filter("Y"."DEPTNO">=10 AND "Y"."DEPTNO"<=50)
Note
-----
- dynamic sampling used for this statement
CREATE VIEW MERITS ( empno, ename, deptno, job, mgr, merits, grade)
AS SELECT empno, ename, deptno, job, mgr, merits, grade
FROM EMP
WHERE deptno IN ( SELECT deptno
FROM DEPT
WHERE deptname = user )
AND TO_CHAR(sysdate,'HH24MI') BETWEEN '0900' AND '1800';
CREATE ROLE merit_role_1 IDENTIFIED BY password ;
CREATE ROLE merit_role_2 IDENTIFIED BY password ;
GRANT SELECT ON MERITS TO merit_role_1 ;
GRANT all ON MERITS TO merit_role_1 ;
-- 'ROLE'을 이용하여 사용자별 보안 관리함.
CREATE VIEW BOUNS_TAB
AS
SELECT empno, ename, sal*decode(job, 'MANAGER',1.1,
'SALESMAN',1.12, 1.05) BOUNS
FROM EMP;
-- VIEW의 쿼리문이 상황에 따라 자주 변경된다면 더욱 뷰를 이용하는 것이 편리다.
기존의 것을 수정하지 않아도되므로 융통성이 향상될것이다.
-- 특정한 컬럼에 대해서 입력, 수정을 허용하지 않은 뷰는 해당컬럼을 가공시킨 뷰
-- 입력,수정은 불가능하나 삭제는 가능한 뷰
CREATE VIEW TEST_VIEW
AS
SELECT empno,ename, NVL(SAL,0) sal
FROM EMP;
=>결과
SQL> INSERT INTO TEST_VIEW VALUES(9999,'AAAA',99999);
INSERT INTO TEST_VIEW VALUES(9999,'AAAA',99999)
*
1행에 오류:
ORA-01733: 가상 열은 사용할 수 없습니다
SQL> UPDATE TEST_VIEW SET SAL=SAL+1000;
UPDATE TEST_VIEW SET SAL=SAL+1000
*
1행에 오류:
ORA-01733: 가상 열은 사용할 수 없습니다
SQL> DELETE TEST_VIEW;
14 행이 삭제되었습니다.
-----------------------------------------------------------------------------
-- 검색을 제외한 모든 데이터처리 봉쇄 'WITH READ ONLY' 의 뷰
-- 오직 검색(Select)만 가능
CREATE VIEW TEST_VIEW1
AS
SELECT empno,ename, NVL(SAL,0) sal
FROM EMP
WITH READ ONLY;
-- 결과
SQL> SELECT * FROM TEST_VIEW1;
EMPNO ENAME SAL
---------- ---------- ----------
7369 SMITH 800
7499 ALLEN 1600
7521 WARD 1250
7566 JONES 2975
7654 MARTIN 1250
7698 BLAKE 2850
7782 CLARK 2450
7788 SCOTT 3000
7839 KING 5000
7844 TURNER 1500
7876 ADAMS 1100
EMPNO ENAME SAL
---------- ---------- ----------
7900 JAMES 950
7902 FORD 3000
7934 MILLER 1300
14 개의 행이 선택되었습니다.
SQL> INSERT INTO TEST_VIEW1 VALUES(9999,'AAAA',99999);
INSERT INTO TEST_VIEW1 VALUES(9999,'AAAA',99999)
*
1행에 오류:
ORA-42399: 읽기 전용 뷰에서는 DML 작업을 수행할 수 없습니다.
SQL> DELETE TEST_VIEW1;
DELETE TEST_VIEW1
*
1행에 오류:
ORA-42399: 읽기 전용 뷰에서는 DML 작업을 수행할 수 없습니다.
-----------------------------------------------------------
-- 어떤 컬럼의 특정한 범위에 있는 값에 대해서만 테이터 처리를 봉쇄하고자할때 'WITH CHECK OPTION' 의 뷰
-- DEPTNO IN (10,30) 이 아닌경우에는 입력, 수정 불가능
CREATE VIEW TEST_VIEW2
AS
SELECT empno,ename, deptno, NVL(SAL,0) sal
FROM EMP
WHERE deptno in (10,30)
WITH CHECK OPTION;
=> 중요한 보안 관리가 요구되는 테이블
=> 뷰의 사용 권한을 변경한다고 하더라도 테이블의 사용권한을 병경시킬수가 없기 때문에 보다 확실한 보안을 관리 할수있다.
-- 같은 종류의 뷰는 각종 'DECODE'를 이용하여 여러가지 경우를 분류하는 경우 유용하게 사용될수있다.
CREATE view cur_emp
AS
SELECT * from emp
WHERE ( retire_date = '000000' OR retiredate is null
OR resume_date = '000000' AND resume_date is null )
AND company = '1';
CREATE VIEW "부서" ("부서번호","부서명","위치")
AS SELECT deptno, dname, loc FROM DEPT ;
=>결과
SQL> SELECT * FROM 부서;
부서번호 부서명 위치
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
=>RDBMS의 데이터의 중복을 최소화 하고, 일관성을 보장하기 위해 정규화 과정을 거치면서 불가피하게 조인을 발생이된다.
=> 조인 뷰를 활용하면 마치 테이블을 정규화하지 않은 형태로 생성한것 처럼 사용 할수 있다.
CREATE VIEW cus_me2v
AS
SELECT c.custno, c.custname, m.saleno, m.saledept
FROM CUSTOMER c, MECHULT m
WHERE c.custno = m.custno
AND c.nation = 'KOR'
AND m.market ='L';
-- DECODE 함수를 사용하여 뷰를 생성하므로써 일반 사용자는 내부적인 알고리즘을 모르는체 원하는 결과를 추출할 수 있다.
CREATE VIEW dept_year_sale
AS
SELECT saledept,
SUM(DECODE(SUBSTR(saledate,3,2),'01',saleqyt)) m01,
SUM(DECODE(SUBSTR(saledate,3,2),'02',saleqyt)) m02,
SUM(DECODE(SUBSTR(saledate,3,2),'03',saleqyt)) m03,
SUM(DECODE(SUBSTR(saledate,3,2),'04',saleqyt)) m04,
SUM(DECODE(SUBSTR(saledate,3,2),'05',saleqyt)) m05,
SUM(DECODE(SUBSTR(saledate,3,2),'06',saleqyt)) m06,
SUM(DECODE(SUBSTR(saledate,3,2),'07',saleqyt)) m07,
SUM(DECODE(SUBSTR(saledate,3,2),'08',saleqyt)) m08,
SUM(DECODE(SUBSTR(saledate,3,2),'09',saleqyt)) m09,
SUM(DECODE(SUBSTR(saledate,3,2),'10',saleqyt)) m10,
SUM(DECODE(SUBSTR(saledate,3,2),'11',saleqyt)) m11,
SUM(DECODE(SUBSTR(saledate,3,2),'12',saleqyt)) m12,
SUM(saleqty) total,
FROM MECHUL2t
WHERE saledte like TO_CHAR(sysdate,'yy')||'%'
GROUP BY saledept ;
=> 조인문은 먼저 조인이 모두 수행된 후에 'GROUP BY'를 하였으므로 불필요한 조인이 많이 발생한다.
SELECT d.dname, d.loc, sum(s.salqty), avg(s.salqty), max(s.salqty), min(s.salqty)
FROM DEPT d, SALET s
WHERE d.deptno = s.deptno
AND s.yymm = '9510'
GROUP BY d.dname
ORDER BY d.loc ;
=> 뷰를 SQL에서 부여한 조건이 뷰내에 파고들어 뷰가 먼저 수행된 후 DEPT테이블과 조인을 일으킨 경우 연결횟수가 줄어들므로 수행속도가 향상된다.
=> DEPT_SAL 뷰 생성
CREATE VIEW DEPT_SAL (deptno, yymm, totqty, avgqty, maxqty, minqty)
AS
SELECT deptno, yymm, SUM(salqty), AVG(salqty), MAX(salqty), MIN(salqyt)
FROM SALET
GROUP BY deptno, yymm ;
=> 'v.yymm='9510'' 조건문이 VIEW내에 먼저 들어가 'yymm='9510'' 을 실행시키고 GROUP BY를 수행하여 DEPT테이블과 조인을 하므로 각 부서별로 한번씩 조인되어 수행된다.
SELECT d.dname, v.totqty, v.avgqty, v.maxqty, v.minqty
FROM DEPT d, DEPT_SAL v
WHERE d.deptno = v.deptno
AND v.yymm='9510'
ORDER BY loc ;
SELECT d.dname, v.totqty, v.avgqty, v.maxqty, v.minqty
FROM ( SELECT deptno, SUM(salqty) as totqty,
AVG(salqty) as avgqty,
MAX(salqty) as maxqty,
MIN(salqty) as minqty
FROM SALET
WHERE yymm='9510'
GROUP BY deptno ) v, DEPT D
WHERE v.deptno = d.deptno
ORDER BY d.loc ;
-- 기본적으로 VIEW내에 삽입된 현재월이 처리범위가 되어 '방호벽' 역할을 하므로 전체 테이블을 액세스하여
'GROUP BY'하는 악성 검색을 막을수 있다
CREATE VIEW ITEM_SALE ( saldate, item, highqty, lowqty, totqty )
AS
SELECT x.saldate, x.item,
SUM(DECODE(y.type, 'H', x.salqty)),
SUM(DECODE(y.type, 'L', x.salqty)),
SUM(x.salqty))
FROM SALET x, ITEM y
WHERE x.item = y.item
AND x.saldate like to_char(sysdate,'yymm'))||'%'
GROUP BY x.saldate, item ;
SELECT saldate, item, highqty, lowqty, totqty
FROM ITEM_SALE
WHERE item ='ABCD';
SELECT saldate, item, highqty, lowqty, totqty
FROM ITEM_SALE
WHERE saldate = '951001';
-- VIEW내에서도 힌트를 적용하여 SQL을 추출할수있다.
CREATE VIEW MAX_NUM( saldept, maxseq)
AS SELECT + INDEX_DESC (A pk_index) */
saldept, ordseq + 1
FROM ORDER A
WHERE saledept > ' '
AND rownum = 1 ;
1) 기존에 사용하던 테이블들의 이름을 다음과 같이 변경시킨다.
[SYNCTAX]
RENAME old_object_name TO rename_object_name;
2) 과거 사용하던 오브젝트 명칭으로 다음과 같은 뷰를 생성한다.
CREATE VIEW old_object_name (old_column,....)
AS
SELECT new_column,...
FROM new_object_name;
-- 어플리케이션에는 뷰를 사용한 SQL을 삽입하고 만약 금액 산정 기준이 변경된다면 뷰만 수정하여 재생성(Recreation), 혹은
대채(Replace)시키면 관련되 모든 애플리케이션을 수정할 필요가 없어진다.
CREATE or REPLACE VIEW overtime
( empno, work_day, work_hour, pay_amt )
AS
SELECT empno, work_day, work_hour
decode(sign(2-work_hour), 1,5000,
decode(sign(4-work_hour), 1,12000,
decode(sign(5-work_hour), 1,18000, 30000 )))
FROM EMPWORK ;
;
-- 'B D A C H E G F' 순으로 정렬을 원한다면 다음과같이 VIEW를 생성하여 활용할수있다.
CREATE VIEW PROD_VIEW
( sortno, prod, item, pdate, cost, prodqty )
AS
SELECT DECODE(prod, 'A',3,'B',1,'C',4,'D',2,'E',6,'F',8,'G',7,'H',5),
prod, item, pdate, cost, nvl(prodqty,0)
FROM PRODUCT ;
SELECT * FROM PROD_VIEW
WHERE pdate like '9501%'
ORDER BY sortno;
-- 'GROUP BY'의 결과를 이용하여 'COUNT, MAX, MIN, AVG'를 표현한 VIEW
CREATE VIEW SAL_VIEW
( prod, prod_cnt, prod_avg, prod_max, prod_min )
AS
SELECT prod, COUNT(*), AVG(salqty), MAX(salqty), MIN(salqty)
FROM SALET
GROUP BY prod ;
SELECT COUNT(*)
FROM SAL_VIEW
WHERE prod like 'AB%';
-- 다른 테이블에 동일한 종류의 컬럼을 각각 'GROUP BY'한 후 같은 종류의 값을 가진 것들을 같은 줄에 맞추고자 한경우
CREATE VIEW GROUP_VIEW ( empno, yymmdd, type, cnt )
AS
SELECT empno, accept_date, '1', COUNT(*)
FROM 접수
GROUP BY empno, accept_date
union all
SELECT empno, action_date,'2',COUNT(*)
FROM 처리
GROUP BY empno, action_date
union all
SELECT empno, approv_date, '3', COUNT(*)
FROM 결재
GROUP BY empno, approv_date ;
SELECT empno,
SUM(DECODE(type,'1',cnt)) "접수 횟수",
SUM(DECODE(type,'2',cnt)) "처리 횟수",
SUM(DECODE(type,'3',cnt)) "결재 횟수",
SUM(cnt) "소계"
FROM GROUP_VIEW
WHERE yymmdd like '9510%'
GROUP BY empno ;
SELECT prod,DECODE(y.num,1,'소계',grade),
NVL(SUM(d_tot_qty),0), NVL(SUM(d_tot_amt),0), NVL(SUM(m_tot_qty),0), NVL(SUM(m_tot_amt),0)
FROM ( SELECT prod, grade,
SUM(DECODE(saldate,TO_CHAR(sysdate,'yymmdd'),d_qty)) as d_tot_qty,
SUM(DECODE(saldate,TO_CHAR(sysdate,'yymmdd'),d_amt)) as d_tot_amt,
SUM(qty) as m_tot_qty, SUM(amt) as m_tot_amt
FROM SALET
WHERE saldate like '9510%'
GROUP BY prod, grade) x, CARTESIAN y
WHERE y.num <= 2
GROUP BY prod, DECODE(y.num, 1, '소계', grade)
ORDER BY 1,2;
결과
제 품 명 등 급 당일 판매량 당일 매출액 월간 판매량 원간 매출액
--------- ------ ---------- ---------- ---------- --------------
책상 소 계 3,650 5,858,000 65,010 3,772,002,000
HA220 200 1,580,000 3,010 50,255,600
HA410 350 2,320,000 6,000 120,546,000
HG100 3100 1,958,000 53,000 3,601,200,400
...... ...... ..... ..... ...... ............