在以下问题上需要一些帮助:
案例1 :存储过程在服务器1上 - 来自server1的调用
declare @tempCountry table (countryname char(50))
insert into @tempCountry
exec [database1_server1].[dbo].[getcountrylist]
Select * from @tempCountry
结果:成功执行
Case2 :i如果使用链接服务器从不同的服务器调用相同的存储过程,请执行以下操作:
declare @tempCountry table (countryname char(50))
insert into @tempCountry
exec [database2_server2].[database1_server1].[dbo].[getcountrylist]
Select * from @tempCountry
结果
消息7391,级别16,状态2,行2 无法执行操作,因为OLEDB提供程序" SQLNCLI" for linkedserver" Server2_Database2"无法开始分布式交易。
案例3
但是当试图单独执行存储过程[没有临时表插入]时,如下所示
exec [database2_server2].[database1_server1].[dbo].[getcountrylist]
结果:执行存储过程没有任何错误并返回数据。
我忘了提到我正在使用SQL Server 2005.根据服务器管理员的说法,您使用的功能在2005年没有提供。
答案 0 :(得分:12)
你有(我相信)两个选项:
使用these行集功能
尝试避免使用MSDTC
(以及与分布式事务相关的所有OPENQUERY不愉快的事情)
/ 假设(此处及以下)[database2_server2]是链接服务器的名称 /
declare @tempCountry table (countryname char(50))
insert into @tempCountry
select * from openquery([database2_server2], '[database1_server1].[dbo].[getcountrylist]')
select * from @tempCountry
或强>
您可以将链接服务器的选项Enable Promotion Of Distributed Transaction
设置为False
,以防止本地事务促进分布式事务,从而使用MSDTC:
EXEC master.dbo.sp_serveroption
@server = N'database2_server2',
@optname = N'remote proc transaction promotion',
@optvalue = N'false'
并且您的原始查询应该可以正常工作:
declare @tempCountry table (countryname char(50))
insert into @tempCountry
exec [database2_server2].[database1_server1].[dbo].[getcountrylist]
select * from @tempCountry
答案 1 :(得分:0)
可以完全避免链接服务器。您可以创建一个SQLCLR存储过程,该过程与远程实例(即Database1)建立标准连接。
以下C#代码适用于SQLCLR存储过程:
允许使用可选的数据库名称。如果为空,则当前数据库将是默认数据库,或者如果提供,它将在连接后更改为该数据库(以便当前数据库可以与默认数据库不同)
允许选择使用模拟。没有模拟(默认行为),连接是由运行SQL Server服务的Windows登录进行的(即"登录为"帐户在"服务")。这可能不是所希望的,因为它通常提供比调用者通常具有的更高级别的权限。如果Login与Windows登录相关联,则使用Impersonation将维护Login执行存储过程的安全上下文。 SQL Server登录没有安全上下文,因此在尝试使用模拟时会出错。
在此处提供的代码中打开和关闭模拟的功能仅用于测试目的,因此更容易看到使用模拟和不使用模拟之间的差异。在实际项目中使用此代码时,通常没有理由允许最终用户(即调用者)更改设置。使用模拟通常更安全。但是,使用模拟的主要困难在于它仅限于本地计算机,除非在Active Directory中为委派启用了Windows登录。
Server1
中调用Server2
:Database2
的实例上创建
需要PERMISSION_SET
EXTERNAL_ACCESS
。最好通过以下方式处理:
[master]
中,从DLL创建一个非对称密钥[master]
中,使用此新的非对称密钥创建登录EXTERNAL ACCESS ASSEMBLY
权限[Database2]
中,执行以下操作:ALTER ASSEMBLY [NoLinkedServer] WITH PERMISSION_SET = EXTERNAL_ACCESS;
应按以下方式执行:
EXEC dbo.RemoteExec N'Server1', N'Database1', 0;
和:
EXEC dbo.RemoteExec N'Server1', N'Database1', 1;
每次执行后,运行以下内容并注意前两个字段:
SELECT [login_name], [original_login_name], *
FROM sys.dm_exec_sessions
WHERE LEFT([program_name], 14) = N'Linked Server?';
C#代码:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Security.Principal;
using Microsoft.SqlServer.Server;
public class LinkedServersSuck
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void RemoteExec(
[SqlFacet(MaxSize = 128)] SqlString RemoteInstance,
[SqlFacet(MaxSize = 128)] SqlString RemoteDatabase,
SqlBoolean UseImpersonation)
{
if (RemoteInstance.IsNull)
{
return;
}
SqlConnectionStringBuilder _ConnectionString =
new SqlConnectionStringBuilder();
_ConnectionString.DataSource = RemoteInstance.Value;
_ConnectionString.Enlist = false;
_ConnectionString.IntegratedSecurity = true;
_ConnectionString.ApplicationName =
"Linked Server? We don't need no stinkin' Linked Server!";
SqlConnection _Connection =
new SqlConnection(_ConnectionString.ConnectionString);
SqlCommand _Command = new SqlCommand();
_Command.CommandType = CommandType.StoredProcedure;
_Command.Connection = _Connection;
_Command.CommandText = @"[dbo].[getcountrylist]";
SqlDataReader _Reader = null;
WindowsImpersonationContext _SecurityContext = null;
try
{
if (UseImpersonation.IsTrue)
{
_SecurityContext = SqlContext.WindowsIdentity.Impersonate();
}
_Connection.Open();
if (_SecurityContext != null)
{
_SecurityContext.Undo();
}
if (!RemoteDatabase.IsNull && RemoteDatabase.Value != String.Empty)
{
// do this here rather than in the Connection String
// to reduce Connection Pool Fragmentation
_Connection.ChangeDatabase(RemoteDatabase.Value);
}
_Reader = _Command.ExecuteReader();
SqlContext.Pipe.Send(_Reader);
}
catch
{
throw;
}
finally
{
if (_Reader != null && !_Reader.IsClosed)
{
_Reader.Close();
}
if (_Connection != null && _Connection.State != ConnectionState.Closed)
{
_Connection.Close();
}
}
return;
}
}