3

私のアプリケーションでは、select (MSSQL) で TADOQuery を使用し、TClientDataSet とリンクしています。約 100 万件のレコードと ApplyUpdates を挿入する必要があります。

では、SQL Server Profiler には何が表示されるのでしょうか? 挿入された行ごとに、3 つのクエリがあることがわかります。挿入スクリプトの sp_prepare、いくつかの値を指定した sp_execute、および sp_unprepare です。

挿入する前にすべてのレコードに対してSQLを1回準備し、後で準備を解除したいだけです。どうすればいいですか?

の後に追加:

クエリには、ストアド プロシージャを実行するためのスクリプトがあります。

tmpQuery := DefineQuery(FConnection, [
  'exec up_getOperatorDataSet ',
  '  @tablename     = :tablename, ',
  '  @operator      = :operator, ',
  '  @forappend     = :forappend, ',
  '  @withlinksonly = :withlinksonly, ',
  '  @ids           = :ids '
], [
  Param(ftString, sTableName),
  Param(ftInteger, FOperatorId),
  Param(ftBoolean, opForAppendOnly in OpenParams),
  Param(ftBoolean, opOnlyWithModelLinks in OpenParams),
  Param(ftString, sIds)
], Result);

いくつかのパラメーターを使用して、テーブルsTableNameからすべてのフィールドを選択します。

プロファイラーから挿入する例:

ステップ1:

declare @p1 int
set @p1=486
exec sp_prepare @p1 output,N'@P1 int,@P2 int,@P3 datetime,@P4 int,@P5 int,@P6 int,@P7 int,@P8 int,@P9 varchar(128),@P10 bit,@P11 numeric(19,4),@P12 smallint,@P13 smallint,@P14 smallint,@P15 smallint',N'insert into parser_prices
  (operator_id, request_id, date, nights, model_hotel_id, model_meal_id, model_room_id, model_htplace_id, spo, hotelstop, price, frout_econom, frout_business, frback_econom, frback_business)
values
  (@P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8, @P9, @P10, @P11, @P12, @P13, @P14, @P15)
',1
select @p1

ステップ2:

exec sp_execute 486,21,2000450,'2009-12-04 00:00:00',14,2118,22,-9555,18,'2009-10.MSK.Bali.13.10.09-27.03.10',0,15530.0000,3,3,3,3

ステップ 3:

exec sp_unprepare 486

それはすべての新しい行のためのものです。

4

4 に答える 4

1

コード内のインライン クエリではなく、ストアド プロシージャを呼び出しているため、SQL Server はストアド プロシージャへの各呼び出しを個別の呼び出しとして扱い、毎回準備と準備解除を行っています。これを回避する方法があるかどうかはわかりません。

ストアド プロシージャ内で起こっていることがコード内のクエリから実行できる場合は、SQL ステートメントを初めて準備する次のような構造を使用できます。

{Prepare the insert query}
ADOQuery1.SQL.Append('INSERT INTO Tablename');
ADOQuery1.SQL.Append('(StringField1, IntField2)');             {repeat as necessary}
ADOQuery1.SQL.Append('VALUES (:sFieldValue1, :sFieldValue2)'); {repeat as necessary}
ADOQuery1.SQL.Prepare;

{In a For, While, Repeat loop, use:}
ADOQuery1.ParamByName('sFieldValue1').AsString := 'Value for field 1';
ADOQuery1.ParamByName('sFieldValue2').AsInteger := 2;
ADOQuery1.ExecSQL;

ADOQuery コンポーネントのプロパティ名とメソッド名が正しくない場合は申し訳ありません。現在、私は Delphi PC を使用しておらず、通常は TADO コンポーネントを使用していませんが、これはTDataSet の概念。

于 2009-11-07T11:46:47.827 に答える
0

考え...

  1. ストアド プロシージャの呼び出しを準備する必要はありません。事実上、それはすでに準備されています。ほとんどのクライアント実装では、これをオフにすることができます。

  2. 一度に 100 万行を処理できない場合があります。256 MBのバッチ サイズ制限 (たとえば、単一の DB 呼び出し) があります (デフォルトの 4k ネットワーク パケットを想定)。

  3. 他のクライアント実装では、「バッチ サイズ」(ポイント 2 とは異なる概念)、たとえば 10,000 を設定できるため、100 万ではなく 100 回の呼び出ししかできません。

于 2009-11-11T20:24:00.563 に答える
0

その答えは、TADOConnection で使用されるプロバイダーにありました。MSDASQL から SQLOLEDB に切り替えて、追加のクエリなしですべてが今すぐです。

于 2009-11-12T13:42:48.787 に答える
0

他の回答はパフォーマンスの調整に役立つと思いますが、TClientDataSet にアクセスするために使用する方法は実際には問題ではないと思いますが、どちらの方法でも、実際のデータベースの更新は別のものである (そして自動的に生成される) ためです。

更新が各行の個別の準備として機能する場合、多くの行を更新するには毎回異なるパラメーターを使用するだけで同じクエリが必要になることは明らかであるため、Borland の設計上の選択は不適切だったようです。

一方、TClientDataSet はメモリ内データベース、つまり比較的小規模なデータベースを対象としていました。100 万行のようなものを使用することは、おそらく意図したユース ケースを超えています。

一方、この時点でアプリケーションから ClientDataSet を交換するのは面倒かもしれません。アプリケーションのパフォーマンスが重要な部分については、変更された行を自分で追跡し、上記のルーチンを使用して手動更新ルーチンを作成します。それに加えて、TClientDataSet のソース コードを変更してより効率的にするか、サブクラス化し、変更を適用するメソッドをオーバーライドすることもできます。

(個人的には、プログラムのストレージに SQLite3 を使用しているため、ClientDataSet はほとんど役に立たず、あまり使っていません)。

于 2009-11-12T07:04:54.730 に答える