我正在尝试将大量记录插入表中。 这是场景:
SQL Server 2008(数据库是2005)
目标表具有聚簇索引(PK)。该字段应该是Identity,但DB的开发人员(我们无法更改它,因为它会影响程序)将其创建为Integer。每次程序需要向表中添加一行时,请查看最大ID(在此情况下为historyno
)并加1。
当我们需要同时插入大量记录时,这会影响我们的性能,因此我们创建了一个从生产时间之外的临时表(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
答案 0 :(得分:2)
显然,正确的答案是将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
虽然
可能必须在事务中锁定表格答案 1 :(得分:1)
您可以选择应插入临时表的数据,其中包含由Rownumber()生成的新HistoryNo,并使用max(historyNo)FROM NOVADB..niHist进行更改。
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
答案 2 :(得分:0)
您永远不应该使用您用于分配索引的max + 1策略。假设您不能使用标识和主表,并且您没有使用最新版本的sql server - 基于标识字段创建影子表并使用它来生成序列号
即
create table AKT_ES_CampTool_Shadow
(
id int identity(1234,1) not null -- replacing 1234 with a value based on max+1
, dummy varchar(1) null
)
然后生成一个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中选择前1,并在完成处理后从@TempHist中删除相应的行。
您不会使用游标,也不会进行大型批处理操作