我有一大堆预先存在的sql select语句。
从[Server_A]上的存储过程,我想在多个不同的SQL Server&上执行这些语句。数据库(列表存储在[Server_A]上的本地表中,并将结果返回到[Server_A]上的表中。
但是,我不想在我的sql语句中使用完全限定的表名。我想执行" select * from users",not" select * from ServerName.DatabaseName.SchemaName.Users"
我已经使用Openrowset进行了调查,但是我找不到任何可以将服务器名称和DatabaseName都指定为作为连接属性的示例,而不是实际嵌入到实际的SQL语句。
Openrowset能胜任吗?是否有另一种方法(从存储过程中,而不是诉诸Powershell或其他一些非常不同的方法?)
不可避免的"为什么我要这样做?"
答案 0 :(得分:1)
这可以通过SQLCLR轻松完成。如果结果集是动态的,那么它需要是存储过程而不是TVF。
假设您正在执行存储过程,您只需:
@ServerName, @DatabaseName, @SQL
SqlConnection
,其连接字符串为:String.Concat("Server=", ServerName.Value, "; Database=", DatabaseName.Value, "; Trusted_Connection=yes; Enlist=false;")
或使用ConnectionStringBuilder
SqlCommand
创建SqlConnection
并使用SQL.Value
。SqlContext.WindowsIdentity.Impersonate();
_Connection.Open();
_Reader = Command.ExecuteReader();
SqlContext.Pipe.Send(_Reader);
finally
子句中处理Reader,Command,Connection和ImpersonationContext 与支持Ad Hoc Distributed Query访问相比,这种方法不是一个安全问题,因为它更加绝缘和可控。它还不允许SQL Server登录获得提升权限,因为当代码执行Impersonate()
方法时,SQL Server登录将收到错误。
此外,此方法允许返回多个结果集,OPENROWSET不允许的内容:
虽然查询可能返回多个结果集,但OPENROWSET只返回第一个结果集。
<强>更新强>
基于对此答案的评论修改的伪代码:
@QueryID
SqlConnection
(_MetaDataConnection),其连接字符串为:Context Connection = true;
ServerName
DatabaseName
查询_MetaDataConnection以获取Query
,QueryID.Value
和SqlDataReader
SqlConnection
(_QueryConnection),其连接字符串为:String.Concat("Server=", _Reader["ServerName"].Value, "; Database=", _Reader["DatabaseName"].Value, "; Trusted_Connection=yes; Enlist=false;")
或使用ConnectionStringBuilder
SqlCommand
为_QueryConnection创建_Reader["SQL"].Value
(_ QueryCommand)。QueryID.Value
SqlDataReader
以创建SqlParameter
并添加到_QueryCommand _MetaDataConnection.Close();
SqlContext.WindowsIdentity.Impersonate();
_QueryConnection.Open();
_Reader = _QueryCommand.ExecuteReader();
SqlContext.Pipe.Send(_Reader);
finally
子句中处理读者,命令,连接和ImpersonationContext 答案 1 :(得分:0)
如果要在实例中的每个数据库上执行sql语句,可以使用(不支持的,非官方的,但广泛使用的)exec sp_MSforeachdb
,如下所示:
EXEC sp_Msforeachdb 'use [?]; select * from users'
这相当于通过
遍历每个数据库use db...
go
select * from users
答案 2 :(得分:0)
这是一个有趣的问题,因为我搜索了很多很多小时,并且发现有几个人试图做与问题中提到的完全相同的事情。
最常见的回复:
无论如何,这是你如何做到的:
如果您使用的是静态SQL:
select * from OPENROWSET('SQLNCLI','Server=ServerName[\InstanceName];Database=AdventureWorks2012;Trusted_Connection=yes','select top 10 * from HumanResources.Department')
如果您正在使用动态SQL - 因为OPENROWSET不接受变量作为参数,您可以使用这样的方法(就像一个人为的例子):
declare @sql nvarchar(4000) = N'select * from OPENROWSET(''SQLNCLI'',''Server=Server=ServerName[\InstanceName];Database=AdventureWorks2012;Trusted_Connection=yes'',''@zzz'')'
set @sql = replace(@sql,'@zzz','select top 10 * from HumanResources.Department')
EXEC sp_executesql @sql
值得注意的是:如果你认为将这个语法包装在一个接受@ ServerName,@ DatabaseName,@ SQL的很好的Table Valued函数中会很好,你就不能,因为TVF的结果集列必须在编译时确定。
相关阅读:
<强>结论:强>
OPENROWSET是唯一可以100%避免至少一些对象名称完全限定的方法;即使使用EXEC AT,您仍然必须使用数据库名称为对象添加前缀。
额外提示:流行的观点似乎是不应该使用OPENROWSET“因为它存在安全风险”(没有任何风险细节)。我的理解是风险仅在您使用SQL Server身份验证时,更多详细信息如下:
https://technet.microsoft.com/en-us/library/ms187873%28v=sql.90%29.aspx?f=255&MSPPError=-2147217396
连接到其他数据源时,SQL Server会为Windows身份验证登录正确模拟登录;但是,SQL Server无法模拟SQL Server身份验证登录。因此,对于SQL Server经过身份验证的登录,SQL Server可以使用运行SQL Server服务的Windows帐户的安全上下文来访问其他数据源,如文件,非关系数据源(如Active Directory)。这样做可能会使这些登录访问他们没有权限的另一个数据源,但运行SQL Server服务的帐户确实具有权限。使用SQL Server身份验证登录时,应考虑这种可能性。