使用输出变量动态执行存储过程
Dynamically Executing Stored Proc with output variables
我有一个脚本调用另一个 SQL 服务器上的存储过程,使用链接服务器引用和 OPENQUERY()
语句。为了演示,我在远程服务器上做了以下程序:
-- ON SQL_SVR Remote Server
CREATE PROC [dbo].[MyProc]
( @Pram_A NVARCHAR(4) OUTPUT
,@Pram_B NVARCHAR(4) OUTPUT
,@Pram_C NVARCHAR(4) OUTPUT
,@Pram_D NVARCHAR(4) OUTPUT
)
AS
SELECT
@Pram_A = 'foo'
,@Pram_B = 'bar'
,@Pram_C = 'this'
,@Pram_D = 'that'
GO
现在,我想调用此过程并检索它 returns 的所有四个值,因此我在本地服务器上编写了以下脚本:
-- These variables are final resting places for my data
DECLARE
@Pram_1 NVARCHAR(4)
,@Pram_2 NVARCHAR(4)
,@Pram_3 NVARCHAR(4)
,@Pram_4 NVARCHAR(4);
DECLARE
@SQL NVARCHAR(MAX)
-- I will pass values from the remote proc to the dynamic sql through some output parameters, which are middle men between the remote Alpha and the local Numeric params
,@Parms NVARCHAR(MAX) = '@Pram_A1 NVARCHAR(4) OUTPUT, @Pram_B2 NVARCHAR(4) OUTPUT, @Pram_C3 NVARCHAR(4) OUTPUT, @Pram_D4 NVARCHAR(4) OUTPUT'
,@LinkedServer VARCHAR(50) = '[SQL_SVR]';
-- The dynamic sql expects to get the middle-men parameters, which it will fill with the values it gets for it's alpha-style params
SET @SQL = 'SELECT * FROM OPENQUERY(' + @LinkedServer + ',''EXEC [dbo].[MyProc] @Pram_A = @Pram_A1 OUTPUT, @Pram_B = @Pram_B2,@Pram_C = @Pram_C3,@Pram_D = @Pram_D4'')';
-- Take whatever the remote proc puts into the middle-men params and put it into our local numeric params
EXEC sp_executesql @SQL, @Parms
,@Pram_A1 = @Pram_1 OUTPUT
,@Pram_B2 = @Pram_2 OUTPUT
,@Pram_C3 = @Pram_3 OUTPUT
,@Pram_D4 = @Pram_4 OUTPUT;
-- show the contents of the local, numeric params.
SELECT
@Pram_1 AS P1
,@Pram_2 AS P2
,@Pram_3 AS P3
,@Pram_4 AS P4;
据我所知,这是对(公认的稍微复杂的)参数传递的所有正确用法,声明局部参数,将它们传递给动态 sql 并在动态 sql 使用那些在远程脚本上声明的。但是,当我 运行 以上时,我收到以下警告和错误:
--OLE DB provider "SQLNCLI11" for linked server "PRODSQL-V2" returned message "Deferred prepare could not be completed.".
'Msg 8180, Level 16, State 1, Line 1
Statement(s) could not be prepared.
Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "@Pram_A1".'
我广泛搜索了解决方案,但我看到的一切似乎都暗示我的脚本是正确的。我知道我一定是误解了某些东西并犯了一个错误,但我这辈子都看不到什么!你能看出我做错了什么或解释脚本可能失败的可能原因吗?
注意 1:使用动态 sql 和变量服务器名称的原因并未在非常简化的示例脚本中显示。
注2:本地脚本在SQL服务器14.0.3048.4,远程在13.0.4001.0
您可以在每个“作用域”中使用相同的参数名称以简化操作,并且您必须使用 sp_executesql
而不是 OPENQUERY 来绑定输出参数。使用 OPENQUERY,您必须 return 结果集中的值。
所以像这样:
-- ON SQL_SVR Remote Server
CREATE OR ALTER PROC [dbo].[MyProc]
( @Pram_A NVARCHAR(4) OUTPUT
,@Pram_B NVARCHAR(4) OUTPUT
,@Pram_C NVARCHAR(4) OUTPUT
,@Pram_D NVARCHAR(4) OUTPUT
)
AS
SELECT
@Pram_A = 'foo'
,@Pram_B = 'bar'
,@Pram_C = 'this'
,@Pram_D = 'that'
GO
-- These variables are final resting places for my data
DECLARE @Pram_A NVARCHAR(4)
,@Pram_B NVARCHAR(4)
,@Pram_C NVARCHAR(4)
,@Pram_D NVARCHAR(4);
declare @LinkedServer VARCHAR(50) = 'SQL_SVR';
declare @SQL NVARCHAR(MAX) = concat('
EXEC ',quotename(@LinkedServer),'.[TempDb].[dbo].[MyProc] @Pram_A = @Pram_A OUTPUT,
@Pram_B = @Pram_B OUTPUT,
@Pram_C = @Pram_C OUTPUT,
@Pram_D = @Pram_D OUTPUT
')
declare @Parms NVARCHAR(MAX) = '@Pram_A NVARCHAR(4) OUTPUT, @Pram_B NVARCHAR(4) OUTPUT, @Pram_C NVARCHAR(4) OUTPUT, @Pram_D NVARCHAR(4) OUTPUT'
exec sp_executesql @sql, @Parms, @Pram_A = @Pram_A OUTPUT,
@Pram_B = @Pram_B OUTPUT,
@Pram_C = @Pram_C OUTPUT,
@Pram_D = @Pram_D OUTPUT
SELECT
@Pram_A AS P1
,@Pram_B AS P2
,@Pram_C AS P3
,@Pram_D AS P4;
我有一个脚本调用另一个 SQL 服务器上的存储过程,使用链接服务器引用和 OPENQUERY()
语句。为了演示,我在远程服务器上做了以下程序:
-- ON SQL_SVR Remote Server
CREATE PROC [dbo].[MyProc]
( @Pram_A NVARCHAR(4) OUTPUT
,@Pram_B NVARCHAR(4) OUTPUT
,@Pram_C NVARCHAR(4) OUTPUT
,@Pram_D NVARCHAR(4) OUTPUT
)
AS
SELECT
@Pram_A = 'foo'
,@Pram_B = 'bar'
,@Pram_C = 'this'
,@Pram_D = 'that'
GO
现在,我想调用此过程并检索它 returns 的所有四个值,因此我在本地服务器上编写了以下脚本:
-- These variables are final resting places for my data
DECLARE
@Pram_1 NVARCHAR(4)
,@Pram_2 NVARCHAR(4)
,@Pram_3 NVARCHAR(4)
,@Pram_4 NVARCHAR(4);
DECLARE
@SQL NVARCHAR(MAX)
-- I will pass values from the remote proc to the dynamic sql through some output parameters, which are middle men between the remote Alpha and the local Numeric params
,@Parms NVARCHAR(MAX) = '@Pram_A1 NVARCHAR(4) OUTPUT, @Pram_B2 NVARCHAR(4) OUTPUT, @Pram_C3 NVARCHAR(4) OUTPUT, @Pram_D4 NVARCHAR(4) OUTPUT'
,@LinkedServer VARCHAR(50) = '[SQL_SVR]';
-- The dynamic sql expects to get the middle-men parameters, which it will fill with the values it gets for it's alpha-style params
SET @SQL = 'SELECT * FROM OPENQUERY(' + @LinkedServer + ',''EXEC [dbo].[MyProc] @Pram_A = @Pram_A1 OUTPUT, @Pram_B = @Pram_B2,@Pram_C = @Pram_C3,@Pram_D = @Pram_D4'')';
-- Take whatever the remote proc puts into the middle-men params and put it into our local numeric params
EXEC sp_executesql @SQL, @Parms
,@Pram_A1 = @Pram_1 OUTPUT
,@Pram_B2 = @Pram_2 OUTPUT
,@Pram_C3 = @Pram_3 OUTPUT
,@Pram_D4 = @Pram_4 OUTPUT;
-- show the contents of the local, numeric params.
SELECT
@Pram_1 AS P1
,@Pram_2 AS P2
,@Pram_3 AS P3
,@Pram_4 AS P4;
据我所知,这是对(公认的稍微复杂的)参数传递的所有正确用法,声明局部参数,将它们传递给动态 sql 并在动态 sql 使用那些在远程脚本上声明的。但是,当我 运行 以上时,我收到以下警告和错误:
--OLE DB provider "SQLNCLI11" for linked server "PRODSQL-V2" returned message "Deferred prepare could not be completed.".
'Msg 8180, Level 16, State 1, Line 1
Statement(s) could not be prepared.
Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "@Pram_A1".'
我广泛搜索了解决方案,但我看到的一切似乎都暗示我的脚本是正确的。我知道我一定是误解了某些东西并犯了一个错误,但我这辈子都看不到什么!你能看出我做错了什么或解释脚本可能失败的可能原因吗?
注意 1:使用动态 sql 和变量服务器名称的原因并未在非常简化的示例脚本中显示。
注2:本地脚本在SQL服务器14.0.3048.4,远程在13.0.4001.0
您可以在每个“作用域”中使用相同的参数名称以简化操作,并且您必须使用 sp_executesql
而不是 OPENQUERY 来绑定输出参数。使用 OPENQUERY,您必须 return 结果集中的值。
所以像这样:
-- ON SQL_SVR Remote Server
CREATE OR ALTER PROC [dbo].[MyProc]
( @Pram_A NVARCHAR(4) OUTPUT
,@Pram_B NVARCHAR(4) OUTPUT
,@Pram_C NVARCHAR(4) OUTPUT
,@Pram_D NVARCHAR(4) OUTPUT
)
AS
SELECT
@Pram_A = 'foo'
,@Pram_B = 'bar'
,@Pram_C = 'this'
,@Pram_D = 'that'
GO
-- These variables are final resting places for my data
DECLARE @Pram_A NVARCHAR(4)
,@Pram_B NVARCHAR(4)
,@Pram_C NVARCHAR(4)
,@Pram_D NVARCHAR(4);
declare @LinkedServer VARCHAR(50) = 'SQL_SVR';
declare @SQL NVARCHAR(MAX) = concat('
EXEC ',quotename(@LinkedServer),'.[TempDb].[dbo].[MyProc] @Pram_A = @Pram_A OUTPUT,
@Pram_B = @Pram_B OUTPUT,
@Pram_C = @Pram_C OUTPUT,
@Pram_D = @Pram_D OUTPUT
')
declare @Parms NVARCHAR(MAX) = '@Pram_A NVARCHAR(4) OUTPUT, @Pram_B NVARCHAR(4) OUTPUT, @Pram_C NVARCHAR(4) OUTPUT, @Pram_D NVARCHAR(4) OUTPUT'
exec sp_executesql @sql, @Parms, @Pram_A = @Pram_A OUTPUT,
@Pram_B = @Pram_B OUTPUT,
@Pram_C = @Pram_C OUTPUT,
@Pram_D = @Pram_D OUTPUT
SELECT
@Pram_A AS P1
,@Pram_B AS P2
,@Pram_C AS P3
,@Pram_D AS P4;