如何向存储过程(SQL 服务器)调用的存储过程提供参数值?
How can I provide parameter values to a Stored Proc called by a Stored Proc (SQL Server)?
根据答案 here,我已经开始了一个测试 SP,我计划最终构建它来多次调用,具有更多的临时表和 [=40= 的不同值] 范围。不过,@BegDate、@EndDate 和@SortBy 参数将始终相同 - 由用户提供。
这是我目前所拥有的:
IF OBJECT_ID ( 'testSP', 'P' ) IS NOT NULL
DROP PROCEDURE testSP;
GO
CREATE PROC [dbo].[testSP]
@BegDate datetime,
@EndDate datetime,
@SortBy varchar(20)
AS
BEGIN
SELECT * FROM sys.databases
END
GO
sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
SELECT * INTO #MyTempTable FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
'EXEC SP2BCalled @Unit = "FirstUnit"; @BegDate = @BegDate; @EndDate = @EndDate; @SortBy = @SortBy; ')
SELECT * FROM #MyTempTable
我的问题:这是将参数传递给现有存储过程 (SP2BCalled) 的正确方法吗:
'EXEC SP2BCalled @Unit = "Abuelos"; @BegDate = @BegDate; @EndDate = @EndDate; @SortBy = @SortBy; ')
IOW,被调用的 SP 需要将@BegDate、@EndDate 和@SortBy 参数传递给它,因此新的 SP 会收集这些参数并继续传递,但是这样做的正确语法是什么?
我应该这样做吗:
CREATE PROC [dbo].[testSP]
@BegDateLocal datetime,
@EndDateLocal datetime,
@SortByLocal varchar(20)
...然后像这样调用现有的 SP:
SELECT * INTO #MyTempTable1 FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
'EXEC SP2BCalled @Unit = "FirstUnit"; @BegDate = @BegDateLocal; @EndDate = @EndDateLocal; @SortBy = @SortByLocal; ')
SELECT * INTO #MyTempTable2 FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
'EXEC SP2BCalled @Unit = "SecondUnit"; @BegDate = @BegDateLocal; @EndDate = @EndDateLocal; @SortBy = @SortByLocal; ')
SELECT * INTO #MyTempTable3 FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
'EXEC SP2BCalled @Unit = "ThirdUnit"; @BegDate = @BegDateLocal; @EndDate = @EndDateLocal; @SortBy = @SortByLocal; ')
...?,或者如何?
然后我需要 UNION 所有临时表(#MyTempTable1、#MyTempTable2 和 #MyTempTable3),return 作为最终结果。
更新
海因里希·斯密特是对的;当我尝试 运行 testSP 时,我得到:
配置选项 'show advanced options' 从 0 更改为 1。运行 要安装的 RECONFIGURE 语句。
配置选项 'Ad Hoc Distributed Queries' 从 0 更改为 1。 运行 要安装的 RECONFIGURE 语句。
链接服务器“(null)”returned 消息 "Login timeout expired" 的 OLE DB 提供程序 "SQLNCLI11"。
链接服务器“(null)”的 OLE DB 提供程序 "SQLNCLI11" returned 消息 "A network-related or instance-specific error has occurred while establishing a connection to SQL Server. Server is not found or not accessible. Check if instance name is correct and if SQL Server is configured to allow remote connections. For more information see SQL Server Books Online.".
局部参数与调用过程的参数是否重名无关紧要。所以你问的这两种方式没有区别。
重要的是你不应该用分号分隔参数。你应该使用逗号。
'EXEC SP2BCalled @Unit = ''FirstUnit'', @BegDate = @BegDateLocal, @EndDate = @EndDateLocal, @SortBy = @SortByLocal, ')
据我所知,您将无法直接将参数传递给 OPENROWSET
调用。您需要创建一个动态 SQL 字符串并执行它,例如
DECLARE @SQL VARCHAR(MAX)
SET @SQL = 'SELECT * FROM OPENROWSET(''SQLNCLI'', ''DRIVER={SQL Server}; Server=127.0.0.1;'',''EXEC [Test].[dbo].[Test] @var1 = ' + @var1 + ''')'
EXEC(@SQL)
此外,根据提供的代码,
SELECT * INTO #MyTempTable FROM OPENROWSET(..
part 永远不会 运行 作为存储过程的一部分,因为有多个 GO 结束定义。
您也没有指定要在 OPENROWSET
中使用的数据库。
使用 OPENROWSET
而不是使用链接服务器的 FQDN [server].[database].[schema].[table]
是否有特定原因?
如果您知道返回的数据集的结构是什么,那么您可以使用如下内容:
-- Making assumptions about the data set
CREATE TABLE #MyTempTable1
(
Col1 int not null
,Col2 datetime not null
,ColEtc nvarchar(max) not null
)
INSERT #MyTempTable1 (Col1, Col2, ColEtc)
EXECUTE SP2BCalled
@Unit = 'Abuelos'
,@BegDate = @BegDate
,@EndDate = @EndDate
,@SortBy = @SortBy
OPENROWSET
只接受文字字符串。要使用它,您需要在动态 SQL 中构建整个命令,然后 exec
该命令。
在 exec
中创建的 Temp table 仅存在于 exec
的范围内 - 它们在 returns 时消失。
您可以使用全局临时文件 tables ##MyTempTable1 等,但是如果多个用户同时调用它,他们将全部写入同一个全局临时文件 table。
NEWID 技巧创建了一个独特的全局温度 table 来避免这种情况,但更难管理。
使用双引号 "
仅当 SET QUOTED_IDENTIFIER
在远程服务器上关闭时才有效。如果它是 ON,那么双引号中的任何内容都不会被视为字符串。
相反,它被视为一个对象名称,如列或 table。
为避免担心这一点,请使用一对单引号 ''
来表示字符串中的单引号。但是,以这种方式处理嵌套引号确实令人难以置信。
您只需将远程服务器配置为接受一次临时分布式查询,这样您就可以预先进行,而不是每次调用时都进行。
Whosebug
是保存远程存储过程的数据库的名称。只需替换为您的正确名称即可。
Declare @Unit varchar(max) = 'FirstUnit', @BegDateLocal datetime = '01-jun-2016', @EndDateLocal datetime = '30-jun-2016', @SortBy varchar(max) = 'X'
Declare @sql nvarchar(max)
Declare @exec nvarchar(max) = 'EXEC Whosebug.dbo.SP2BCalled @Unit = ''''FirstUnit'''', @BegDate = ''''' + convert(varchar(max), @BegDateLocal, 120) + ''''''
+ ', @EndDate = ''''' + convert(varchar(max), @EndDateLocal, 120) + ''''''
+ ', @SortBy = ''''' + @SortBy + ''''''
Declare @GlobalTemp varchar(max) = '##MyTempTable1'
Declare @GlobalTempNEWID varchar(max) = '[##' + cast(newid() as char(36)) + ']'
Declare @DropCheck varchar(max) = 'IF object_id(''tempdb..' + @GlobalTemp + ''') is not null drop table ' + @GlobalTemp
Set @sql = @DropCheck + '; SELECT * INTO ' + @GlobalTemp
+ 'FROM OPENROWSET(
''SQLNCLI'',
''Server=(local)\SQL2008;Trusted_Connection=yes;'','
+ '''' + @exec + ''')'
Print @sql
exec (@sql)
SELECT * FROM ##MyTempTable1
根据答案 here,我已经开始了一个测试 SP,我计划最终构建它来多次调用,具有更多的临时表和 [=40= 的不同值] 范围。不过,@BegDate、@EndDate 和@SortBy 参数将始终相同 - 由用户提供。
这是我目前所拥有的:
IF OBJECT_ID ( 'testSP', 'P' ) IS NOT NULL
DROP PROCEDURE testSP;
GO
CREATE PROC [dbo].[testSP]
@BegDate datetime,
@EndDate datetime,
@SortBy varchar(20)
AS
BEGIN
SELECT * FROM sys.databases
END
GO
sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
SELECT * INTO #MyTempTable FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
'EXEC SP2BCalled @Unit = "FirstUnit"; @BegDate = @BegDate; @EndDate = @EndDate; @SortBy = @SortBy; ')
SELECT * FROM #MyTempTable
我的问题:这是将参数传递给现有存储过程 (SP2BCalled) 的正确方法吗:
'EXEC SP2BCalled @Unit = "Abuelos"; @BegDate = @BegDate; @EndDate = @EndDate; @SortBy = @SortBy; ')
IOW,被调用的 SP 需要将@BegDate、@EndDate 和@SortBy 参数传递给它,因此新的 SP 会收集这些参数并继续传递,但是这样做的正确语法是什么?
我应该这样做吗:
CREATE PROC [dbo].[testSP]
@BegDateLocal datetime,
@EndDateLocal datetime,
@SortByLocal varchar(20)
...然后像这样调用现有的 SP:
SELECT * INTO #MyTempTable1 FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
'EXEC SP2BCalled @Unit = "FirstUnit"; @BegDate = @BegDateLocal; @EndDate = @EndDateLocal; @SortBy = @SortByLocal; ')
SELECT * INTO #MyTempTable2 FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
'EXEC SP2BCalled @Unit = "SecondUnit"; @BegDate = @BegDateLocal; @EndDate = @EndDateLocal; @SortBy = @SortByLocal; ')
SELECT * INTO #MyTempTable3 FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
'EXEC SP2BCalled @Unit = "ThirdUnit"; @BegDate = @BegDateLocal; @EndDate = @EndDateLocal; @SortBy = @SortByLocal; ')
...?,或者如何?
然后我需要 UNION 所有临时表(#MyTempTable1、#MyTempTable2 和 #MyTempTable3),return 作为最终结果。
更新
海因里希·斯密特是对的;当我尝试 运行 testSP 时,我得到:
配置选项 'show advanced options' 从 0 更改为 1。运行 要安装的 RECONFIGURE 语句。 配置选项 'Ad Hoc Distributed Queries' 从 0 更改为 1。 运行 要安装的 RECONFIGURE 语句。 链接服务器“(null)”returned 消息 "Login timeout expired" 的 OLE DB 提供程序 "SQLNCLI11"。 链接服务器“(null)”的 OLE DB 提供程序 "SQLNCLI11" returned 消息 "A network-related or instance-specific error has occurred while establishing a connection to SQL Server. Server is not found or not accessible. Check if instance name is correct and if SQL Server is configured to allow remote connections. For more information see SQL Server Books Online.".
局部参数与调用过程的参数是否重名无关紧要。所以你问的这两种方式没有区别。
重要的是你不应该用分号分隔参数。你应该使用逗号。
'EXEC SP2BCalled @Unit = ''FirstUnit'', @BegDate = @BegDateLocal, @EndDate = @EndDateLocal, @SortBy = @SortByLocal, ')
据我所知,您将无法直接将参数传递给 OPENROWSET
调用。您需要创建一个动态 SQL 字符串并执行它,例如
DECLARE @SQL VARCHAR(MAX)
SET @SQL = 'SELECT * FROM OPENROWSET(''SQLNCLI'', ''DRIVER={SQL Server}; Server=127.0.0.1;'',''EXEC [Test].[dbo].[Test] @var1 = ' + @var1 + ''')'
EXEC(@SQL)
此外,根据提供的代码,
SELECT * INTO #MyTempTable FROM OPENROWSET(..
part 永远不会 运行 作为存储过程的一部分,因为有多个 GO 结束定义。
您也没有指定要在 OPENROWSET
中使用的数据库。
使用 OPENROWSET
而不是使用链接服务器的 FQDN [server].[database].[schema].[table]
是否有特定原因?
如果您知道返回的数据集的结构是什么,那么您可以使用如下内容:
-- Making assumptions about the data set
CREATE TABLE #MyTempTable1
(
Col1 int not null
,Col2 datetime not null
,ColEtc nvarchar(max) not null
)
INSERT #MyTempTable1 (Col1, Col2, ColEtc)
EXECUTE SP2BCalled
@Unit = 'Abuelos'
,@BegDate = @BegDate
,@EndDate = @EndDate
,@SortBy = @SortBy
OPENROWSET
只接受文字字符串。要使用它,您需要在动态 SQL 中构建整个命令,然后 exec
该命令。
exec
中创建的 Temp table 仅存在于 exec
的范围内 - 它们在 returns 时消失。
您可以使用全局临时文件 tables ##MyTempTable1 等,但是如果多个用户同时调用它,他们将全部写入同一个全局临时文件 table。
NEWID 技巧创建了一个独特的全局温度 table 来避免这种情况,但更难管理。
使用双引号 "
仅当 SET QUOTED_IDENTIFIER
在远程服务器上关闭时才有效。如果它是 ON,那么双引号中的任何内容都不会被视为字符串。
相反,它被视为一个对象名称,如列或 table。
为避免担心这一点,请使用一对单引号 ''
来表示字符串中的单引号。但是,以这种方式处理嵌套引号确实令人难以置信。
您只需将远程服务器配置为接受一次临时分布式查询,这样您就可以预先进行,而不是每次调用时都进行。
Whosebug
是保存远程存储过程的数据库的名称。只需替换为您的正确名称即可。
Declare @Unit varchar(max) = 'FirstUnit', @BegDateLocal datetime = '01-jun-2016', @EndDateLocal datetime = '30-jun-2016', @SortBy varchar(max) = 'X'
Declare @sql nvarchar(max)
Declare @exec nvarchar(max) = 'EXEC Whosebug.dbo.SP2BCalled @Unit = ''''FirstUnit'''', @BegDate = ''''' + convert(varchar(max), @BegDateLocal, 120) + ''''''
+ ', @EndDate = ''''' + convert(varchar(max), @EndDateLocal, 120) + ''''''
+ ', @SortBy = ''''' + @SortBy + ''''''
Declare @GlobalTemp varchar(max) = '##MyTempTable1'
Declare @GlobalTempNEWID varchar(max) = '[##' + cast(newid() as char(36)) + ']'
Declare @DropCheck varchar(max) = 'IF object_id(''tempdb..' + @GlobalTemp + ''') is not null drop table ' + @GlobalTemp
Set @sql = @DropCheck + '; SELECT * INTO ' + @GlobalTemp
+ 'FROM OPENROWSET(
''SQLNCLI'',
''Server=(local)\SQL2008;Trusted_Connection=yes;'','
+ '''' + @exec + ''')'
Print @sql
exec (@sql)
SELECT * FROM ##MyTempTable1