2012-03-08 14 views
0

PL/SQLでバルク・バインドを使用する際に問題があります。基本的には、テーブル(Component)がComponent_idとfieldnameに依存してフィールド値を更新する必要があります。これらはすべてパラメタとして渡されます(varchar2_nested_tableの型は効果的であり、発生する必要のある各更新ステートメントの要素の1つです)。たとえば、Component_id = 'Compid1'、fieldname = 'name'の場合、fieldvalueは '新しいコンポーネント名'に更新する必要があります。PL/SQL一括バインド/一括更新文

このhttp://www.oracle.com/technetwork/issue-archive/o14tech-plsql-l2-091157.htmlと関連して以下のコードを入力しました。コードは機能しますが、INパラメータのすべての要素の更新を実行する単純なループより高速ではありません。したがって、パラメータに1000の要素がある場合、1000の更新文が実行されます。私はBULK COLLECT INTOを使用していないことも認識していますが、私はデータベースから何も選択する必要はないので、私は必要と思っていませんでした。

現時点では、両方とも1000回の更新で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

はあなたが測定するために、 'dbms_utility.get_timeを'追加することができます参照しますpartition_eligibilityの前、afterの後、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行で0.25秒かかります)。とにかくテストにタイムアウトしていただきありがとうございます。 – user1255191

+0

私が行ったように(新しく作成したテーブルで)テストを実行できますか? –

+0

ありがとうございます。テストを見て、(id、fieldname)にプライマリキーを設定しているのを見た。私はフィールドが一意ではなかったので、私は1つを設定していないと私は1つが必要と思っていませんでした。現在ははるかに速く走っています(1,000秒で0.049秒)。私はあなたのテストを見ていなかったと思っていないので、あなたの返信をありがとう。ありがとう。 – user1255191

0

いつでもSQL文が実行されるように、PL/SQLブロックをOracleサーバーに送信しています。 SQLエンジンを介して、プロシージャ文も実行されます。手続き的執行責任者を通じて。このプロシージャ・ステートメント・エグゼキュータは、PL/SQLエンジンで使用できます.SQL、PL/SQL文を使用して大量のロードを使用している場合、常にoracleサーバーはこれらのエンジンを介してこれらの文を個別に実行します。

このタイプの実行方法は、常にコンテンツスイッチング実行方法によってアプリケーションのパフォーマンスを低下させます。この問題を解決するために、oracleはコレクションを使用して「バルク・バインド」プロセスを導入しました。つまり、この方法ではoracleサーバーは一度にすべてのSQL文を実行します。 バルク収集:

この句は自動的に使用されます。 Oracleサーバーは日付を選択してコレクションに格納します。

  1. に使用されるバルクコレクト句を選択...に...句
  2. カーソルフェッチ文
  3. DML RETURNING句

はまたPL/SQL Bulk Collect And Bulk bind