Sql server 使用“whileexists”循环缓慢插入

Sql server 使用“whileexists”循环缓慢插入,sql-server,performance,loops,insert,while-loop,Sql Server,Performance,Loops,Insert,While Loop,我正在尝试向表中插入大量记录。 这是一个场景: SQLServer2008DB是2005 目标表具有聚集索引PK。这个字段应该是一个标识,但是DB的开发人员无法更改它,因为它会影响程序将其创建为整数。每次程序需要向表中添加一行时,请查看本例中的max id historyno并求和一行 当我们需要同时插入大量记录时,这会影响我们的性能,因此我们创建了一个流程,从生产时间以外的临时表AKT_ES_CampTool_TempHist中插入行 问题是,在一小时内,它只插入8K行。考虑到我们需要插入超过

我正在尝试向表中插入大量记录。 这是一个场景:

SQLServer2008DB是2005

目标表具有聚集索引PK。这个字段应该是一个标识,但是DB的开发人员无法更改它,因为它会影响程序将其创建为整数。每次程序需要向表中添加一行时,请查看本例中的max id historyno并求和一行

当我们需要同时插入大量记录时,这会影响我们的性能,因此我们创建了一个流程,从生产时间以外的临时表AKT_ES_CampTool_TempHist中插入行

问题是,在一小时内,它只插入8K行。考虑到我们需要插入超过120K的数据,我们的时间不够了

我们使用的代码如下。请,如果有人有任何改进的想法,我们将不胜感激

DECLARE             @HistNo         AS INT

WHILE EXISTS (SELECT * FROM AKT_ES_CampTool_TempHist WHERE Inserted = 0)
BEGIN

    SELECT  @HistNo=MIN(HistoryNo) FROM AKT_ES_CampTool_TempHist WHERE Inserted = 0

    INSERT INTO NOVADB.dbo.niHist   (
        HistoryNo,ObjectType,ObjectNo,SubNo,ReferenceNo, 
        Time,Type,Priority,Collector,Code,
        Action,RemainingAmount,Obliterated,SubType,ActSegment,
        Data,FreetextData,quantity
                                        )
        SELECT      
        (SELECT  max(historyNo)+1 
        FROM    NOVADB..niHist),ObjectType,ObjectNo,SubNo,ReferenceNo,
        Time,Type,Priority,Collector,Code,
        Action,RemainingAmount,Obliterated,SubType,ActSegment,
        Data,FreetextData,quantity 
        FROM        AKT_ES_CampTool_TempHist 
        WHERE       HistoryNo=@HistNo

        UPDATE  AKT_ES_CampTool_TempHist
        SET     Inserted=1
        WHERE   HistoryNo=@HistNo

END

您可以选择应该插入到临时表中的数据,该临时表具有由Rownumber生成的新HistoryNo,并使用NOVADB..niHist中的maxhistoryNo进行更改

SELECT  ROW_NUMBER() OVER (Order by ID) as NEW_HistoryNo , * 
into #tmp
FROM AKT_ES_CampTool_TempHist 
WHERE Inserted = 0
ORDER BY HistoryNo

Update #tmp set NEW_HistoryNo=NEW_HistoryNo + (SELECT  max(historyNo) FROM    NOVADB..niHist)

INSERT INTO NOVADB.dbo.niHist   (
        HistoryNo,ObjectType,ObjectNo,SubNo,ReferenceNo, 
        Time,Type,Priority,Collector,Code,
        Action,RemainingAmount,Obliterated,SubType,ActSegment,
        Data,FreetextData,quantity )                                 )
SELECT      
        NEW_HistoryNo,ObjectType,ObjectNo,SubNo,ReferenceNo,
        Time,Type,Priority,Collector,Code,
        Action,RemainingAmount,Obliterated,SubType,ActSegment,
        Data,FreetextData,quantity
from #tmp

Update AKT_ES_CampTool_TempHist  set Inserted = 1
from #tmp
Where #tmp.HistoryNo=AKT_ES_CampTool_TempHist.HistoryNo and AKT_ES_CampTool_TempHist.Inserted = 0

