前几天给开发部门部署测试数据库时,遇到一个很诡异的问题:创建一个链接服务器GEK-MIS01时,报错如下:
消息 15190,级别 16,状态 1,过程 sp_dropserver,第 56 行
仍有对服务器 'GEK-MIS01' 的远程登录或链接登录。
脚本如下(略去登录名等关键信息):
/****** Object: LinkedServer [GEK-MIS01] Script Date: 07/05/2013 16:19:58 ******/ IF EXISTS (SELECT srv.name FROM sys.servers srv WHERE srv.server_id != 0 AND srv.name = N'GEK-MIS01')EXEC master.dbo.sp_dropserver @server=N'GEK-MIS01', @droplogins='droplogins' GO /****** Object: LinkedServer [GEK-MIS01] Script Date: 07/05/2013 16:19:58 ******/ EXEC master.dbo.sp_addlinkedserver @server = N'GEK-MIS01', @srvproduct=N'SQL Server' /* For security reasons the linked server remote logins password is changed with ######## */ EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'GEK-MIS01',@useself=N'False',@locallogin=NULL,@rmtuser=N'username',@rmtpassword='*****' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'collation compatible', @optvalue=N'false' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'data access', @optvalue=N'true' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'dist', @optvalue=N'false' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'pub', @optvalue=N'false' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'rpc', @optvalue=N'true' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'rpc out', @optvalue=N'true' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'sub', @optvalue=N'false' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'connect timeout', @optvalue=N'0' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'collation name', @optvalue=null GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'lazy schema validation', @optvalue=N'false' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'query timeout', @optvalue=N'0' GO EXEC master.dbo.sp_serveroption @server=N'GEK-MIS01', @optname=N'use remote collation', @optvalue=N'false' GO
因为当时是一批脚本执行而且仅有这个脚本出错,当我准备查检查出错原因的时候,又有更紧急的事情要处理,就耽搁了处理这个问题,开发那边在测试过程发现这个数据库链接有问题,邮件反馈给我,我检查时居然发现很多不可思议的现象:
(1): 我用SSMS进入“服务器对象”——“链接服务器”下,居然看不到这个链接服务器对象,而通过sysservers等系统表又能查到这个服务器链接对象的信息,当时我就百思不得其解,纳尼啊?
(2) 重新执行上面脚本时发现删除链接服务器那段脚本根本删除不了该链接服务器,而添加链接服务器时则报错
IF EXISTS (SELECT srv.name FROM sys.servers srv WHERE srv.server_id != 0 AND srv.name = N'GEK-MIS01')
EXEC master.dbo.sp_dropserver @server=N'GEK-MIS01', @droplogins='droplogins'
GO
EXEC master.dbo.sp_addlinkedserver @server = N'GEK-MIS01', @srvproduct=N'SQL Server'
消息 15028,级别 16,状态 1,过程 sp_addlinkedserver,第 82 行
服务器 'GEK-MIS01' 已存在。
(3):接下来我测试了下链接服务器的使用情况,发现有些登录名(Windows 身份认证,sysadmin角色)下使用该数据库链接没有问题,而SQL Server身份验证的登录名则会报下面错误:
EXEC [GEK-MIS01].DatabaseName.dbo.Procedure 'xxxx'
消息 916,级别 14,状态 1,第 1 行
服务器主体 "username" 无法在当前安全上下文下访问数据库 "xxxxx"。
(4):而在SSMS下,在"服务器对象"—>“链接服务器”—> "GEK-MIS01"下单击目录时,报如下错误。
具体错误信息如下:
标题: Microsoft SQL Server Management Studio ------------------------------ 无法为该请求检索数据。 (Microsoft.SqlServer.Management.Sdk.Sfc) 有关帮助信息,请单击: http://go.microsoft.com/fwlink?ProdName=Microsoft+SQL+Server&LinkId=20476 ------------------------------ 其他信息: 执行 Transact-SQL 语句或批处理时发生了异常。 (Microsoft.SqlServer.ConnectionInfo) ------------------------------ 拒绝了对对象 'xp_prop_oledb_provider' (数据库 'mssqlsystemresource',架构 'sys')的 EXECUTE 权限。 (Microsoft SQL Server,错误: 229) 有关帮助信息,请单击: http://go.microsoft.com/fwlink?ProdName=Microsoft+SQL+Server&ProdVer=10.50.4000&EvtSrc=MSSQLServer&EvtID=229&LinkId=20476 ------------------------------ 按钮: 确定 ------------------------------
解决办法: 查看当前USER为guest, 执行下面赋权语句即可解决问题
SELECT CURRENT_USER;
GRANT EXECUTE ON SYS.XP_PROP_OLEDB_PROVIDER TO guest;
但是对于现象1,2,3则困扰了我好久,一直以为是权限问题,最后经过吐血的漫长排查,终于发现一个让人吐血的原因:这台测试数据库服务器原先的计算机名称为gek-mis01,后来不知道啥原因被系统管理员用作测试服务器(历史原因了我就不多说了),改名为GETTESTNT15,而一台新的服务器命名为gek-mis01,结果我今天新建链接服务器时,就踩到了这个地雷。
接下来解决起来就比较顺利了,首先删除该链接服务器,修复计算机改名问题,重新新建该链接数据库,OK,问题解决了!
- exec sp_droplinkedsrvlogin'GEK-MIS01' ,'xxxxx'
- exec sp_dropserver'GEK-MIS01'
- USE master;
- GO
- IF SERVERPROPERTY('servername') <> @@SERVERNAME
- BEGIN
- DECLARE @server sysname;
- SET @server =@@SERVERNAME;
- EXEC sp_dropserver@server= @server;
- SET @server = CAST(SERVERPROPERTY('servername') AS sysname);
- EXEC sp_addserver@server=@server, @local='LOCAL';
- END