2017-05-12 2 views
0

대량 수집 및 forall 문을 사용하려면이 쿼리를 최적화하는 데 도움이 필요합니다. 원본 테이블 (ORIG_xxx)의 모든 데이터를 복사 할 수있는 백업 테이블 (BCK_xxxx)을 만들었지 만 대량으로 변환하는 데 문제가 있습니다. BULK에서 보았던 대부분의 예제에는 % rowtype을 사용하여 테이블 이름과 구조가 이미 정의되어 있습니다. 그러나 필자는 백업 할 테이블이 수백 개이므로 동적으로 테이블 이름을 쿼리해야합니다. 대량없이 하나/삭제 된 데이터의 하나가 수집 삽입하고 시간이 많이 소요이 내 원래의 질의 : 내가 코드를 포함하여 생각하고동적 쿼리 및 테이블 이름이있는 BULK COLLECT/FORALL 문 - Oracle PL/SQL

DECLARE 
--select all table names from backup tables (ex: BCK_tablename) 
CURSOR cur_temp_tbl IS 
    SELECT table_name 
    FROM all_tables 
    WHERE OWNER = 'BCKUP' 
    ORDER BY 1; 

--select all table names from original tables (ex: ORIG_tablename) 
CURSOR cur_original_tbl IS 
    SELECT table_name 
    FROM all_tables 
    WHERE OWNER = 'ORIG' 
    ORDER BY 1;  
    l_tbl_nm VARCHAR2(30 CHAR); 

BEGIN 
    --first loop to delete all tables from backup 
    FOR a IN cur_temp_tbl LOOP 
     l_tbl_nm := a.table_name;          
     EXECUTE IMMEDIATE 'DELETE FROM '|| l_tbl_nm; 
     l_deleted_cnt := l_deleted_cnt +1;    
    END LOOP; 

    --second loop to insert data from original to backup   
    FOR b IN cur_original_tbl LOOP    
     l_tbl_nm := b.table_name; 
     CASE 
      WHEN INSTR(l_tbl_nm,'ORIG_') > 0 THEN 
      l_tbl_nm := REPLACE(l_tbl_nm,'ORIG_','BCK_'); 
      ELSE 
      l_tbl_nm := 'BCK_' || l_tbl_nm; 
     END CASE; 

     EXECUTE IMMEDIATE 'INSERT INTO ' || l_tbl_nm || ' SELECT * FROM ' || b.table_name; 
     l_inserted_cnt := l_inserted_cnt +1; 
    END LOOP; 

    dbms_output.put_line('Deleted/truncated tables from backup :' ||l_deleted_cnt); 
    dbms_output.put_line('No of tables inserted with data from original to backup :' ||l_inserted_cnt); 
EXCEPTION 
WHEN OTHERS THEN 
dbms_output.put_line(SQLERRM); 
dbms_output.put_line(l_tbl_nm); 
END; 

아래 내 두 번째 루프 후 추가하지만 난 어떻게하는 데 문제가 'cur_tbl'커서 및 'l_tbl_data'TABLE 데이터 형식을 선언하십시오. 나는 동적이어야 TABLENAME 이후 ROWTYPE을 사용할 수 없습니다이고 원래 테이블에서 모든 테이블 이름을 나열합니다 내 두 번째 루프의 각 반복에서 변경됩니다 :

TYPE CurTblTyp IS REF CURSOR; 
cur_tbl CurTblTyp; 
TYPE l_tbl_t IS TABLE OF tablename.%ROWTYPE; 
l_tbl_data l_tbl_t ; 

OPEN cur_tbl FOR 'SELECT * FROM :s ' USING b.table_name; 
FETCH cur_tbl BULK COLLECT INTO l_tbl_data LIMIT 5000; 
EXIT WHEN cur_tbl%NOTFOUND;  
CLOSE cur_tbl;   

FORALL i IN 1 .. l_tbl_data .count 
EXECUTE IMMEDIATE 'insert into '||l_tbl_nm||' values (:1)' USING 
l_tbl_data(i); 

당신이 날 도와 내가 할 수있는 방법을 제안 할 수 있습니다 희망 이 코드는 훨씬 간단합니다. 고마워.

답변

3

기존 백업 테이블에서 모든 행을 제거한 다음 원본 테이블의 전체 내용을 백업 테이블로 다시 복사하려는 것으로 보입니다. 이것이 맞다면, 삭제를 위해 DELETE을 사용하고 삽입을위한 루프 연산은 느려질 것입니다.

먼저 데이터를 제거하려면 TRUNCATE을 사용하십시오. 다시 채울 예정이므로 REUSE STORAGE 옵션을 사용하십시오. 이것은 테이블에서 모든 행을 삭제하는 가장 효율적인 방법입니다.

TRUNCATE TABLE <backup table> REUSE STORAGE; 

둘째, 그냥 INSERTSELECT로, 다시 채워야합니다.

INSERT INTO <backup table> SELECT * FROM <orig table>; 

테이블로 반복 할 때 루프에서 사용할 수 있습니다. 테이블 행을 통해 커서를 이동할 필요가 없기 때문에 더 빠릅니다. 새 테이블이있는 경우, 당신은 CTAS와 비슷한 뭔가를 할 수

...

CREATE TABLE <backup table> AS SELECT * FROM <orig_table>; 
+0

감사합니다. 이미 백업 테이블을 만들 때 ctas를 사용했습니다. 나는 truncate를 사용하는 것에 익숙하지만 ddl 문 (자동 커밋)이기 때문에 여기서 사용하는 것을 주저한다. 또한이 것과 비슷한 프로 시저를 만들지 만 원래 테이블의 데이터를 삭제하고 백업 테이블의 레코드를 삽입합니다 .. 잘라 내기를 사용하면 모든 데이터가 백업 테이블. 삭제를 사용하는 다른 방법이 있습니까?또는 커밋 같은 자동 커밋없이 일괄 삭제 같은 것이 있습니까? – mariozelda

+0

truncate를 사용하려는 이유는 모든 삭제 사항으로 리두 로그를 플러드하지 않기 때문입니다. 예, 잘라 내기에서 롤백이 없으므로주의해야합니다. 그러나 그것들은 본질적으로 선택 사항이며 삭제하고 자릅니다. – unleashed

0

이 삭제 이외에 제 3의 옵션과 옵션을 절단 : 이제/드롭 이름을 바꾸는 것이. 이전 백업 테이블의 이름을 바꾸고 새 백업 (CTAS)을 다시 만듭니다. create-insert가 성공하면 이름이 바뀐 테이블을 삭제합니다. 새 백업이 실패하면 이전 백업의 이름을 초기 백업 이름으로 다시 변경합니다. 기본적으로 재실행 로그를 위해 임시로 디스크 공간을 사용합니다.

대량 처리가 필요하지 않지만 CTAS는 대량 처리보다 빠릅니다.

-1

FORCE DELETE를 사용 했습니까? Oracle Master J.B.E 에 의해 처음 도입되었으며 데이터 삭제에 사용되며 테이블에있을 수있는 제약 조건을 무시하며 다른 삭제 명령문보다 훨씬 빠릅니다.

FORCE DELETE FROM <table_name>; 
+0

이것은 유효한 구문처럼 보이지 않습니다. 이에 관한 문서를 제공 할 수 있습니까? –