sql-server – 在同一个数据库中将数百万条记录从一个表复制到另一个具有相同模式的表

我试图找到将大量行(约7000万)从一个表复制到另一个表的最佳方法,在同一个数据库中使用相同的模式.

我们的系统目前使用一组A / B表运行,这些表通过View在其他地方引用.我们的客户可以选择将数据作为完整文件或增量文件发送给我们.当摄取完整文件时,“非活动”表将被截断并直接加载新数据.对于增量文件,我们首先将所有数据从“活动”复制到“非活动”,然后执行增量更新.在这两种情况下,当数据加载完成后,我们将视图切换到适当的表.

这项工作多年来一直很好;通常客户端会在夜间向我们发送数据文件,并在没有人使用系统时处理其数据.但是,我们的一个大客户现在希望在一天内向我们发送增量文件.对于较小尺寸的客户端,A-> B复制操作相当快(600k行约30秒),但是这个大客户端的副本需要30-60分钟,具体取决于负载.

两个表的模式如下(用TableA替换TableB):

CREATE TABLE [dbo].[TableA](
    [ClientId] NCHAR(10) NOT NULL,
    [ItemId] INT NOT NULL,
    [Amount] DECIMAL(19,6) NOT NULL,
    [Loaded] DATETIME NOT NULL,
 CONSTRAINT [PK_TableA] PRIMARY KEY CLUSTERED ( [ItemId] ASC, [ClientId] ASC) ON [PRIMARY])

ALTER TABLE [dbo].[TableA]  WITH NOCHECK ADD  CONSTRAINT [FK_TableA_Items] FOREIGN KEY([ItemId])
REFERENCES [dbo].[Items] ([ItemId])

ALTER TABLE [dbo].[TableA] CHECK CONSTRAINT [FK_TableA_Items]

ALTER TABLE [dbo].[TableA]  WITH NOCHECK ADD  CONSTRAINT [FK_TableA_Clients] FOREIGN KEY([ClientId])
REFERENCES [dbo].[Clients] ([ClientId])

ALTER TABLE [dbo].[TableA] CHECK CONSTRAINT [FK_TableA_Clients]

CREATE NONCLUSTERED INDEX [IX_TableA_ClientId_ItemID_INC_Amount] ON [dbo].[TableA] ([ClientId] ASC, [ItemId] ASC )  INCLUDE ([Amount]) 

我们当前使用的insert / select语句很简单:

INSERT INTO [dbo].[TableB]  ( ClientId, [ItemId], [Amount], [Loaded] )
SELECT ClientId, ItemId, Amount, Loaded FROM [dbo].[TableA]

我尝试了各种方法来优化这个副本;我已经将TabLock添加到目标表中,尝试设置Trace Flag 610并尝试了许多不同的批量大小,但似乎没有任何效果.我尝试过的查询的一个变体是:

DECLARE @BatchSize BIGINT = 50000;
WHILE 1 = 1
BEGIN
      INSERT INTO TableB WITH ( TABLOCK ) ( [ClientId], [ItemId], [Amount], [Loaded] )
      SELECT TOP ( @BatchSize ) ClientId, ItemId,Amount, Loaded
      FROM      TableA t1
      WHERE     NOT EXISTS ( SELECT 1 FROM TableB WHERE ClientId = t1.ClientId AND ItemId = t1.ItemId );

      IF @@ROWCOUNT < @BatchSize
         BREAK;
END;

我试过删除/重新创建索引,但这也证明是昂贵的.我提出的最佳时间大约是5分钟,但这涉及删除目标表并执行select into,然后添加索引并检查约束.这纯粹是“教育性” – 由于模式绑定视图和其他一些阻止这种方法的项目,我不能走这条路线(除了我们的普通视图,我们有一个“B”视图,指向另一个表格仅在此过程中使用)

虽然我知道SSIS可能是最好的方法,但现在还不可能 – 我的任务是加速现有流程,如果可能的话.我确实可以控制从C#调用的程序等.

话虽如此,我还有其他选择吗?我可以做任何我想要的非活动表,因为没有使用它除了这些程序.我正在考虑的一件事是以某种方式从C#用BCP导出数据,然后重新摄取它.除了不确定它是否可能之外,我不知道它是否会更好(或者更糟糕的是)

最佳答案
如何将所有增量数据保留在非活动表中直到当天结束,然后将其加载到活动状态并截断非活动状态?您的视图可以使用UNION ALL引用这两个表,这样您就不会在两个表之间切换.

您可以使用相同的方法使用分区交换.您要对两个表进行分区,在晚上11点之前加载非活动状态,然后将非活动的加载分区交换为活动分区.您的视图仍然需要引用这两个表.这样,除了最初导入文件所需的时间之外,您没有加载的停机时间,并且增量文件应该每天加载到空表中.

转载注明原文:sql-server – 在同一个数据库中将数百万条记录从一个表复制到另一个具有相同模式的表 - 代码日志