通过链接服务器将实例A上的数据写入实例B,通常有以下两种方式
--方案1:在实例A上执行
insert into LinkForB.B..TableB select * from TableA
--方案2:在实例B上执行
insert into TableB select * from LinkForA.A..TableA
目前接手的数据库,大量使用链接服务器来搬迁数据,并且使用者随心所欲。本地写入到链接服务器,从链接服务器读数据写入本地,从链接服务器读数据写入到另一链接服务器。更有坑爹的,本来是写入本地,却在本地的前面加上本地实例的链接服务器!
以前没有特别关注哪种方式更好,有在群里针对搬迁数据的方式请教过,有朋友建议最好采用pull的形式链接方式,即insert into TableB select * from LinkForA.A..TableA
直到看到这篇文章,结合自己遇到的问题,尝试分析通过链接服务器insert数据的区别
此处为了方便,我所创建的链接服务器[MY]实际是指向当前实例,但是不影响对结果的分析。
首先创建测试表,并在测试表上创建触发器
USE TEST GO --创建测试表 CREATE TABLE [dbo].[Product]( [ProductID] INT, [Name] [nvarchar](50), [Remark] [nvarchar](50) ) GO --创建触发器 CREATE TRIGGER [dbo].[Product_TR] ON [dbo]. [Product] FOR INSERT AS BEGIN SET XACT_ABORT ON BEGIN TRY IF (select len(ProductID) from inserted)>0--当select查询只返回一个值时才能操作=、!=、<、<=、>、>= BEGIN PRINT ‘one row‘ END END TRY BEGIN CATCH PRINT ‘消息 ‘+CONVERT(VARCHAR,ERROR_NUMBER())+‘,级别 ‘+CONVERT(VARCHAR,ERROR_SEVERITY()) +‘,状态 ‘+CONVERT(VARCHAR,ERROR_STATE())+‘,过程 ‘+CONVERT(VARCHAR,ERROR_PROCEDURE()) +‘,第 ‘+CONVERT(VARCHAR,ERROR_LINE())+‘ 行‘ PRINT ERROR_MESSAGE() END CATCH END GO
注意触发器脚本,是为了在批量插入数据,select...from inserted子查询返回多条记录,进行>操作出错。
插入数据检验
--step1:插入1条数据,成功 INSERT [Test].[dbo].[Product]( [ProductID],[Name],[Remark]) SELECT TOP 1 [ProductID],[Name],‘one row‘ FROM [AdventureWorks2008R2].[Production].[Product] --step2:插入5条数据,失败 INSERT [Test].[dbo].[Product]( [ProductID],[Name],[Remark]) SELECT TOP 5 [ProductID],[Name],‘more than one row‘ FROM [AdventureWorks2008R2].[Production].[Product] --step3:插入5条数据到链接服务器,成功 INSERT [MY].[Test].[dbo].[Product]([ProductID],[Name],[Remark]) SELECT TOP 5 [ProductID],[Name],‘ToLink more than one row‘ FROM [AdventureWorks2008R2].[Production].[Product] --Step4:从链接服务器获取5条数据再插入,失败 INSERT [Test].[dbo].[Product]( [ProductID],[Name],[Remark]) SELECT TOP 5 [ProductID],[Name],‘FromLink more than one row‘ FROM [MY].[AdventureWorks2008R2].[Production].[Product]
我们查看表中结果
通过图中的Remark字段,可以看出step1和step3的语句执行成功,step2和step4的语句执行失败
step1:插入1条数据时,select...from inserted子查询返回1行值,它可和0进行比较(>),数据insert成功。
step2:插入5条数据时,select...from inserted子查询返回5行值,它不能和单一的0进行比较,触发器报错,整个insert语句失败。
step3:插入5条数据到链接服务器,竟然成功!对比前面的语句,说明insert into linksvr.db.sch.tb是一行一行的insert(因为表上有触发器,如果一次多行insert会报错)
step4:当从链接服务器获取5条数据,再insert失败!它近似等效于step2。
现在我们的问题是step3和step4到底哪个更高效?虽然此时我们已经知道,step3会拆分成单条写入,step4一次写入多条。为了让这两个语句正常执行,我们禁用表上的触发器
--禁用/启用触发器 alter table [dbo].[Product] disable trigger [Product_TR] --disable/enable --查看触发器 select name,type_desc,create_date,modify_date,is_disabled from sys.triggers t
开启跟踪,然后再次执行step3、step4中的语句
INSERT [MY].[Test].[dbo].[Product]([ProductID],[Name],[Remark]) SELECT TOP 5 [ProductID],[Name],‘ToLink more than one row‘ FROM [AdventureWorks2008R2].[Production].[Product]
在SPID=52窗口执行INSERT Linksvr SELECT TOP 5 FROM语句,链接服务器在SPID=55下执行,有5个RPC:Completed事件
INSERT [Test].[dbo].[Product]( [ProductID],[Name],[Remark]) SELECT TOP 5 [ProductID],[Name],‘FromLink more than one row‘ FROM [MY].[AdventureWorks2008R2].[Production].[Product]
在SPID=52窗口执行INSERT Tab SELECT TOP 5 FROM Linksvr语句,链接服务器在SPID=55的会话中执行,仅一个RPC:Completed事件
此时表中的数据如下
此时全部将表写入到测试表
Duration字段可以看出insert linksvr要比insert select linksvr耗时些
profile启动跟踪,sys.traces 增加一新记录(status=1),暂停跟踪,原记录的status=0,停止跟踪,原记录删除
profile启动跟踪,sys.traces 增加一新记录(status=1)
SELECT * FROM sys.traces t
语句暂停跟踪
exec sp_trace_setstatus 2, 0
sys.traces 对应记录删除,profile如新创建的跟踪样(暂未开启)
profile启动跟踪,sys.traces 增加一新记录(status=1),暂停跟踪,原记录的status=0
语句启动跟踪
exec sp_trace_setstatus 2, 1
sys.traces 对应记录status=1,说明已启动,但是profile中依旧是暂停状态!
请问此时sys.traces 中的traceid=2的跟踪是否有开启,如果开启了它收集的数据在哪?
此时点击profile中的启动按钮,弹出"在进行修改之前,必须先停止活动跟踪。"
确定,profile恢复成初始状态!