Drop Table #tmp

显然,正确的答案是将historyNo列更改为一个标识,但既然您无法做到这一点,为什么不在整个集合上使用ROW_NUMBER来获得一个递增的数字,以添加到prev max historyNo

然后您可以将插入更改为

DECLARE  @OldMaxHistNo  AS INT
SELECT @OldMaxHistNo = MAX(historyNo) FROM NOVADB..niHist

INSERT INTO NOVADB.dbo.niHist   (
    HistoryNo,ObjectType,ObjectNo,SubNo,ReferenceNo, 
    Time,Type,Priority,Collector,Code,
    Action,RemainingAmount,Obliterated,SubType,ActSegment,
    Data,FreetextData,quantity
                                    )
    SELECT      
    @OldMaxHistNo+ ROW_NUMBER() OVER(ORDER BY ObjectNo)
    FROM    NOVADB..niHist),ObjectType,ObjectNo,SubNo,ReferenceNo,
    Time,Type,Priority,Collector,Code,
    Action,RemainingAmount,Obliterated,SubType,ActSegment,
    Data,FreetextData,quantity 
    FROM        AKT_ES_CampTool_TempHist 
    WHERE       Inserted = 0

    UPDATE  AKT_ES_CampTool_TempHist
    SET     Inserted=1

在执行事务时可能必须锁定事务中的表,但决不能使用分配索引时使用的max+1策略。假设您不能使用标识和主表,并且您没有使用最新版本的sql server,请基于标识字段创建一个阴影表,并使用该阴影表生成序列号

i、 e

然后生成一个id-比max+1便宜-无锁定问题

create proc AKT_ES_CampTool_idgen(@newid output)
(
  declare @newid int
  begin tran

  insert into dbo.AKT_ES_CampTool_Shadow (dummy) values ('') 
  select @newid = scope_id()
  rollback  
)
你不会说AKT_ES_CampTool_TempHist有多大。如果该字段较大,则可能会出现性能问题,尤其是在插入的字段上没有索引的情况下

您可以首先创建一个包含相关列的表变量

declare @TempHist table
(
  HistNo int
, inserted int
, etc.

primary key(...)
)
然后使用单个插入查询填充@TempHist。如果您没有此表的适当PK,请使用生成的RowID标记PK

现在,您可以在不引起锁争用的情况下循环这个表。只需从@TempHist中选择top 1,并在完成处理后从@TempHist中删除相应的行


您不必使用光标,也不必进行大批量操作

您好,谢谢您的回复。一次插入整个表很好,但是由于有很多进程在这个表上插入行,我们需要进行动态插入以避免重复PK的错误。忘了说临时表上的HistoryNo只是一个ID,与主表上的HistoryNo不相关。我以前这样做过,但事务的问题是,我会将表保持锁定状态,以便其他进程无法访问该表。如果我错了,请更正我的错误。我完全同意正确的方法是将HistoryNo转换为身份,也许在以后的版本中…你会在那里,但是,隔离级别可序列化的事务是阻止另一个进程从表中读取错误的最大ID,然后尝试写入与大数据集中的条目冲突的+1值的唯一方法。也许您可以使用一种混合解决方案—在while循环中一次插入1000个,提交事务,然后在每次迭代后启动另一个事务?我现在唯一的另一个建议就是永远不要插入HistoryNo值,而是让表上的触发器计算出来。对不起,我不明白你的意思。。。AKT_ES_CampTool_TempHist是可变的,理想情况下,必须在一天开始时插入0行。在月底,我们的表上通常有300K-400K行。此表上的插入从200到100K。从未使用过scope_标识,也不知道如何填充阴影表。AKT_ES_CampTool_TempHist在HistoryNo身份上有PK,与niHist上的HistoryNo无关。历史索引No和HistoryNo Inserted在你告诉我之前没有一个关于Inserted的索引,现在是双倍速度,仍然很慢,但这是一个进步
declare @TempHist table
(
  HistNo int
, inserted int
, etc.

primary key(...)
)