如何对链接服务器执行存储过程?

How to execute a stored procedure against linked server?

我们当前对链接服务器执行存储过程使用:

EXECUTE [LinkedServer].[DatabaseName].[dbo].[MyProcedure]

例如:

EXECUTE Screwdriver.CRM.dbo.GetCustomer 619

这很好用;通过链接服务器查询工作正常。

禁用已弃用的“远程访问”功能

显然有一个鲜为人知、很少使用的功能,称为远程访问。除了 say here:

之外,Microsoft 对 的功能几乎没有说什么

This configuration option is an obscure SQL Server to SQL Server communication feature that is deprecated, and you probably shouldn't be using it.

Important

This feature will be removed in the next version of Microsoft SQL Server. Do not use this feature in new development work, and modify applications that currently use this feature as soon as possible. Use sp_addlinkedserver instead.

The remote access option only applies to servers that are added by using sp_addserver, and is included for backward compatibility.

来自 SQL Server 2000 联机丛书:

Note Support for remote servers is provided for backward compatibility only. New applications that must execute stored procedures against remote instances of SQL Server should use linked servers instead.

我们只添加了链接的服务器,从未使用过此“远程访问”功能,也从未使用过添加服务器sp_addserver.

我们都很好。对吧?

除了关闭远程访问会破坏一切

一位审计员提到我们应该关闭 远程访问 功能:

应该没问题吧?

微软文档how to turn off this hardly used feature:

Configure the remote access Server Configuration Option


EXEC sp_configure 'remote access', 0 ;  
GO  
RECONFIGURE ;  
GO

除了我们这样做的时候:一切都见鬼去吧:

Msg 7201, Level 17, State 4, Procedure GetCustomer, Line 1

Could not execute procedure on remote server 'Screwdriver' because SQL Server is not configured for remote access. Ask your system administrator to reconfigure SQL Server to allow remote access.

比失败还糟糕? 

为了绝对确定我使用的是链接服务器,我:

EXECUTE sp_dropserver @server='screwdriver', @dropLogins='droplogins'

EXECUTE sp_addlinkedserver N'screwdriver', N'SQL Server'

和re-run我的程序调用:

EXECUTE Screwdriver.CRM.dbo.GetCustomer 619

Msg 7201, Level 17, State 4, Procedure GetCustomer, Line 1
Could not execute procedure on remote server 'Screwdriver' because SQL Server is not configured for remote access. Ask your system administrator to reconfigure SQL Server to allow remote access.

比失败还糟糕!?

我可以 confirm 服务器 链接服务器(而不是 “远程” 服务器)使用:

SELECT SrvName, IsRemote 
FROM master..sysservers
WHERE SrvName = 'Screwdriver'

Srvname      IsRemote
-----------  --------
screwdriver  0

或者使用现代的objects:

SELECT Name, Is_Linked
FROM sys.servers
WHERE Name = 'Screwdriver'

Name         Is_linked
-----------  --------
screwdriver  1

总结

我们现在处于:

为什么不起作用?

这让我想到了我的问题:

推论:

奖金聊天

您使用 sp_configure 调整的 远程访问 配置选项也通过用户界面公开。 SSMS UI 错误地描述了该功能:

不正确的措辞是:

Allow remote connections to this server

应该写成:

Allow remote connections to from this server.

Books Online 也错误地记录了该功能:

Allow remote connections to this server

Controls the execution of stored procedures from remote servers running instances of SQL Server. Selecting this check box has the same effect as setting the sp_configure remote access option to 1. Clearing it prevents execution of stored procedures from a remote server.

应该是:

Allow remote connections to from this server

Controls the execution of stored procedures from to remote servers running instances of SQL Server. Selecting this check box has the same effect as setting the sp_configure remote access option to 1. Clearing it prevents execution of stored procedures from to a remote server.

现在 Microsoft 的年轻人不记得他们从未接触过的 20 年前已弃用的功能是什么,这是有道理的。

BOL 2000 的文档

SQL Server 2000 是最后一次记录此功能。出于后代和调试目的转载于此:

Configuring Remote Servers


A remote server configuration allows a client connected to one instance of Microsoft® SQL Server™ to execute a stored procedure on another instance of SQL Server without establishing another connection. The server to which the client is connected accepts the client request and sends the request to the remote server on behalf of the client. The remote server processes the request and returns any results to the original server, which in turn passes those results to the client.

If you want to set up a server configuration in order to execute stored procedures on another server and do not have existing remote server configurations, use linked servers instead of remote servers. Both stored procedures and distributed queries are allowed against linked servers; however, only stored procedures are allowed against remote servers.

Note Support for remote servers is provided for backward compatibility only. New applications that must execute stored procedures against remote instances of SQL Server should use linked servers instead.

另见

备用标题:禁用 SQL 服务器远程访问会破坏存储过程。

好吧,无论是否记录在案,只要您是对的,您就是对的。将 remote access 设置为 0(并重新启动)会导致使用四部分语法的远程存储过程调用失败,即使所有文档都表明链接服务器不应失败。即使在 SQL Server 2017 (RTM CU12) 的最新版本中也是如此,因此这不是特定于版本的。目前尚不清楚这是否是 真正的 限制,或者代码是否只是错误并根据 remote access 功能检查阻止它,即使它在技术上是可行的。

涉及四部分名称 (SELECT * FROM server.db.scheme.table) 的查询不会失败,大概是因为这仅适用于链接服务器,并且从一开始就不涉及远程访问。

作为解决方法,您可以将调用更改为使用 EXECUTE .. AT:

EXEC ('EXECUTE CRM.dbo.GetCustomer 619') AT Screwdriver

只要链接服务器启用了 RPC Out 选项(如果由 sp_addlinkedserver 添加且没有特殊选项,则默认情况下会启用)。

不幸的是,EXECUTE .. AT在涉及参数时就不太方便了,因为它只支持?语法:

EXEC ('EXECUTE CRM.dbo.GetCustomer @Customer=?', 619) AT Screwdriver

这里的参数名称是可选的,但我强烈建议使用它来保持可预测性。与 EXECUTE 相同——它是可选的,但它清楚地表明我们实际上是 运行 一个任意查询,而不仅仅是调用存储过程。

如果你的程序有OUTPUT个参数,这个plain将不起作用; EXECUTE .. AT 不够聪明。您可以在调用中指定 OUTPUT,但该值不会被复制回来。解决方法超出了这个答案的范围,但它们不会很漂亮。

可能值得为此打开一个 suggestion,因为它看起来确实应该像记录的那样工作(如果 Microsoft 想要永久摆脱 remote access ,他们无论如何都需要)。

我在2016和2019版本的SQL服务器上测试过,运行良好。您可以从配置中禁用远程访问,您将能够在链接服务器上执行该过程。您只需要在链接服务器的参数中启用 RPC out 选项。

Microsoft 文档 article 从今天起已经改写(完全披露:我改写了它)。该功能仍被弃用,因此我们的指导是您应该保留它。

现在说(强调):

This article is about the remote access configuration option, which is a deprecated SQL Server to SQL Server communication feature.

This option affects servers that are added by using sp_addserver and sp_addlinkedserver. You should leave remote access enabled (the default) if you use linked servers.