Sql 使用变量与文字时,远程查询速度较慢
我到处都在寻找这种情况,除了动态SQL之外,找不到解决方案,我不想使用动态SQL 以下是我要在服务器2上更新的表:Sql 使用变量与文字时,远程查询速度较慢,sql,sql-server,sql-server-2005,sql-update,openquery,Sql,Sql Server,Sql Server 2005,Sql Update,Openquery,我到处都在寻找这种情况,除了动态SQL之外,找不到解决方案,我不想使用动态SQL 以下是我要在服务器2上更新的表: (Stuff Id UNIQUEIDENTIFIER , stuffname NVARCHAR(64)) 我需要从服务器1更新它 所以我一直在尝试: DECLARE @newstuff nvarchar(64) SELECT @newstuff = 'new stuff' UPDATE [server2].database2.dbo.Stuff SET stuffname=@
(Stuff Id UNIQUEIDENTIFIER
, stuffname NVARCHAR(64))
我需要从服务器1更新它
所以我一直在尝试:
DECLARE @newstuff nvarchar(64)
SELECT @newstuff = 'new stuff'
UPDATE [server2].database2.dbo.Stuff
SET stuffname=@newstuff
WHERE stuffId='4893CD93-08B3-4981-851B-5DC972288290'
这需要11秒。下一个使用文本的运行时间不到1秒
UPDATE [server2].database2.dbo.Stuff
SET stuffname='new stuff'
WHERE stuffId='4893CD93-08B3-4981-851B-5DC972288290'
我比较了实际执行计划。慢的是进行远程扫描,花费100%的成本,再加上其他5个步骤(过滤、表假脱机、计算标量、远程更新、更新)。快速的只执行更新和远程查询步骤。我需要使用变量,所以我需要一种方法来强制它远程执行整个查询
我尝试过使用OPTION(重新编译),但server1使用的是SQL Server 2005。server2正在使用SQL Server 2012。如果没有严重问题,我根本无法更改server2上的数据库结构。我没有任何身份验证问题。我在更新表时尝试了使用别名
我也尝试过使用Openquery。当我将id筛选器放在查询字符串中时,它返回到1秒以下:
UPDATE OPENQUERY([server2], 'select stuffname, stuffid from database2.dbo.stufftable where contactid=''4CA1D489-9221-E511-A441-005056C00008''')
SET stuffname = @newstuff
但我需要该id也是一个变量,并且该开放查询不接受变量()。我尝试在查询外部使用id过滤器运行Openquery,但这只需要4秒钟。比11强,但不是很好:
UPDATE OPENQUERY([server2],'select stuffname, stuffid from database2.dbo.stufftable')
set stuffname=@newstuff
where contactid='4CA1D489-9221-E511-A441-005056C00008'
当然,我使用exec(@sql)运行openquery,但我真的不想这样做。我可以用文字来完成整个update语句,甚至不用OPENQUERY,也可以得到同样的结果
我有没有办法在不使用exec(@sql)的情况下修复此性能?您可以在远程使用sp_executesql对参数使用动态sql
declare @SQL nvarchar(max);
set @SQL = 'UPDATE database2.dbo.Stuff
SET stuffname=@newstuff
WHERE stuffId=''4893CD93-08B3-4981-851B-5DC972288290'''
exec [server2].master.dbo.sp_executesql @SQL, N'@newstuff nvarchar(64)', @newstuff
我会这样做。我不会将实际的
UPDATE
查询发送到server2,而是在server2上创建一个包含必要参数的存储过程,并从server1调用它
在存储过程内部,您可以根据需要使用server2的所有功能调整查询以使其快速运行(例如,像选项(重新编译)
)
此外,像这样的显式存储过程定义了两个系统如何交互的接口,这本身就很好。解决方案应该是确保使用的参数与列的长度和类型匹配。例如,确保NVARCHAR(64)列以“DECLARE@var AS NVARCHAR(64)”为目标
在您的示例中,情况似乎是这样,但在本地设置中进行测试时(使用SQLEXPRESS 2005链接自SQLEXPRESS 2014),我仅在长度和类型不匹配时获得“远程扫描” 问题可能与唯一标识符列有关。您是否尝试过: 在server2上定义以下存储过程:
CREATE PROCEDURE updateStuff( @newstuff nvarchar(30), @stuffid varchar(36))
AS
UPDATE Stuff
SET stuffname=@newstuff
WHERE stuffId=convert(uniqueidentifier, @stuffid))
来自服务器1的调用:
exec server2.database2.updatestuff N'New stuff','4893CD93-08B3-4981-851B-5DC972288290'
旧建议:
声明@stuffid uniqueidentifier
Set@stuffid=convert(唯一标识符,='4893CD93-08B3-4981-851B-5DC972288290')
更新[server2].database2.dbo.Stuff
SET stuffname=@newstuff
其中,stuffId=@stuffId可以尝试在变量声明中设置正确的值:
DECLARE @newstuff nvarchar(64) = 'new stuff'
UPDATE [server2].database2.dbo.Stuff
SET stuffname=@newstuff
WHERE stuffId='4893CD93-08B3-4981-851B-5DC972288290'
我认为删除
select
部分会有所帮助 我相信您的问题与您运行与链接服务器的连接的权限有关
有一些链接解释了这个案例,我也有类似的经历。
以下是几个链接:
我将在下面发布我的解决方案
我已经设置了一个环境来测试您的解决方案。
我的服务器2是sql server 2005
我的服务器1是sql server 2012
在server2上,我以以下方式创建并填充了stuff表:
我使用一个名为TableBackup的数据库,该数据库具有特定的命名约定,但我相信您可以理解:
结果是一个表,在标识字段和另一个更新字段上有一个聚集主键。在我的示例中,这个表有100000条记录
select @@version
--Microsoft SQL Server 2005 - 9.00.5000.00 (Intel X86)
--Dec 10 2010 10:56:29
--Copyright (c) 1988-2005 Microsoft Corporation
--Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2)
use tablebackups
go
CREATE TABLE dbo._MM_201504710_stuff ( Id UNIQUEIDENTIFIER
, stuffname NVARCHAR(64)
)
ALTER TABLE dbo._MM_201504710_stuff ADD CONSTRAINT [PK_Stuff] UNIQUE CLUSTERED ( ID );
-- add 100,000 records to the table so that we can have an idea of execution
SET NOCOUNT ON
insert into dbo._MM_201504710_stuff values (NewID(),'Radhe Radhe')
GO 100000 -- 100,000
SET NOCOUNT OFF
--this took 19:38
--just to test
SELECT TOP 100 * FROM dbo._MM_201504710_stuff
--18D4BDEA-6226-47E1-94DB-00402A29798F
DECLARE @newstuff nvarchar(64)
SELECT @newstuff = 'new stuff'
UPDATE dbo._MM_201504710_stuff
SET stuffname=@newstuff
WHERE Id='18D4BDEA-6226-47E1-94DB-00402A29798F'
UPDATE dbo._MM_201504710_stuff
SET stuffname='new stuff'
WHERE Id='18D4BDEA-6226-47E1-94DB-00402A29798F'
这些更新的执行计划非常相似,不是问题。
如下图所示
在转到server1之前,我会仔细检查stuff表的统计信息是否已更新,因为这将影响查询计划的生成。
只是想确定一下
然后我转到服务器1
否,在转到server1之前,我在server2上拥有此sql登录,并具有以下权限:
我称之为“监视器”
以及“监视器”的权限
我使用此选项选择:
SELECT p.[name], sp.permission_name, p.type_desc AS loginType FROM sys.server_principals p
JOIN sys.server_permissions Sp
ON p.principal_id = sp.grantee_principal_id WHERE sp.class = 100
向我显示以下权限:
现在在server1上,我有一个链接到server2的服务器(sqlsalon1.dev.boden.local)
这个链接的服务器使用“监视器”连接到server2
如上所述,此monitor sql用户拥有查看和更新统计信息所需的所有权限,因此即使在运行远程事务时,我们也可以使用最佳计划
在服务器1上:
我使用以下链接服务器连接到server2:
运行这些脚本(不到一秒)
我得到这个查询计划:
因此,请仔细检查链接服务器帐户的权限,如果您复制我的帐户,我相信您的问题会得到解决,因为这在这里工作,除非有其他不同的情况,在这种情况下,请让我知道,我将尝试进一步解决它
相反
从SQL 2005更新SQL 2012中的表
关于sql 2012
创建并填充表
select @@version
--Microsoft SQL Server 2012 - 11.0.5058.0 (X64)
-- May 14 2014 18:34:29
-- Copyright (c) Microsoft Corporation
-- Standard Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600: ) (Hypervisor)
use tablebackups
go
CREATE TABLE dbo._MM_201504710_stuff ( Id UNIQUEIDENTIFIER
, stuffname NVARCHAR(64)
)
ALTER TABLE dbo._MM_201504710_stuff ADD CONSTRAINT [PK_Stuff] UNIQUE CLUSTERED ( ID );
-- add 100,000 records to the table so that we can have an idea of execution
SET NOCOUNT ON
insert into dbo._MM_201504710_stuff values (NewID(),'Radhe Radhe')
GO 100000 -- 100,000
SET NOCOUNT OFF
--this took 19:38
--just to test
SELECT TOP 100 * FROM dbo._MM_201504710_stuff
--3E29A8E5-BA57-4A9C-803E-003C13A80905
我再检查一遍,这次没问题
创建从sql 2005到sql 2012的链接服务器:
USE [master]
GO
/****** Object: LinkedServer [SQLMON1] Script Date: 13/07/2015 17:09:08 ******/
EXEC master.dbo.sp_addlinkedserver @server = N'SQLMON1', @srvproduct=N'SQL Server'
/* For security reasons the linked server remote logins password is changed with ######## */
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'SQLMON1',@useself=N'False',@locallogin=NULL,@rmtuser=N'monitor',@rmtpassword='########'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'collation compatible', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'data access', @optvalue=N'true'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'dist', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'pub', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'rpc', @optvalue=N'true'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'rpc out', @optvalue=N'true'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'sub', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'connect timeout', @optvalue=N'0'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'collation name', @optvalue=null
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'lazy schema validation', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'query timeout', @optvalue=N'0'
GO
EXEC master.dbo.sp_serveroption @server=N'SQLMON1', @optname=N'use remote collation', @optvalue=N'true'
GO
我已删除下面的服务器选项。
检查目标服务器上“监视器”的权限
SELECT p.[name] collate database_default,
sp.permission_name,
p.type_desc AS loginType
FROM sys.server_principals p
JOIN sys.server_permissions Sp
ON p.principal_id = sp.grantee_principal_id
WHERE sp.class = 100
and name = 'monitor'
然后,我们可以从SQLServer2005运行更新
--first update
UPDATE [SQLMON1].tablebackups.dbo._MM_201504710_stuff
SET stuffname='new stuff'
WHERE Id='3E29A8E5-BA57-4A9C-803E-003C13A80905'
--second update
DECLARE @newstuff nvarchar(64)
SELECT @newstuff = 'new stuff'
UPDATE [SQLMON1].tablebackups.dbo._MM_201504710_stuff
SET stuffname=@newstuff
WHERE Id='3E29A8E5-BA57-4A9C-803E-003C13A80905'
这将更新ro
SELECT p.[name] collate database_default,
sp.permission_name,
p.type_desc AS loginType
FROM sys.server_principals p
JOIN sys.server_permissions Sp
ON p.principal_id = sp.grantee_principal_id
WHERE sp.class = 100
and name = 'monitor'
--first update
UPDATE [SQLMON1].tablebackups.dbo._MM_201504710_stuff
SET stuffname='new stuff'
WHERE Id='3E29A8E5-BA57-4A9C-803E-003C13A80905'
--second update
DECLARE @newstuff nvarchar(64)
SELECT @newstuff = 'new stuff'
UPDATE [SQLMON1].tablebackups.dbo._MM_201504710_stuff
SET stuffname=@newstuff
WHERE Id='3E29A8E5-BA57-4A9C-803E-003C13A80905'