sql - 使用输出变量动态执行存储过程

标签 sql sql-server t-sql openquery sp-executesql

我有一个脚本,它使用链接服务器引用和 OPENQUERY() 语句调用不同 SQL Server 上的存储过程。为了演示,我在远程服务器上执行了以下过程:

-- 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

现在,我想调用此过程并检索它返回的所有四个值,因此我在本地服务器上编写了以下脚本:

-- 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 Server 14.0.3048.4 上,远程脚本在 13.0.4001.0 上

最佳答案

您可以在每个“范围”中使用相同的参数名称,这可以简化操作,并且必须使用 sp_executesql 而不是 OPENQUERY 来绑定(bind)输出参数。使用 OPENQUERY,您必须返回结果集中的值。

所以像这样:

-- 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 - 使用输出变量动态执行存储过程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62876514/

相关文章:

mysql - 添加一个新列,将列值一分为二,然后在新列中插入一部分值

php - 创建一个 View ,获取排名、用户名和用户记录数

sql - 将数据复制到另一个表中

sql - 使用TSQLUNIT进行SQL单元测试: don't you need to duplicate your SQL code?

MySQL:SELECT EXISTS() AS field WHERE field = x

php - 如何使用 NOW() 更新 SQL 日期

sql - 如何与具有复合主键的表建立关系?

sql - 在 Select 中合并两个表 (SQL Server 2008)

sql-server - 为什么一个 SQL 查询返回的结果与使用始终为 true 的过滤器的 SQL 查询返回的结果不同?

sql-server - 输出使用动态 PL/SQL 执行的 SELECT 的结果