在我的应用程序中,我使用TADOQuery和select(MSSQL)并将其与TClientDataSet链接。我必须插入大约一百万条记录和ApplyUpdates。
那么我在SQL Server Profiler中看到了什么?我看到,对于每个插入的行,我们有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
并且所有新行都。
答案 0 :(得分: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概念。
答案 1 :(得分:0)
...思想
您无需准备存储过程调用。实际上,它已经准备好了。您可以在大多数客户端实现中将其打开。
您可能无法一次性完成一百万行。您有一个batch size limit (eg a single DB call) of 256 MB(假设默认的4k网络数据包)。
在其他客户端实现中,您可以设置“batch size”(与第2点不同的概念),例如10,000,因此您只能拨打100个电话而不是100个。
答案 2 :(得分:0)
我认为其他答案可能很好地帮助调整性能,虽然我想你使用什么方法来访问TClientDataSet并不重要,因为无论哪种方式,实际的数据库更新都是独立的(并自动生成) )。
如果更新作为每条线的单独准备工作,那么Borland似乎是一个糟糕的设计选择,因为显然更新许多行将需要相同的查询,每次只有不同的参数。
另一方面,TClientDataSet适用于内存数据库,这意味着相对较小的数据库。使用像百万行的东西可能超出了预期的用例。
另一方面,此时从应用程序中交换ClientDataSet可能会很麻烦。我会说,对于应用程序的性能敏感部分,我会自己跟踪修改的行,并使用上面提到的例程编写手动更新例程。除此之外,您可以尝试修改TClientDataSet的源代码以使其更有效,或者对其进行子类化并覆盖应用更改的方法。
(就个人而言,我在我的程序中使用SQLite3进行存储,因此ClientDataSet没什么用处,而且我还没有玩过很多)。
答案 3 :(得分:0)
答案是在TADOConnection中使用的提供程序中。从MSDASQL切换到SQLOLEDB,所有这些都是现在,没有任何其他查询。