2009-11-06 53 views
3

在我的應用程序使用TADOQuery使用select(MSSQL)與它的TClientdataSet聯繫。我必須插入大約百萬條記錄和ApplyUpdates。的TClientdataSet和大插入

所以,我在SQL Server事件探查看到了什麼?我看到,對於每個插入的行,我們有3個查詢:sp_prepare插入腳本,sp_execute它與一些值和sp_unprepare。

我只想準備sql 一次所有的記錄在插入之前和之後進行一番對比。我該怎麼做?

添加後

在查詢我對存儲過程執行的腳本:

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 

,它是所有的新行。

+2

我們可以看到一個腳本的例子嗎?您應該可以對其進行參數化,在這種情況下,您可以先進行預先分析,然後在重新執行之前每次更改參數。 – 2009-11-06 13:29:46

+0

添加了一些細節。 – silent 2009-11-06 13:56:34

回答

0

答案是在TADOConnection中使用的提供者。從MSDASQL切換到SQLOLEDB,現在一切正常,沒有任何額外的查詢。

+0

司機是原因?相當奇怪.. – 2011-05-23 22:46:30

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概念。

+0

是的,這是我需要的,但是...自動:) – silent 2009-11-07 11:57:24

+0

你是什麼意思自動?你的意思是它需要在數據庫觸發器中嗎? – 2009-11-07 12:32:33

+0

自動意味着我希望組件生成正確的查詢,而無需創建其他對象。 – silent 2009-11-09 11:18:09

0

思考...

  1. 你並不需要準備一個存儲過程調用。實際上,它已經準備好了。在大多數客戶端實現中,您可以將其改變。

  2. 您可能無法做到這一點的百萬行一氣呵成。你有一個batch size limit (eg a single DB call) of 256 MB(假設默認的4k網絡數據包)。

  3. 在其他客戶端實現中,您可以設置「batch size」(與第2點不同的概念),例如10,000,因此您只能撥打100個電話,而不是100萬。

+0

1.我不準備我的選擇 2.我知道 3.那麼,什麼? :) – silent 2009-11-12 08:06:20

+0

@silent:1.您正在準備,因爲SQL事件探查器顯示它:它可能是自動的,但通常可以關閉。 3.你能批量打電話嗎?畢竟,這是你問題的關鍵,當然。 – gbn 2009-11-12 08:23:26

+0

插入SQL使用TSQLResolver在Provider中生成,而不是由我生成。我調試它,發現在通過TADOCommand執行之前,它沒有準備好的標誌。 – silent 2009-11-12 12:00:44

0

我認爲其他的答案可能是善於幫助調整性能,但我想你用來訪問的TClientdataSet其實並不重要,因爲無論哪種方式用什麼方法,實際的數據庫更新是分開(並自動生成)。

如果更新作爲單獨的每行準備工作,那麼Borland似乎是一個糟糕的設計選擇,因爲很明顯更新許多行將需要相同的查詢,每次只有不同的參數。

另一方面,TClientDataSet用於內存數據庫,這意味着相對較小的數據庫。使用類似於一百萬行的東西可能會超出預期的用例。

另一方面,此時從您的應用程序中交換ClientDataSet可能會很麻煩。我會說,對於應用程序性能敏感的部分,我會自己跟蹤修改的行,並使用上述例程編寫手動更新例程。除此之外,您可以嘗試修改TClientDataSet的源代碼以使其更加高效,或者對其進行子分類並覆蓋應用更改的方法。 (個人而言,我使用SQLite3存儲在我的程序中,所以ClientDataSet沒什麼用處,我也沒有玩過它)。