서론
Oracle에서 프로시저를 사용하다가 자꾸 예상하지 못한 부분에서 COMMIT/ROLLBACK 이 처리되었다. 확인해보니까 내부 로직에서 데이터를 적재한 후 로그 테이블에 로그를 쌓기위한 다른 프로시저를 호출하였는데, 그 프로시저에서 COMMIT/ROLLBACK을 실행하고 있었다.
이처럼 프로시저가 하나의 트랜잭션에서 여러 프로시저를 호출하여 사용하는 경우 문제가 발생할 수 있다. 따라서, 각 프로시저마다 독립적인 트랜잭션을 구성할 수 있도록 'PRAGMA AUTONOMOUS_TRANSACTION' 옵션을 적용해야한다.
간단한 예제를 통해 어떻게 적용되는지 알아보도록 하자.
1. 예제
1-1. 요약
1. 데이터를 적재하는 프로시저를 호출.
2. 프로시저가 테이블에 데이터를 INSERT하고 COMMIT/ROLLBACK을 처리하기전에 다른 프로시저를 호출.
3. 다른 프로시저에서 데이터를 적재하고 COMMIT/ROLLBACK을 처리한 후 종료.
4. 다른 프로시저가 종료된 후 COMMIT/ROLLBACK을 처리.
5. 데이터 적재 결과 확인
1-2. 테이블 생성
1
2
3
4
5
6
7
8
9
10
|
-- 테이블 생성
CREATE TABLE T_COMMIT_TEST(
NUM NUMBER,
CALLER VARCHAR2(30),
LOG VARCHAR2(200),
LOAD_DTTM VARCHAR2(30)
);
-- 테이블 삭제
-- DROP TABLE T_COMMIT_TEST PURGE;
|
cs |
1-3. 로그 프로시저 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
CREATE OR REPLACE PROCEDURE PR_INSERT_LOG(
I_CALLER IN VARCHAR2,
I_LOG IN VARCHAR2
)
IS
-- 현재 프로시저가 독립적인 트랜잭션 가지도록 설정.
PRAGMA AUTONOMOUS_TRANSACTION;
V_NUM NUMBER;
V_CALLER VARCHAR2(30);
V_LOG VARCHAR2(200);
V_LOAD_DTTM VARCHAR2(30);
BEGIN
V_NUM := -1;
V_CALLER := I_CALLER;
V_LOG := I_LOG;
BEGIN
-- 다음 순번 조회
SELECT NVL(MAX(T1.NUM), 0)+1 AS NEXT_NUM
INTO V_NUM
FROM T_COMMIT_TEST T1
;
V_LOAD_DTTM := TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS');
-- INSERT
INSERT INTO T_COMMIT_TEST (
NUM,
CALLER,
LOG,
LOAD_DTTM
) VALUES (
V_NUM,
V_CALLER,
V_LOG,
V_LOAD_DTTM
);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( SQLERRM );
ROLLBACK;
V_LOG := SQLERRM;
V_LOAD_DTTM := TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS');
-- INSERT
INSERT INTO T_COMMIT_TEST (
NUM,
CALLER,
LOG,
LOAD_DTTM
) VALUES (
V_NUM,
V_CALLER,
V_LOG,
V_LOAD_DTTM
);
COMMIT;
END;
END
;
|
cs |
1-4. 데이터 적재 프로시저 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
CREATE OR REPLACE PROCEDURE PR_COMMIT_TESTER(
I_CALLER IN VARCHAR2
)
IS
V_NUM NUMBER;
V_CALLER VARCHAR2(10);
V_DATA VARCHAR2(200);
BEGIN
V_NUM := -1;
V_CALLER := I_CALLER;
-- BLOCK 1
BEGIN
FOR V_IDX IN 1..3
LOOP
-- 데이터 적재 시작 로그
PR_INSERT_LOG('PROCEDURE', '[BLOCK-'||V_IDX||'][START]' );
-- 다음 순번 조회
SELECT NVL(MAX(T1.NUM), 0)+1 AS NEXT_NUM
INTO V_NUM
FROM T_COMMIT_TEST T1
;
-- 테스트 데이터
V_DATA := 'TEST DATA1';
V_LOAD_DTTM := TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS');
-- INSERT
INSERT INTO T_COMMIT_TEST (
NUM,
CALLER,
LOG,
LOAD_DTTM
) VALUES (
V_NUM,
V_CALLER,
V_DATA,
TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS')
);
-- TOGGLE COMMIT AND ROLLBACK
IF MOD(V_IDX, 3) != 1 THEN
-- 데이터 적재 완료 로그
PR_INSERT_LOG('PROCEDURE', '[BLOCK-'||V_IDX||'][COMMIT]' );
COMMIT;
ELSE
PR_INSERT_LOG('PROCEDURE', '[BLOCK-'||V_IDX||'][ROLLBACK]' );
ROLLBACK;
END IF
;
-- 데이터 적재 완료 로그
PR_INSERT_LOG('PROCEDURE', '[BLOCK-'||V_IDX||'][END]' );
END LOOP
;
EXCEPTION
WHEN OTHERS THEN
V_LOAD_DTTM := TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS');
DBMS_OUTPUT.PUT_LINE( SQLERRM );
ROLLBACK;
END
;
END
;
|
cs |
1-5. 프로시저 실행
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
-- OUTPUT 설정 ON
SET SERVEROUTPUT ON
-- PROCEDURE 실행
BEGIN
-- 기존 데이터 삭제
EXECUTE IMMEDIATE 'TRUNCATE TABLE T_COMMIT_TEST';
-- 시작 로그
PR_INSERT_LOG('DOCHI', 'START MAIN');
-- 데이터 적재
PR_COMMIT_TESTER('DOCHI');
-- 완료 로그
PR_INSERT_LOG('DOCHI', 'END MAIN');
END
;
|
cs |
1-6. 결과 확인
1
2
3
4
5
6
7
8
|
-- 적재 결과 조회
SELECT T1.NUM
, T1.CALLER
, T1.LOG
, T1.LOAD_DTTM
FROM T_COMMIT_TEST T1
WHERE 1=1
ORDER BY T1.NUM ASC
|
cs |
마치며
'PRAGMA AUTONOMOUS_TRANSACTION' 옵션을 적용하여 간단하게 독립적인 트랜잭션 환경을 만들어 보았다. 프로시저 내부에서 다른 프로시저를 호출할 때, 그 내부에서 COMMIT/ROLLBACK 처리된다면 이 옵션을 적용해주도록 하자.
'DATABASE > ORACLE' 카테고리의 다른 글
[SPOOL] SQL 스크립트를 출력하는 스풀링 (0) | 2020.04.24 |
---|---|
[SQL] WITH문을 이용한 계층형쿼리 ( With. CONNECT BY절) (2) | 2020.01.23 |
[SQL] WITH문 (2) | 2020.01.22 |
[PL/SQL] 예제 (1) - 1~100의 수를 더하는 과정을 출력 (0) | 2020.01.22 |
[PL/SQL] 무작정 시작하기 (5) - EXCEPTION 예외 (0) | 2020.01.16 |
댓글