我有一个包含大量行的表,我希望通过动态SQL执行。它们基本上是存在检查和插入语句,我想将数据从一个生产数据库迁移到另一个生产数据库 - 我们正在合并事务数据。我试图找到执行行的最佳方式。
我一直在找到一种合并方法,用于将所有行相互附加到不高效,尤其是当一次执行的行数大于~100时。
假设源表的结构是任意的,如下所示:
CREATE TABLE [dbo].[MyTable]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[DataField1] [int] NOT NULL,
[FK_ID1] [int] NOT NULL,
[LotsMoreFields] [NVARCHAR] (MAX),
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED ([ID] ASC)
)
CREATE TABLE [dbo].[FK1]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [int] NOT NULL, -- Unique constrained value
CONSTRAINT [PK_FK1] PRIMARY KEY CLUSTERED ([ID] ASC)
)
另一个要求是我正在跟踪源表PK与目标PK以及是否发生了插入或者我是否已将该行迁移到目标。为此,我正在跟踪另一个表中的迁移行,如下所示:
CREATE TABLE [dbo].[ChangeTracking]
(
[ReferenceID] BIGINT IDENTITY(1,1),
[Src_ID] BIGINT,
[Dest_ID] BIGINT,
[TableName] NVARCHAR(255),
CONSTRAINT [PK_ChangeTracking] PRIMARY KEY CLUSTERED ([ReferenceID] ASC)
)
我现有的方法是执行由存储过程生成的一些动态sql。存储的proc执行PK查找,因为源系统具有表[dbo]的不同PK值。[FK1]。例如。
IF NOT EXISTS (<ignore this existence check for now>)
BEGIN
INSERT INTO [Dest].[dbo].[MyTable] ([DataField1],[FK_ID1],[LotsMoreFields]) VALUES (333,(SELECT [ID] FROM [Dest].[dbo].[FK1] WHERE [Name]=N'ValueFoundInSource'),N'LotsMoreValues');
INSERT INTO [Dest].[dbo].[ChangeTracking] ([Src_ID],[Dest_ID],[TableName]) VALUES (666,SCOPE_IDENTITY(),N'MyTable'); --666 is the PK in [Src].[dbo].[MyTable] for this inserted row
END
所以当你有一百万这些时,它并不快。
是否有推荐的高效方法?
如上所述,当您查看复杂的JOIN条件时,MERGE语句很有效(如果这些字段中的任何一个不同,则更新记录以匹配)。您还可以考虑创建整个记录的HASHBYTES哈希,以快速找到源表和目标表之间的差异,尽管这对于非常大的数据集来说也很耗时。
听起来像是通过检查每一行匹配然后进行插入,就像前端开发人员一样进行这些更新。使用单个查询执行插入会更有效。下面是一个示例,它查找tblNewClient表中的名称,但不在tblClient表中查找:
INSERT INTO tblClient
( [Name] ,
TypeID ,
ParentID
)
SELECT nc.[Name] ,
nc.TypeID ,
nc.ParentID
FROM tblNewClient nc
LEFT JOIN tblClient cl
ON nc.[Name] = cl.[Name]
WHERE cl.ID IS NULL;
这比RBAR更有效率(通过痛苦排行)。
从@RusselFox得到两个答案并将它们放在一起,我达到了这个试验性的解决方案(但看起来效率更高):
MERGE INTO [Dest].[dbo].[MyTable] [MT_D]
USING (SELECT [MT_S].[ID] as [SrcID],[MT_S].[DataField1],[FK_1_D].[ID] as [FK_ID1],[MT_S].[LotsMoreFields]
FROM [Src].[dbo].[MyTable] [MT_S]
JOIN [Src].[dbo].[FK_1] ON [MT_S].[FK_ID1] = [FK_1].[ID]
JOIN [Dest].[dbo].[FK_1] [FK_1_D] ON [FK_1].[Name] = [FK_1_D].[Name]
) [SRC] ON 1 = 0
WHEN NOT MATCHED THEN
INSERT([DataField1],[FL_ID1],[LotsMoreFields])
VALUES ([DataField1],[FL_ID1],[LotsMoreFields])
OUTPUT [SRC].[SrcID],INSERTED.[ID],0,N'MyTable' INTO [Dest].[dbo].[ChangeTracking]([Src_ID],[Dest_ID],[AlreadyExists],[TableName]);