1. test script 준비(2010 하반기 SCRIPT 참조)
- 고객 테이블 및 데이터 생성
SQL> CREATE TABLE 고객 AS
2 SELECT LEVEL AS 고객번호
3 , (SELECT SYSDATE - CEIL(DBMS_RANDOM.VALUE(1, 365)) FROM DUAL) AS 최종거래일시
4 , 0 AS 최근거래횟수
5 , 0 AS 최근거래금액
6 FROM DUAL
7 CONNECT BY LEVEL <= 1000000;
테이블이 생성되었습니다.
- 고객 테이블 인덱스 생성
SQL> ALTER TABLE 고객 ADD CONSTRAINT IDX_고객_PK PRIMARY KEY(고객번호);
테이블이 변경되었습니다.
- 거래 테이블 및 데이터 생성
SQL> CREATE TABLE 거래 AS
2 SELECT CEIL(LEVEL / 1000000) 고객번호
3 , ADD_MONTHS(SYSDATE,-4) + FLOOR( DBMS_RANDOM.VALUE(1,120) ) AS 거래일시
4 , (FLOOR( DBMS_RANDOM.VALUE(1,13) )*100) + 500 AS 거래금액
5 FROM DUAL
6 CONNECT BY LEVEL <= 10000000;
테이블이 생성되었습니다.
※ SQL TRACE
********************************************************************************
SQL> EXPLAIN PLAN FOR
2 UPDATE 고객 c
3 SET 최종거래일시 = ( SELECT MAX(거래일시) FROM 거래
4 WHERE 고객번호 = c.고객번호
5 AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
6 , 최근거래횟수 = ( SELECT COUNT(*) FROM 거래
7 WHERE 고객번호 = c.고객번호
8 AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
9 , 최근거래금액 = ( SELECT SUM(거래금액) FROM 거래
10 WHERE 고객번호 = c.고객번호
11 AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
12 WHERE EXISTS ( SELECT 'x' FROM 거래
13 WHERE 고객번호 = c.고객번호
14 AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)));
해석되었습니다.
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
Plan hash value: 240844059
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 10 | 700 | | 13553 (9)| 00:02:43 |
| 1 | UPDATE | 고객 | | | | | |
|* 2 | HASH JOIN SEMI | | 10 | 700 | 59M| 13553 (9)| 00:02:43 |
| 3 | TABLE ACCESS FULL| 고객 | 1035K| 47M| | 720 (6)| 00:00:09 |
|* 4 | TABLE ACCESS FULL| 거래 | 1832K| 38M| | 6852 (16)| 00:01:23 |
| 5 | SORT AGGREGATE | | 1 | 22 | | | |
|* 6 | TABLE ACCESS FULL| 거래 | 18326 | 393K| | 6135 (6)| 00:01:14 |
| 7 | SORT AGGREGATE | | 1 | 22 | | | |
|* 8 | TABLE ACCESS FULL| 거래 | 18326 | 393K| | 6135 (6)| 00:01:14 |
| 9 | SORT AGGREGATE | | 1 | 35 | | | |
|* 10 | TABLE ACCESS FULL| 거래 | 18326 | 626K| | 6135 (6)| 00:01:14 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("고객번호"="C"."고객번호")
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
4 - filter("거래일시">=TRUNC(ADD_MONTHS(SYSDATE@!,-1)))
6 - filter("고객번호"=:B1 AND "거래일시">=TRUNC(ADD_MONTHS(SYSDATE@!,-1)))
8 - filter("고객번호"=:B1 AND "거래일시">=TRUNC(ADD_MONTHS(SYSDATE@!,-1)))
10 - filter("고객번호"=:B1 AND "거래일시">=TRUNC(ADD_MONTHS(SYSDATE@!,-1)))
Note
-----
- dynamic sampling used for this statement
※Semi Join (반조인)
선행 Table의 Row가 수행 Table의 Row와 Match되기만 하면 즉각 Join 조건이 만족된 것으로 간주하고 해당 Row에
대해서는 더 이상의 탐색을 진행하지 않는다. 따라서 보다 효율적이다.
Exists와 In Operation의 효율적인 처리를 위한 고안된 Join 방식이다.
주로 Hash Join(Hash Semi Join)의 형태나 Nested Loops Join(Nested Loops Semi Join)의 형태로 구현된다.
Sort Merge Semi Join 또한 이론적으로는 발생 가능하다.
********************************************************************************
※ TKPROF LOG
********************************************************************************
UPDATE 고객 c
SET 최종거래일시 = ( SELECT MAX(거래일시) FROM 거래
WHERE 고객번호 = c.고객번호
AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
, 최근거래횟수 = ( SELECT COUNT(*) FROM 거래
WHERE 고객번호 = c.고객번호
AND 거래일시 >= TRUNC(ADD_MONTHet(SYSDATE, -1)))
, 최근거래금액 = ( SELECT SUM(거래금액) FROM 거래
WHERE 고객번호 = c.고객번호
AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
WHERE EXISTS ( SELECT 'x' FROM 거래
WHERE 고객번호 = c.고객번호
AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.18 0 7 0 0
Execute 1 87.45 115.94 29727 819213 23 10
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 87.46 116.12 29727 819220 23 10
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 54
Rows Row Source Operation
------- ---------------------------------------------------
0 UPDATE 고객 (cr=819213 pr=29727 pw=4867 time=115941430 us)
10 HASH JOIN SEMI (cr=29373 pr=6550 pw=4867 time=24677884 us)
1000000 TABLE ACCESS FULL 고객 (cr=3045 pr=0 pw=0 time=5000047 us)
2352766 TABLE ACCESS FULL 거래 (cr=26328 pr=2613 pw=0 time=54130598 us)
10 SORT AGGREGATE (cr=263280 pr=23177 pw=0 time=40484285 us)
2352766 TABLE ACCESS FULL 거래 (cr=263280 pr=23177 pw=0 time=37635492 us)
10 SORT AGGREGATE (cr=263280 pr=0 pw=0 time=25108757 us)
2352766 TABLE ACCESS FULL 거래 (cr=263280 pr=0 pw=0 time=27684837 us)
10 SORT AGGREGATE (cr=263280 pr=0 pw=0 time=25310851 us)
2352766 TABLE ACCESS FULL 거래 (cr=263280 pr=0 pw=0 time=27495547 us)
********************************************************************************
-- 한 달 이내 거래가 있던 고객을 두번 조회하는 것으로 변경, 총 고객 수와 한 달 이내 거래가 발생한 고객 수에 따라 성능이 좌우된다.
※ SQL TRACE
********************************************************************************
SQL> EXPLAIN PLAN FOR
2 UPDATE 고객 c
3 SET ( 최종거래일시, 최근거래횟수, 최근거래금액 ) =
4 ( SELECT MAX(거래일시), COUNT(*), SUM(거래금액)
5 FROM 거래
6 WHERE 고객번호 = c.고객번호
7 AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
8 WHERE EXISTS ( SELECT 'x' FROM 거래
9 WHERE 고객번호 = c.고객번호
10 AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)));
해석되었습니다.
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
Plan hash value: 613077917
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 10 | 700 | | 13553 (9)| 00:02:43 |
| 1 | UPDATE | 고객 | | | | | |
|* 2 | HASH JOIN SEMI | | 10 | 700 | 59M| 13553 (9)| 00:02:43 |
| 3 | TABLE ACCESS FULL| 고객 | 1035K| 47M| | 720 (6)| 00:00:09 |
|* 4 | TABLE ACCESS FULL| 거래 | 1832K| 38M| | 6852 (16)| 00:01:23 |
| 5 | SORT AGGREGATE | | 1 | 35 | | | |
|* 6 | TABLE ACCESS FULL| 거래 | 18326 | 626K| | 6135 (6)| 00:01:14 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("고객번호"="C"."고객번호")
4 - filter("거래일시">=TRUNC(ADD_MONTHS(SYSDATE@!,-1)))
6 - filter("고객번호"=:B1 AND "거래일시">=TRUNC(ADD_MONTHS(SYSDATE@!,-1)))
Note
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
-----
- dynamic sampling used for this statement
********************************************************************************
※ TKPROF LOG
********************************************************************************
UPDATE 고객 c
SET ( 최종거래일시, 최근거래횟수, 최근거래금액 ) =
( SELECT MAX(거래일시), COUNT(*), SUM(거래금액)
FROM 거래
WHERE 고객번호 = c.고객번호
AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
WHERE EXISTS ( SELECT 'x' FROM 거래
WHERE 고객번호 = c.고객번호
AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.00 0 4 0 0
Execute 1 35.79 42.29 3937 292653 20 10
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 35.81 42.30 3937 292657 20 10
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 54
Rows Row Source Operation
------- ---------------------------------------------------
0 UPDATE 고객 (cr=292653 pr=3937 pw=4867 time=42291709 us)
10 HASH JOIN SEMI (cr=29373 pr=3937 pw=4867 time=16552524 us)
1000000 TABLE ACCESS FULL 고객 (cr=3045 pr=0 pw=0 time=4000037 us)
2352766 TABLE ACCESS FULL 거래 (cr=26328 pr=0 pw=0 time=21174969 us)
10 SORT AGGREGATE (cr=263280 pr=0 pw=0 time=25322376 us)
2352766 TABLE ACCESS FULL 거래 (cr=263280 pr=0 pw=0 time=29662943 us)
********************************************************************************
-- 총 고객수가 많다면 exists 서브 쿼리를 아래와 같이 해시 세미 조인으로 유도
※ SQL TRACE
********************************************************************************
SQL> EXPLAIN PLAN FOR
2 UPDATE 고객 c
3 SET ( 최종거래일시, 최근거래횟수, 최근거래금액 ) =
4 ( SELECT MAX(거래일시), COUNT(*), SUM(거래금액)
5 FROM 거래
6 WHERE 고객번호 = c.고객번호
7 AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
8 WHERE EXISTS ( SELECT /*+ unnest hash_sj */ 'x' FROM 거래
9 WHERE 고객번호 = c.고객번호
10 AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)));
해석되었습니다.
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
Plan hash value: 613077917
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 10 | 700 | | 13553 (9)| 00:02:43 |
| 1 | UPDATE | 고객 | | | | | |
|* 2 | HASH JOIN SEMI | | 10 | 700 | 59M| 13553 (9)| 00:02:43 |
| 3 | TABLE ACCESS FULL| 고객 | 1035K| 47M| | 720 (6)| 00:00:09 |
|* 4 | TABLE ACCESS FULL| 거래 | 1832K| 38M| | 6852 (16)| 00:01:23 |
| 5 | SORT AGGREGATE | | 1 | 35 | | | |
|* 6 | TABLE ACCESS FULL| 거래 | 18326 | 626K| | 6135 (6)| 00:01:14 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("고객번호"="C"."고객번호")
4 - filter("거래일시">=TRUNC(ADD_MONTHS(SYSDATE@!,-1)))
6 - filter("고객번호"=:B1 AND "거래일시">=TRUNC(ADD_MONTHS(SYSDATE@!,-1)))
Note
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
-----
- dynamic sampling used for this statement
********************************************************************************
※ TKPROF LOG
********************************************************************************
UPDATE 고객 c
SET ( 최종거래일시, 최근거래횟수, 최근거래금액 ) =
( SELECT MAX(거래일시), COUNT(*), SUM(거래금액)
FROM 거래
WHERE 고객번호 = c.고객번호
AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
WHERE EXISTS ( SELECT /*+ unnest hash_sj */ 'x' FROM 거래
WHERE 고객번호 = c.고객번호
AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)))
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 4 0 0
Execute 1 36.57 42.91 3937 292653 20 10
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 36.57 42.91 3937 292657 20 10
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 54
Rows Row Source Operation
------- ---------------------------------------------------
0 UPDATE 고객 (cr=292653 pr=3937 pw=4867 time=42912267 us)
10 HASH JOIN SEMI (cr=29373 pr=3937 pw=4867 time=16109592 us)
1000000 TABLE ACCESS FULL 고객 (cr=3045 pr=0 pw=0 time=4000038 us)
2352766 TABLE ACCESS FULL 거래 (cr=26328 pr=0 pw=0 time=21174967 us)
10 SORT AGGREGATE (cr=263280 pr=0 pw=0 time=25995009 us)
2352766 TABLE ACCESS FULL 거래 (cr=263280 pr=0 pw=0 time=29817758 us)
********************************************************************************
-- 한 달 이내 거래를 발생시킨 고객이 많아 update발생량이 많다면 아래와 같이 변경할 수 있으나,
-- 모든 고객 레코드에 lock이 발생하고 이전과 같은 값으로 갱신되는 비중이 높을수록 Redo 로그 발생량이 증가
※ SQL TRACE
********************************************************************************
SQL> EXPLAIN PLAN FOR
2 UPDATE 고객 c
3 SET ( 최종거래일시, 최근거래횟수, 최근거래금액 ) =
4 ( SELECT NVL(MAX(거래일시), c.최종거래일시)
5 , DECODE( COUNT(*), 0, c.최근거래횟수, COUNT(*))
6 , NVL(SUM(거래금액), c.최근거래금액)
7 FROM 거래
8 WHERE 고객번호 = c.고객번호
9 AND 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1)));
해석되었습니다.
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------
Plan hash value: 1071116265
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1035K| 47M| 720 (6)| 00:00:09 |
| 1 | UPDATE | 고객 | | | | |
| 2 | TABLE ACCESS FULL | 고객 | 1035K| 47M| 720 (6)| 00:00:09 |
| 3 | SORT AGGREGATE | | 1 | 35 | | |
|* 4 | TABLE ACCESS FULL| 거래 | 18326 | 626K| 6135 (6)| 00:01:14 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("고객번호"=:B1 AND "거래일시">=TRUNC(ADD_MONTHS(SYSDATE@!,-1)))
Note
-----
- dynamic sampling used for this statement
********************************************************************************
SQL> EXPLAIN PLAN FOR
2 UPDATE /*+ bypass_ujvc */ --bypass_ujvc 힌트는 view update시에 키보존을 생략할수 있게 해주는 힌트이다..
3 ( SELECT /*+ ordered use_hash(c) */
4 c.최종거래일시, c.최근거래횟수, c.최근거래금액
5 , t.거래일시, t.거래횟수, t.거래금액
6 FROM ( SELECT 고객번호, MAX(거래일시) 거래일시, COUNT(*) 거래횟수, SUM(거래금액) 거래금액
7 FROM 거래
8 WHERE 거래일시 >= TRUNC(ADD_MONTHS(SYSDATE, -1))
9 GROUP BY 고객번호) t, 고객 c
10 WHERE c.고객번호 = t.고객번호
11 )
12 SET 최종거래일시 = 거래일시
13 , 최근거래횟수 = 거래횟수
14 , 최근거래금액 = 거래금액 ;
해석되었습니다.
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------
Plan hash value: 1452902634
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1832K| 167M| | 16119 (10)| 00:03:14 |
| 1 | UPDATE | 고객 | | | | | |
|* 2 | HASH JOIN | | 1832K| 167M| 104M| 16119 (10)| 00:03:14 |
| 3 | VIEW | | 1832K| 83M| | 7155 (19)| 00:01:26 |
| 4 | SORT GROUP BY | | 1832K| 61M| | 7155 (19)| 00:01:26 |
|* 5 | TABLE ACCESS FULL| 거래 | 1832K| 61M| | 6859 (16)| 00:01:23 |
| 6 | TABLE ACCESS FULL | 고객 | 1035K| 47M| | 720 (6)| 00:00:09 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("C"."고객번호"="T"."고객번호")
5 - filter("거래일시">=TRUNC(ADD_MONTHS(SYSDATE@!,-1)))
Note
-----
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------
- dynamic sampling used for this statement
1. 1쪽 집합(dept)과 조인되는 M쪽 집합(emp)의 컬럼을 수정하므로 문제가 없어보이나, 수행하면 에러가 발생한다.
2. delete, insert 문도 에러가 발생한다.
3. dept테이블에 unique 인덱스를 생성하지 않았기 때문에 생긴 에러이다.
4. 1쪽 집합에 PK제약을 설정하거나 unique 인덱스를 생성하해야 수정 가능 조인 뷰를 통합 입력, 수정, 삭제가 가능하다.
5. dept테이블에 PK제약을 설정하면 emp 테이블은 키 보존 테이블, dept 테이블은 비 키-보존 테이블이 된다.
SQL> CREATE TABLE EMP AS SELECT * FROM SCOTT.EMP;
CREATE TABLE EMP AS SELECT * FROM SCOTT.EMP
*
1행에 오류:
ORA-00955: 기존의 객체가 이름을 사용하고 있습니다.
SQL> CONN / AS SYSDBA
연결되었습니다.
SQL> CREATE TABLE EMP AS SELECT * FROM SCOTT.EMP;
테이블이 생성되었습니다.
SQL> CREATE TABLE DEPT AS SELECT * FROM SCOTT.DEPT;
테이블이 생성되었습니다.
SQL> CREATE OR REPLACE VIEW EMP_DEPT_VIEW AS
2 SELECT E.ROWID EMP_RID, E.*, D.ROWID DEPT_RID, D.DNAME, D.LOC
3 FROM EMP E, DEPT D
4 WHERE E.DEPTNO = D.DEPTNO;
뷰가 생성되었습니다.
SQL> UPDATE EMP_DEPT_VIEW SET LOC = 'SEOUL' WHERE JOB = 'CLERK';
UPDATE EMP_DEPT_VIEW SET LOC = 'SEOUL' WHERE JOB = 'CLERK'
*
1행에 오류:
ORA-01779: 키-보존된것이 아닌 테이블로 대응한 열을 수정할 수 없습니다
SQL> SELECT EMPNO, ENAME, JOB, SAL, DEPTNO, DNAME, LOC
2 FROM EMP_DEPT_VIEW
3 ORDER BY JOB, DEPTNO;
EMPNO ENAME JOB SAL DEPTNO DNAME LOC
---------- ---------- --------- ---------- ---------- -------------- -------------
7902 FORD ANALYST 3000 20 RESEARCH DALLAS
7788 SCOTT ANALYST 3000 20 RESEARCH DALLAS
7934 MILLER CLERK 1300 10 ACCOUNTING NEW YORK
7369 SMITH CLERK 800 20 RESEARCH DALLAS
7876 ADAMS CLERK 1100 20 RESEARCH DALLAS
7900 JAMES CLERK 950 30 SALES CHICAGO
7782 CLARK MANAGER 2450 10 ACCOUNTING NEW YORK
7566 JONES MANAGER 2975 20 RESEARCH DALLAS
7698 BLAKE MANAGER 2850 30 SALES CHICAGO
7839 KING PRESIDENT 5000 10 ACCOUNTING NEW YORK
7654 MARTIN SALESMAN 1250 30 SALES CHICAGO
7844 TURNER SALESMAN 1500 30 SALES CHICAGO
7521 WARD SALESMAN 1250 30 SALES CHICAGO
7499 ALLEN SALESMAN 1600 30 SALES CHICAGO
14 개의 행이 선택되었습니다.
SQL> UPDATE EMP_DEPT_VIEW SET COMM = NVL(COMM, 0) + (SAL * 0.1) WHERE SAL <= 1500;
UPDATE EMP_DEPT_VIEW SET COMM = NVL(COMM, 0) + (SAL * 0.1) WHERE SAL <= 1500
*
1행에 오류:
ORA-01779: 키-보존된것이 아닌 테이블로 대응한 열을 수정할 수 없습니다
SQL> DELETE FROM EMP_DEPT_VIEW WHERE JOB = 'CLERK';
DELETE FROM EMP_DEPT_VIEW WHERE JOB = 'CLERK'
*
1행에 오류:
ORA-01752: 뷰으로 부터 정확하게 하나의 키-보전된 테이블 없이 삭제할 수 없습니다
SQL> ALTER TABLE DEPT ADD CONSTRAINT DEPT_PK PRIMARY KEY(DEPTNO);
테이블이 변경되었습니다.
SQL> UPDATE EMP_DEPT_VIEW SET COMM = NVL(COMM, 0) + (SAL * 0.1) WHERE SAL <= 1500;
7 행이 갱신되었습니다.
SQL> COMMIT;
커밋이 완료되었습니다.
SQL> SELECT ROWID, EMP_RID, DEPT_RID, EMPNO, DEPTNO FROM EMP_DEPT_VIEW;
ROWID EMP_RID DEPT_RID EMPNO DEPTNO
------------------ ------------------ ------------------ ---------- ----------
AAANM6AABAAAOxaAAA AAANM6AABAAAOxaAAA AAANM7AABAAAOxiAAB 7369 20
AAANM6AABAAAOxaAAB AAANM6AABAAAOxaAAB AAANM7AABAAAOxiAAC 7499 30
AAANM6AABAAAOxaAAC AAANM6AABAAAOxaAAC AAANM7AABAAAOxiAAC 7521 30
AAANM6AABAAAOxaAAD AAANM6AABAAAOxaAAD AAANM7AABAAAOxiAAB 7566 20
AAANM6AABAAAOxaAAE AAANM6AABAAAOxaAAE AAANM7AABAAAOxiAAC 7654 30
AAANM6AABAAAOxaAAF AAANM6AABAAAOxaAAF AAANM7AABAAAOxiAAC 7698 30
AAANM6AABAAAOxaAAG AAANM6AABAAAOxaAAG AAANM7AABAAAOxiAAA 7782 10
AAANM6AABAAAOxaAAH AAANM6AABAAAOxaAAH AAANM7AABAAAOxiAAB 7788 20
AAANM6AABAAAOxaAAI AAANM6AABAAAOxaAAI AAANM7AABAAAOxiAAA 7839 10
AAANM6AABAAAOxaAAJ AAANM6AABAAAOxaAAJ AAANM7AABAAAOxiAAC 7844 30
AAANM6AABAAAOxaAAK AAANM6AABAAAOxaAAK AAANM7AABAAAOxiAAB 7876 20
AAANM6AABAAAOxaAAL AAANM6AABAAAOxaAAL AAANM7AABAAAOxiAAC 7900 30
AAANM6AABAAAOxaAAM AAANM6AABAAAOxaAAM AAANM7AABAAAOxiAAB 7902 20
AAANM6AABAAAOxaAAN AAANM6AABAAAOxaAAN AAANM7AABAAAOxiAAA 7934 10
14 개의 행이 선택되었습니다.
SQL> ALTER TABLE DEPT DROP PRIMARY KEY;
테이블이 변경되었습니다.
SQL> SELECT ROWID, EMP_RID, DEPT_RID, EMPNO, DEPTNO FROM EMP_DEPT_VIEW;
SELECT ROWID, EMP_RID, DEPT_RID, EMPNO, DEPTNO FROM EMP_DEPT_VIEW
*
1행에 오류:
ORA-01445: 키 보존 테이블이 없는 조인 뷰에서 ROWID를 선택할 수 없음
SQL> ALTER TABLE DEPT ADD CONSTRAINT DEPT_PK PRIMARY KEY(DEPTNO);
테이블이 변경되었습니다.
SQL> INSERT INTO EMP_DEPT_VIEW
2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO, LOC)
3 SELECT EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO, LOC
4 FROM EMP_DEPT_VIEW;
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO, LOC)
*
2행에 오류:
ORA-01776: 조인 뷰에 의하여 하나 이상의 기본 테이블을 수정할 수 없습니다.
SQL> SELECT COLUMN_NAME, INSERTABLE, UPDATABLE, DELETABLE
2 FROM USER_UPDATABLE_COLUMNS
3 WHERE TABLE_NAME = 'EMP_DEPT_VIEW';
COLUMN_NAME INS UPD DEL
------------------------------ --- --- ---
EMP_RID YES YES YES
EMPNO YES YES YES
ENAME YES YES YES
JOB YES YES YES
MGR YES YES YES
HIREDATE YES YES YES
SAL YES YES YES
COMM YES YES YES
DEPTNO YES YES YES
DEPT_RID NO NO NO
DNAME NO NO NO
COLUMN_NAME INS UPD DEL
------------------------------ --- --- ---
LOC NO NO NO
12 개의 행이 선택되었습니다.
SQL> INSERT INTO EMP_DEPT_VIEW
2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
3 SELECT EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO
4 FROM EMP_DEPT_VIEW;
14 개의 행이 만들어졌습니다.
SQL> COMMIT;
커밋이 완료되었습니다.
|
SQL> ALTER TABLE DEPT ADD AVG_SAL NUMBER(7,2);
테이블이 변경되었습니다.
SQL> UPDATE
2 (SELECT D.DEPTNO, D.AVG_SAL D_AVG_SAL, E.AVG_SAL E_AVG_SAL
3 FROM (SELECT DEPTNO, ROUND(AVG(SAL), 2) AVG_SAL FROM EMP GROUP BY DEPTNO) E
4 , DEPT D
5 WHERE D.DEPTNO = E.DEPTNO)
6 SET D_AVG_SAL = E_AVG_SAL;
SET D_AVG_SAL = E_AVG_SAL
*
6행에 오류:
ORA-01779: 키-보존된것이 아닌 테이블로 대응한 열을 수정할 수 없습니다
SQL> UPDATE /*+ BYPASS_UJVC */
2 (SELECT D.DEPTNO, D.AVG_SAL D_AVG_SAL, E.AVG_SAL E_AVG_SAL
3 FROM (SELECT DEPTNO, ROUND(AVG(SAL), 2) AVG_SAL FROM EMP GROUP BY DEPTNO) E
4 , DEPT D
5 WHERE D.DEPTNO = E.DEPTNO)
6 SET D_AVG_SAL = E_AVG_SAL;
3 행이 갱신되었습니다.
SQL> SELECT * FROM DEPT;
DEPTNO DNAME LOC AVG_SAL
---------- -------------- ------------- ----------
10 ACCOUNTING NEW YORK 2916.67
20 RESEARCH DALLAS 2175
30 SALES CHICAGO 1566.67
40 OPERATIONS BOSTON
※ SQL TRACE
********************************************************************************
SQL> EXPLAIN PLAN FOR
2 UPDATE /*+ BYPASS_UJVC */
3 (SELECT D.DEPTNO, D.AVG_SAL D_AVG_SAL, E.AVG_SAL E_AVG_SAL
4 FROM (SELECT DEPTNO, ROUND(AVG(SAL), 2) AVG_SAL FROM EMP GROUP BY DEPTNO) E
5 , DEPT D
6 WHERE D.DEPTNO = E.DEPTNO)
7 SET D_AVG_SAL = E_AVG_SAL;
해석되었습니다.
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------
Plan hash value: 1287183684
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 28 | 1456 | 4 (25)| 00:00:01 |
| 1 | UPDATE | DEPT | | | | |
| 2 | NESTED LOOPS | | 28 | 1456 | 4 (25)| 00:00:01 |
| 3 | VIEW | | 28 | 728 | 3 (34)| 00:00:01 |
| 4 | SORT GROUP BY | | 28 | 728 | 3 (34)| 00:00:01 |
| 5 | TABLE ACCESS FULL | EMP | 28 | 728 | 2 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 26 | 1 (0)| 00:00:01 |
|* 7 | INDEX UNIQUE SCAN | DEPT_PK | 1 | | 0 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
7 - access("D"."DEPTNO"="E"."DEPTNO")
********************************************************************************
※ TKPROF LOG
********************************************************************************
UPDATE /*+ BYPASS_UJVC */
(SELECT D.DEPTNO, D.AVG_SAL D_AVG_SAL, E.AVG_SAL E_AVG_SAL
FROM (SELECT DEPTNO, ROUND(AVG(SAL), 2) AVG_SAL FROM EMP GROUP BY DEPTNO) E
, DEPT D
WHERE D.DEPTNO = E.DEPTNO)
SET D_AVG_SAL = E_AVG_SAL
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.03 0 3 0 0
Execute 1 0.00 0.00 0 8 1 3
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 0.00 0.03 0 11 1 3
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: SYS
Rows Row Source Operation
------- ---------------------------------------------------
0 UPDATE DEPT (cr=8 pr=0 pw=0 time=476 us)
3 NESTED LOOPS (cr=8 pr=0 pw=0 time=382 us)
3 VIEW (cr=3 pr=0 pw=0 time=199 us)
3 SORT GROUP BY (cr=3 pr=0 pw=0 time=172 us)
28 TABLE ACCESS FULL EMP (cr=3 pr=0 pw=0 time=137 us)
3 TABLE ACCESS BY INDEX ROWID DEPT (cr=5 pr=0 pw=0 time=134 us)
3 INDEX UNIQUE SCAN DEPT_PK (cr=2 pr=0 pw=0 time=49 us)(object id 54078)
********************************************************************************
MERGE INTO customer t USING customer_delta s ON (t.cust_id = s.cust_id)
WHEN MATCHED THEN UPDATE
SET t.cust_id = s.cust_id, t.cust_nm = s.cust_nm, t.email = s.email, ...
WHEN NOT MATCHED THEN INSERT
(cust_id, cust_nm, email, tel_no, region, addr, reg_dt) VALUES
(s.cust_id, s.cust_nm, s.email, s.tel_no, s.region, s.addr, s.reg_dt);
MERGE INTO customer t USING customer_delta s ON (t.cust_id = s.cust_id)
WHEN MATCHED THEN UPDATE
SET t.cust_id = s.cust_id, t.cust_nm = s.cust_nm, t.email = s.email, ...;
MERGE INTO customer t USING customer_delta s ON (t.cust_id = s.cust_id)
WHEN NOT MATCHED THEN INSERT
(cust_id, cust_nm, email, tel_no, region, addr, reg_dt) VALUES
(s.cust_id, s.cust_nm, s.email, s.tel_no, s.region, s.addr, s.reg_dt);
MERGE INTO dept d
USING (select deptno, round(avg(sal), 2) avg_sal from emp group by deptno) e
ON (d.deptno = e.deptno)
WHEN MATCHED THEN UPDATE set d.avg_sal = e.avg_sal;
MERGE INTO customer t USING customer_delta s ON (t.cust_id = s.cust_id)
WHEN MATCHED THEN UPDATE
SET t.cust_id = s.cust_id, t.cust_nm = s.cust_nm, t.email = s.email, ...
WHERE reg_dt >= to_char('20000101','yyyymmdd')
WHEN NOT MATCHED THEN INSERT
(cust_id, cust_nm, email, tel_no, region, addr, reg_dt) VALUES
(s.cust_id, s.cust_nm, s.email, s.tel_no, s.region, s.addr, s.reg_dt)
WHERE reg_dt < trunc(sysdate) ;
MERGE INTO customer t USING customer_delta s ON (t.cust_id = s.cust_id)
WHEN MATCHED THEN UPDATE
SET t.cust_id = s.cust_id, t.cust_nm = s.cust_nm, t.email = s.email, ...
DELETE WHERE t.withdraw_dt is not null --탈퇴일시가 null이 아닌 레코드 삭제
WHEN NOT MATCHED THEN INSERT
(cust_id, cust_nm, email, tel_no, region, addr, reg_dt) VALUES
(s.cust_id, s.cust_nm, s.email, s.tel_no, s.region, s.addr, s.reg_dt);
INSERT INTO 청구보험당사자 ( 당사자ID, 접수일자, 접수순번, 담보구분, 청구순번, ...)
SELECT ...
FROM 청구보험당사자_임시 a, 거래당사자 b
WHERE a,당사자ID =b.당사자ID;
INSERT INTO 자동차사고접수당사자 ( 당사자ID, 접수일자, 접수순번, 담보구분, 청구순번, ...)
SELECT ...
FROM 가사고접수당사자_임시 a, 거래당사자 b
WHERE b.당사자구분 NOT IN ( '4','5','6')
AND a,당사자ID =b.당사자ID;
INSERT FIRST
WHEN 구분 = 'A' THEN
INTO 청구보험당사자 ( 당사자ID, 접수일자, 접수순번, 담보구분, 청구순번, ...)
VALUES ( 당사자ID, 접수일자, 접수순번, 담보구분, 청구순번, ...)
WHEN 구분 = 'B' THEN
INTO 자동차사고접수당사자 ( 당사자ID, 접수일자, 접수순번, 담보구분, 청구순번, ...)
VALUES ( 당사자ID, 접수일자, 접수순번, 담보구분, 청구순번, ...)
SELECT a.당사자ID, a.접수일자, a.접수순번, a.담보구분, a.청구순번, ...
FROM (
SELECT 'A' 구분
FROM 청구보험당사자_임시
UNION ALL
SELECT 'B' 구분
FROM 가사고접수당사자_임시
WHERE 당사자구분 NOT IN ( '4','5','6')
) a, 거래당사자 b
WHERE a,당사자ID =b.당사자ID;
- 강좌 URL : http://www.gurubee.net/lecture/3272
- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.