2012-03-08 3 views
0

PL/SQL에서 대량 바인드를 사용하는 데 문제가 있습니다. 기본적으로 내가 원하는 것은 Component_id와 fieldname에 따라 필드 값을 업데이트하는 테이블 (Component)입니다. 이들 모두는 매개 변수로 전달됩니다 (유형 varchar2_nested_table은 효율적이고 문자열 배열입니다. 발생해야하는 각 업데이트 문에 대해 하나의 요소). 예를 들어 Component_id = 'Compid1'및 fieldname = 'name'인 경우 fieldvalue를 'new component name'으로 업데이트해야합니다.PL/SQL 대량 바인드/빠른 업데이트 문

이 코드는 http://www.oracle.com/technetwork/issue-archive/o14tech-plsql-l2-091157.html과 관련하여 아래에 입력했습니다. 코드는 작동하지만 IN 매개 변수의 모든 요소에 대해 업데이트를 수행하는 간단한 루프보다 빠릅니다. 따라서 매개 변수에 1000 개의 요소가 있으면 1000 개의 업데이트 문이 실행됩니다. 나는 또한 BULK COLLECT INTO를 사용하고 있지 않다는 것을 알았지 만 데이터베이스에서 아무것도 선택하지 않아도되므로 업데이트 할 필요가 없다고 생각했습니다.

현재 모두 업데이트를 위해 4-5 초가 걸립니다. 내가 대량 일괄 바인딩을 잘못 사용하고 있거나 사람들이 2 초 등 50,000 행을 실행하고 찾을 수있는 예제에서 오해가 있다고 가정합니다. 나는 FORALL 이해 컨텍스트 스위치 수를 줄임으로써 성능을 향상시켜야합니다. 필자는 커서와 대량 바인드를 사용하여 온라인에서 찾은 다른 방법을 시도했지만 동일한 결과를 보았습니다. 아마도 내 공연 기대치가 너무 많습니까? 나는 다른 사람들의 결과를 보면서 그렇게 생각하지 않습니다. 어떤 도움이라도 대단히 감사하겠습니다.

create or replace procedure BulkUpdate(sendSubject_in IN varchar2_nested_table_type, 
fieldname_in IN varchar2_nested_table_type,fieldvalue_in IN varchar2_nested_table_type) is 


TYPE component_aat IS TABLE OF component.component_id%TYPE 
    INDEX BY PLS_INTEGER; 
TYPE fieldname_aat IS TABLE OF component.fieldname%TYPE 
    INDEX BY PLS_INTEGER; 
TYPE fieldvalue_aat IS TABLE OF component.fieldvalue%TYPE 
    INDEX BY PLS_INTEGER; 

fieldnames fieldname_aat; 
fieldvalues fieldvalue_aat; 
approved_components component_aat; 


PROCEDURE partition_eligibility 
IS 
BEGIN 
    FOR indx IN sendSubject_in.FIRST .. sendSubject_in.LAST 
    LOOP 
    approved_components(indx) := sendSubject_in(indx); 
    fieldnames(indx):= fieldname_in(indx); 
    fieldvalues(indx) := fieldvalue_in(indx); 
    END LOOP; 
END; 


PROCEDURE update_components 
IS 
BEGIN 
    FORALL indx IN approved_components.FIRST .. approved_components.LAST 
    UPDATE Component 
     SET Fieldvalue = fieldvalues(indx) 
     WHERE Component_id = approved_components(indx) 
     AND Fieldname = fieldnames(indx); 
END; 

BEGIN 
    partition_eligibility; 
    update_components; 
END BulkUpdate; 
+0

