안녕하세요
혼자서 이것저것 찾아보다가 진전이 없어 질문드립니다...
운영 데이터와 로컬 데이터를 동기화 하기위한 프로시저를 짜서 실행을 보았었는데 한시간반이나 돌아가다가 너무 오래걸리는것 같아서 중단 시킨경우가 있엇습니다...
그 때 롤백이 되면서도 1시간반이라는 시간이 걸렸었습니다.
테이블도 30개인데 대부분 그렇게 크기가 크지 않은 테이블이 대다수입니다.
보통 쿼리로 직접 하나하나 해보면 한시간까지도 안걸리고 전체 다 하는데 20분정도 걸렸었습니다...
혼자서 해결해보려고 인터넷도 찾아봤는데도 도저히 어느부분이 잘못된건지 모르겠어서 질문드립니다...
앞서 설명을 드리자면 30개의 운영테이블의 데이터를 백업하고 백업한 테이블의 데이터를 가지고 로컬테이블에 넣어서 데이터 동기화를 시키려고 합니다.
순서는 이러합니다.
1. 30개의 운영테이블의 데이터를 DB링크와 LOOP문을 사용하여 CREATE TABLE 구문으로 로컬에 백업테이블을 생성(데이터 전체를 백업)
2. 동기화할 로컬의 테이블을 DELETE를 통해 데이터를 지운 후 INSERT로 백업테이블 데이터를 삽입
1번과 2번의 프로시저를 나눠서 만들었고 두개의 프로시저가 완성되면 후엔 추가적으로 조건문을 달아 필요한 월별 데이터만 가져오는 프로시저를 만들예정입니다...
30개의 테이블명이 각각 다르기 때문에 FOR LOOP문을 돌릴 때 TEMP_SYN_COPY테이블의 TABLE_NM컬럼에 각각의 테이블명을 저장해두었습니다.
또한 각각의 테이블명을 가져오기 위한부분에서 WHERE GROUP_NM = 'LTV' 부분은 필요한 테이블만 걸러내기 위해 사용되었습니다.
테이블명의 길이제한이 30인데 백업테이블을 생성시 테이블 명 뒤에 테이블명_221024 같이 붙게 되는데 본래 테이블의 길이가 길면 테이블명_2210 이런식으로
만들기 위해 IF문으로 분기를 태웠습니다.
아래에는 프로시저 부분입니다.
1번 CREATE 부분입니다.
CREATE OR REPLACE PROCEDURE MVP_MNG.PROC_SYN_COPY_ALL_1( P_DATE IN VARCHAR2 ) IS V_SQL VARCHAR(500); V_CNT NUMBER(1); V_TABLE_NM VARCHAR(30); V_TABLE_SUB_NM VARCHAR(7); V_TABLE_GROUP_NM VARCHAR(30); USER_DEFINE_EXCEPTION EXCEPTION; BEGIN DBMS_OUTPUT.PUT_LINE('운영 테이블 DB 전체 복사'); --백업테이블 중복명칭 존재하는지 체크 FOR TABLE_LIST IN ( SELECT TABLE_NM FROM TEMP_SYN_COPY WHERE TABLE_GROUP ='LTV' ) LOOP -- 테이블 명칭을 내부변수에 넣어서 사용 V_TABLE_NM := TABLE_LIST.TABLE_NM; -- 테이블명 길이제한에 의한 IF문 IF LENGTH(V_TABLE_NM) > 23 THEN V_TABLE_SUB_NM := '_' || TO_CHAR(SYSDATE,'YYMM'); ELSE V_TABLE_SUB_NM := '_' || P_DATE; END IF; SELECT COUNT(1) INTO V_CNT FROM ALL_TABLES WHERE TABLE_NAME = V_TABLE_NM || V_TABLE_SUB_NM; --백업테이블 중복명칭이 존재했을 시 IF V_CNT > 0 THEN RAISE USER_DEFINE_EXCEPTION; END IF; END LOOP; -- CREATE TABLE LOOP FOR TABLE_LIST IN ( SELECT TABLE_NM , GROUP_NM FROM TEMP_SYN_COPY WHERE TABLE_GROUP ='LTV' ) LOOP -- 테이블 명칭, 그룹명을 내부변수에 넣어서 사용 V_TABLE_NM := TABLE_LIST.TABLE_NM; V_TABLE_GROUP_NM := TABLE_LIST.GROUP_NM; -- 테이블명 길이제한에 의한 IF문 IF LENGTH(V_TABLE_NM) > 23 THEN V_TABLE_SUB_NM := '_' || TO_CHAR(SYSDATE,'YYMM'); ELSE V_TABLE_SUB_NM := '_' || P_DATE; END IF; --CREATE 구문 V_SQL := 'CREATE TABLE '|| V_TABLE_NM || V_TABLE_SUB_NM || ' AS SELECT * FROM MVP_MNG.'|| V_TABLE_NM ||'@DL_MVP_LIVE'; EXECUTE IMMEDIATE V_SQL; DBMS_OUTPUT.PUT_LINE(V_TABLE_GROUP_NM || ' ' || V_TABLE_NM || V_TABLE_SUB_NM || ' 복사 완료'); END LOOP; DBMS_OUTPUT.PUT_LINE('테이블 생성 완료'); COMMIT; EXCEPTION WHEN USER_DEFINE_EXCEPTION THEN DBMS_OUTPUT.PUT_LINE(V_TABLE_NM || '의 백업테이블이 존재합니다.'); WHEN OTHERS THEN ROLLBACK; DBMS_OUTPUT.PUT_LINE(V_TABLE_NM || ' 테이블 복사중 실패하였습니다.'); END PROC_SYN_COPY_ALL_1;
2번의 DELETE&INSERT 부분입니다.
CREATE OR REPLACE PROCEDURE MVP_MNG.PROC_SYN_MIG_ALL_2( P_DATE IN VARCHAR2 ) IS V_SQL VARCHAR(500); V_TABLE_NM VARCHAR(30); V_TABLE_SUB_NM VARCHAR(7); V_TABLE_GROUP_NM VARCHAR(30); BEGIN DBMS_OUTPUT.PUT_LINE('운영 백업 테이블 => 개발기 테이블 DB 전체 이전'); FOR TABLE_LIST IN ( SELECT TABLE_NM , GROUP_NM FROM TEMP_SYN_COPY WHERE TABLE_GROUP = 'LTV' ) LOOP -- 테이블 명칭, 그룹명을 내부변수에 넣어서 사용 V_TABLE_NM := TABLE_LIST.TABLE_NM; V_TABLE_GROUP_NM := TABLE_LIST.GROUP_NM; --개발기 테이블 DELETE V_SQL := 'DELETE FROM MVP_MNG.'|| V_TABLE_NM || ' WHERE 1=1'; EXECUTE IMMEDIATE V_SQL; -- 테이블명 길이제한에 의한 IF문 IF LENGTH(V_TABLE_NM) > 23 THEN V_TABLE_SUB_NM := '_' || TO_CHAR(SYSDATE,'YYMM'); ELSE V_TABLE_SUB_NM := '_' || P_DATE; END IF; --운영백업 테이블 => 개발기 테이블 INSERT V_SQL := 'INSERT INTO MVP_MNG.' || V_TABLE_NM || ' SELECT * FROM ' || V_TABLE_NM || V_TABLE_SUB_NM; EXECUTE IMMEDIATE V_SQL; DBMS_OUTPUT.PUT_LINE(V_TABLE_GROUP_NM || ' ' || V_TABLE_NM || V_TABLE_SUB_NM || ' INSERT 완료'); END LOOP; COMMIT; DBMS_OUTPUT.PUT_LINE('운영 백업 테이블 => 개발기 테이블 DB 전체 이전 완료'); EXCEPTION WHEN OTHERS THEN ROLLBACK; DBMS_OUTPUT.PUT_LINE(V_TABLE_NM || V_TABLE_SUB_NM || '데이터 이전중 오류가 발생하였습니다.'); END PROC_SYN_MIG_ALL_2;
이상입니다.
제가 짠 프로시저 부분에서 잘못된 부분이 있는데 어디인지 잘모르겠어서 질문드려요...
잘부탁드리겠습니다ㅜㅜ
우리집아찌님 답변 정말 감사합니다!! 제가 partitoning table에 대해서 처음 듣기도 하고 추후에 월별관리할 때 사용해보려고 찾아봤습니다만 제가 루프문으로 돌린 30개의 테이블중에는 월별기준이 되는 컬럼이 있는 테이블도 있고 없는 테이블도 있기도 하고 또한 각각의 테이블 데이터의 양이 그리 크지도 않아서 사용하기 애매한것같습니다... 후에 사용할만한 일이 있을 때 우리집아찌님이 알려주신 partitioning table을 꼭 기억해서 사용하겠습니다~~다시 한번 감사드립니당 혹시 그 부분말고는 우리집아찌님이 보셨을 때 무언가 이상한 부분이 없을까요?
-- 아마도 마농 님 말씀처럼, Delete 일량을 줄이는 것이 최우선이 되어야 할 것 같습니다. -- 아찌님 이야기 하신 partition 처리가 가능할 것 같네요 -- 프로시저 수행 Trace 를 분석해, 개선방향을 도출해 볼 수 있습니다. -- 아래 참조 하세요 -- 1. 아래는 커서를 오픈하여 데이터를 건바이건 으로 INSERT한 후 바로 커밋을 수행하는 프로그램 예시입니다. < 프로젝트에서 강한 오라클 PL/SQL 프로그래밍, 장정수 저, 비팬북스, 에서 인용 > DECLARE l_time1 NUMBER; l_time2 NUMBER; l_trace_file_name VARCHAR2(200); -- 커서 정의 CURSOR C1 IS (SELECT * FROM SALES WHERE ROWNUM <= 100000 ); BEGIN DBMS_OUTPUT.ENABLE (buffer_size => NULL); SELECT test_pkg.trace_file_name_func INTO l_trace_file_name FROM DUAL; l_time1 := DBMS_UTILITY.GET_TIME; test_pkg.trace_on_proc(10046); -- 건건이 읽어 INSERT 후 COMMIT 수행 FOR REC IN C1 LOOP INSERT INTO SALES_TEMP VALUES REC; COMMIT; END LOOP; l_time2 := DBMS_UTILITY.GET_TIME; DBMS_OUTPUT.PUT_LINE(‘TRACE FILE : ’ || l_trace_file_name); DBMS_OUTPUT.PUT_LINE(‘EXECUTION TIME : ’ || TO_CHAR(l_time2 - l_time1)/100); END ; / TRACE FILE : c:\oracle\diag\rdbms\test\test\trace/test_ora_7564.trc <-- Trace 파일명 EXECUTION TIME : 89.06 <-- 실행시간 -- 2. 위 trace 파일을 분석해서,, 실행과정과 일량, 소요시간 등을 확인할 수 있습니다. tkprof sort=fchela, exeela sys=no aggregate=yes trace={경로}/test_ora_7564.trc output= test_ora_7564.txt ~~> tkprof 로 trace 분석 하는 방법은, DB구르비에 잘 정리되어 있습니다. -- --