必须先执行sp_addlinkedserver注册服务器并用sp_addlinkedsrvlogin配置登录映射,否则所有远程查询均报msg 7202;二者缺一不可,顺序不可颠倒,且需启用rpc out才能执行远程存储过程。

必须先建 Linked Server,否则所有远程调用都会直接报错 Msg 7202 ——SQL Server 根本不认识那个服务器名。
建 Linked Server 和登录映射是前提,缺一不可
没执行 sp_addlinkedserver 和 sp_addlinkedsrvlogin,任何带参数的远程查询(哪怕只是 SELECT)都会失败。这不是语法或权限问题,是元数据层面缺失:SQL Server 在 sys.servers 里查不到目标服务器。
常见错误现象包括:
-
SELECT * FROM RemoteSrv.db.schema.table WHERE id = @p1报Could not find server 'RemoteSrv' -
EXEC RemoteSrv.db.dbo.usp_GetData @param = 'x'报Login failed for user ''或Object does not exist
两步必须都做:
- 用
sp_addlinkedserver注册逻辑名和数据源,@provider推荐'MSOLEDBSQL'(SQL Server 2017+ 官方支持),别用已弃用的'SQLNCLI'或旧版'SQLOLEDB' - 用
sp_addlinkedsrvlogin显式绑定凭据;若用 Windows 身份映射(@useself = 'true'),需确保本地 SQL Server 服务账户在远程端有对应登录且有足够权限;更稳的做法是传@rmtuser和@rmtpassword的 SQL 账户
四部分命名法不能直接传参,WHERE 条件也不下推
建好 Linked Server 后,[RemoteSrv].[db].[schema].[table] 这种写法只能用于静态查询,不能把存储过程参数直接塞进 WHERE 里当变量用——SQL Server 会先拉全表元数据,再本地过滤,大表极易超时或内存溢出。
例如这段代码看似合理,实则危险:
DECLARE @id INT = 123; SELECT * FROM RemoteSrv.AdventureWorks2019.HumanResources.Employee WHERE BusinessEntityID = @id;
它不会把 WHERE 下推到远程执行,而是尝试拉整个 Employee 表到本地再过滤。
真正安全的做法只有两个:
- 改用
OPENQUERY,把完整 SQL 字符串发过去执行(注意 SQL 注入风险):SELECT * FROM OPENQUERY(RemoteSrv, 'SELECT * FROM db.schema.table WHERE id = 123') - 或者用
EXEC AT+ 动态拼接(仅限远程也是 SQL Server):EXEC ('SELECT * FROM db.schema.table WHERE id = ?', @id) AT RemoteSrv
远程执行存储过程必须开 rpc out,rpc 选项无效
即使 Linked Server 和登录都配好了,EXEC RemoteSrv.db.dbo.usp_GetData @p = 'x' 仍会报 Ad hoc access to OLE DB provider has been denied 或静默失败——因为默认关闭了远程过程调用出口。
必须显式启用:
EXEC sp_serveroption 'RemoteSrv', 'rpc out', 'true';
注意:'rpc' 选项(不带 out)只影响“被远程调用”,对“主动调用远程”完全没用,这一步几乎 100% 被忽略,尤其从 SSMS 图形界面创建的 Linked Server 默认不开启。
另外要注意:
- 远程存储过程的参数类型要严格匹配,比如远程端是
varchar(50),本地传nvarchar可能触发隐式转换失败 - 输出参数(
@out_param OUTPUT)可以正常返回,但必须在EXEC语句中显式声明OUTPUT关键字 - 如果远程过程含事务控制(
BEGIN TRAN),本地无法参与其事务,只能当作黑盒调用
异构数据库(Oracle/MySQL)要用双引号、注意大小写和类型映射
远程不是 SQL Server 时,四部分命名基本不可靠。比如连 Oracle,字段名默认全大写,且必须用双引号包裹:
SELECT * FROM RemoteOra."NORTHSNOW"."SYS_USER";
否则报 Msg 7357 或空结果集。原因在于:
- Oracle 对象名默认大写,而 SQL Server 发送的元数据请求按小写或混合大小写处理,导致找不到对象
- MySQL 的
lower_case_table_names设置不同,也可能导致表名解析失败 - 日期、LOB、XML 等类型在跨库映射时容易丢失精度或转成
NULL
这种场景下,OPENQUERY 是唯一稳定选择,因为它把整条 SQL 交由远程引擎原生解析执行,绕过 SQL Server 的元数据协商环节。
最常被忽略的其实是登录映射的粒度问题:一个 Linked Server 只能绑定一套凭据,如果远程库需要按角色切换用户(比如只读账号查表、管理员账号跑过程),就得建多个 Linked Server 别名,分别映射不同账号——而不是指望一个链接复用所有权限场景。