를 참조 선택 ...에 ... 절

  • 커서 문을
  • DML 반환 조항
  • 를 가져

    1. 에 사용 partition_eligibility 전의 시간, 이후의 시간 및 update_components 후에 시간이 소비 된 곳을 확인합니다. – Eggi

    +0

    이미 측정했습니다. update_components를 실행하지 않으면 25 밀리 초가 소요됩니다. update_components는 4500 밀리 초가 걸립니다. – user1255191

    답변

    0

    계속 뭔가가있다, 나는 개별 업데이트가 각 트리거 또는 비효율적 인 인덱스가 아마 때문에, 많은 시간을 복용 생각한다. (각 진술이 개별적으로 비싸다면 대량 업데이트를 사용한다고해서 컨텍스트 스위치가 실제 작업에 비해 무시할 수 있기 때문에 많은 시간을 절약 할 수는 없습니다). 하찮은 테스트 시스템에서 약 1.5 초에 업데이트

    CREATE TABLE Component (
        Component_id NUMBER, 
        fieldname VARCHAR2(100), 
        Fieldvalue VARCHAR2(100), 
        CONSTRAINT component_pk PRIMARY KEY (component_id, fieldname) 
    ); 
    
    -- insert 1 million rows 
    INSERT INTO component 
        (SELECT ROWNUM, to_char(MOD(ROWNUM, 100)), dbms_random.string('p', 10) 
        FROM dual 
        CONNECT BY LEVEL <= 1e6); 
    
    CREATE OR REPLACE TYPE varchar2_nested_table_type AS TABLE OF VARCHAR2(100); 
    /
    
    SET SERVEROUTPUT ON SIZE UNLIMITED FORMAT WRAPPED 
    DECLARE 
        l_id varchar2_nested_table_type; 
        l_names varchar2_nested_table_type; 
        l_value varchar2_nested_table_type; 
        l_time NUMBER; 
    BEGIN 
        SELECT rownum, to_char(MOD(rownum, 100)), dbms_random.STRING('p', 10) 
        BULK COLLECT INTO l_id, l_names, l_value 
        FROM dual 
        CONNECT BY LEVEL <= 100000; 
        l_time := dbms_utility.get_time; 
        BulkUpdate(l_id, l_names, l_value); 
        dbms_output.put_line((dbms_utility.get_time - l_time) || ' cs elapsed.'); 
    END; 
    /
    

    100000 행 :

    여기 내 테스트 설정입니다. 행별로 동일한 데이터 세트를 업데이트하는 데는 약 4 초가 걸립니다.

    새로 만든 테이블을 사용하여 유사한 스크립트를 실행할 수 있습니까?

    +0

    오, 정말? 나는 비슷한 테스트를 수행했고, 결과는 1,000 행에 대해 4.3 초였습니다. 이것은 다소 좋은 사양 기계에도 적용됩니다. BulkUpdate에서 아무 것도 변경하지 않았다고 가정하면 내 문제는 다른 곳에있는 것으로 보입니다. 내 데이터베이스는 다른 어떤 일도하지 않습니다. 트리거 나 인덱스 등이 없습니다. 모든 작업이 삽입됩니다 (1000 행의 경우 .25 초 소요). 어쨌든 시간을내어 테스트 해 주셔서 감사합니다. – user1255191

    +0

    새로 만든 테이블을 사용하여 테스트를 실행할 수 있습니까? –

    +0

    대단히 감사합니다. 테스트를 보면서 (id, fieldname)에 기본 키를 설정했음을 알았습니다. 나는 어떤 분야도 독특하지 않았고, 내가 필요한 분야가 없다고 생각하지 않았기 때문에 나는 하나도 세우지 않았다. 현재는 훨씬 빨라졌습니다 (1,000 초당 0.049 초). 답장을 보내 주셔서 대단히 감사합니다. 테스트를 보지 못했다고 생각조차하지 못했을 것입니다. 감사. – user1255191

    0

    언제든지 SQL 문을 실행하여 Oracle 서버에 PL/SQL 블록을 제출합니다. SQL 엔진을 통해 절차 문이 실행됩니다. 절차 진술 집행자를 통해. 이 절차 적 명령 실행자는 PL/SQL 엔진에서 사용할 수 있습니다. SQL, PL/SQL 문을 통해 대량의로드를 사용할 때마다 항상 oracle 서버가 이러한 엔진을 통해 이러한 명령문을 개별적으로 실행합니다.

    이 유형의 실행 방법은 항상 콘텐츠 전환 실행 방법으로 응용 프로그램의 성능을 저하시킵니다. 이 문제를 극복하기 위해 oracle은 콜렉션을 사용하여 "대량 바인드"프로세스를 도입했습니다. 즉,이 방법에서는 오라클 서버가 한 번에 모든 SQL 문을 실행합니다. 대량 수집 :

    이 절을 자동으로 사용할 때마다. 오라클 서버는 날짜를 현명하게 선택하고 그것을 콜렉션에 저장합니다.대량 수집 절은 당신이 측정하는`dbms_utility.get_time를`추가 할 수 있습니다 또한 PL/SQL Bulk Collect And Bulk bind