Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 使用变量与文字时,远程查询速度较慢_Sql_Sql Server_Sql Server 2005_Sql Update_Openquery - Fatal编程技术网

Sql 使用变量与文字时,远程查询速度较慢

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=@

我到处都在寻找这种情况,除了动态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=@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'