4

Oracle 11g データベースで ODP.NET 11.2.0.2.0 を使用する C# .NET 4.0 アプリケーションを使用しています。アプリケーションはいくつかのルックアップ テーブルにデータをプリロードします。ほとんどのテーブルは 20 レコード未満であるため、スクリプトは非常に高速に実行されます。ただし、スクリプトの 1 つには 802 のレコードがあり、レコードを挿入するのに 248.671 秒かかります。これは、大量のデータで高速な操作を宣伝するデータベースのような少量のデータには過剰に思えます。

だから私は、スクリプトが書かれている現在の方法よりも、スクリプトを介してデータを挿入するためのより速い方法はあるのでしょうか?

挿入されるテーブルは次のように定義されます。

CREATE TABLE FileIds
(
     Id                 NUMERIC(38)                         NOT NULL
    ,Name               NVARCHAR2(50)   DEFAULT 'Unknown'   NOT NULL 
    ,FileTypeGroupId    NUMERIC(38)                         NOT NULL
    ,CONSTRAINT FK_FileIds_FileTypeGroups FOREIGN KEY ( FileTypeGroupId ) REFERENCES FileTypeGroups ( Id )
)

挿入するスクリプトは次のようになります。

BEGIN
    INSERT ALL
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1152,5)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1197,10)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1200,6)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1143,3)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1189,9)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1109,7)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1166,4)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (0,8)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1149,2)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1400,1)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1330,11)
        INTO FileIds ( Id, FileTypeGroupId ) VALUES (1000,0)
        -- 790 Records removed for example purposes.
        SELECT * FROM DUAL;
    COMMIT;
END;

外部キーで参照される FileTypeGroups テーブルは、FileIds テーブルをロードする前に事前にロードされます。FileIds テーブルに関連付けられたシーケンスまたはトリガーはなく、テーブルのインデックスはまだ作成されていません。

4

1 に答える 1

5

問題

解析時間は、特定の種類のステートメント、特に。で指数関数的に増加する可能性がありINSERT ALLます。例えば:

--Clear any cached statements, so we can consistently reproduce the problem.
alter system flush shared_pool;
alter session set sql_trace = true;

--100 rows
INSERT ALL
    INTO FileIds(Id,FileTypeGroupId) VALUES(1, 1)
    ...
    repeat 100 times
    ...
select * from dual;

--500 rows
INSERT ALL
    INTO FileIds(Id,FileTypeGroupId) VALUES(1, 1)
    ...
    repeat 500 times
    ...
select * from dual;

alter session set sql_trace = false;

tkprofを介してトレースファイルを実行すると、多数の行で解析時間が劇的に増加することがわかります。例えば:

100行:

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.06       0.05          0          1          0           0
Execute      1      0.00       0.00          0        100        303         100
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.06       0.05          0        101        303         100

500行:

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1     14.72      14.55          0          0          0           0
Execute      1      0.01       0.02          0        502       1518         500
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2     14.74      14.58          0        502       1518         500

ソリューション

  1. 大きなステートメントをいくつかの小さなステートメントに分割します。最適なサイズを見つけるのは難しいです。Oracleの一部のバージョンでは、問題の原因となる魔法の数の行があります。私は通常、約100行を使用します。これは、ステートメントのグループ化の利点のほとんどを得るのに十分ですが、解析のバグを回避するのに十分な低さです。また...
  2. insert into ... select ... from dual union all ...代わりにこの方法を試してください。通常ははるかに高速に実行されますが、解析パフォーマンスもサイズによって大幅に低下する可能性があります。
  3. Oracleをアップグレードします。新しいバージョンでは、解析パフォーマンスが向上しています。バージョン12.2ではこの問題を再現できなくなりました。

警告

これから間違った教訓を学ばないでください。SQLのパフォーマンスが心配な場合は、99%の場合、類似したものを分割するのではなく、グループ化する方がよいでしょう。あなたは正しい方法で物事をやっています、あなたはただ奇妙なバグに遭遇しました。(My Oracle Supportを検索しましたが、これに関する公式のバグは見つかりませんでした。)

于 2012-07-26T05:42:07.313 に答える