【问题标题】:Is it possible to supply sp_ExecuteSql parameter names dynamically?是否可以动态提供 sp_ExecuteSql 参数名称?
【发布时间】:2021-03-17 20:58:20
【问题描述】:

是否可以动态地向 sp_ExecuteSql 提供参数列表?

在 sp_ExecuteSql 中,查询和参数定义是字符串。我们可以为这些使用字符串变量,并传入我们想要执行的任何查询和参数定义。但是,在为参数赋值时,我们似乎不能使用字符串或字符串变量作为参数名称。

例如:

DECLARE @SelectedUserName NVARCHAR(255) ,
    @SelectedJobTitle NVARCHAR(255);
SET @SelectedUserName = N'TEST%';
SET @SelectedJobTitle = N'%Developer%';

DECLARE @sql NVARCHAR(MAX) ,
    @paramdefs NVARCHAR(1000);
SET @sql = N'select * from Users where Name LIKE @UserName '
    + N'and JobTitle LIKE @JobTitle;'
SET @paramdefs = N'@UserName nvarchar(255), @JobTitle nvarchar(255)';
EXEC sp_ExecuteSql @sql, @paramdefs, @UserName = @SelectedUserName,
    @JobTitle = @SelectedJobTitle;

查询@sql 和参数定义@paramdefs 可以作为字符串变量动态传递到sp_ExecuteSql。但是,在我看来,在为参数分配值时,我们不能动态分配,并且必须始终提前知道参数的数量及其名称。请注意,在我的示例中,我如何动态声明参数 @UserName 和 @JobTitle 并将该声明作为字符串变量传递,但是当我想设置参数名称时,我必须显式指定它们。有什么办法可以绕过这个限制吗?

我希望能够动态声明参数并动态分配参数。比如:

EXEC sp_ExecuteSql @sql, @paramdefs,
    N'@UserName = @SelectedUserName, @JobTitle = @SelectedJobTitle';

请注意,这实际上不起作用,但说明了我希望发生的事情。如果这种事情有效,那么我可以传递具有不同名称的不同数量参数的不同查询。整个过程是动态的,我不必事先知道参数的名称或数量。

【问题讨论】:

标签: sql sql-server tsql


【解决方案1】:

您可以通过使用表值参数作为唯一参数来做到这一点:

DECLARE @YourQuery NVARCHAR(MAX0 = '<your dynamic query>'

CREATE TYPE dbo.SqlVariantTable AS TABLE
(
    [Name]  VARCHAR(255),
    Type    VARCHAR(255),
    Value   SQL_VARIANT
)

DECLARE @Table SqlVariantTable;

-- Insert your dynamic parameters here:
INSERT INTO @Table 
VALUES
    ('Parameter1', 'VARCHAR(255)', 'some value'),
    ('Parameter2', 'INT', 3),

DECLARE @ParameterAssignment NVARCHAR(MAX)
SELECT @ParameterAssignment = ISNULL(@ParameterAssignment + ';','') + 'DECLARE ' + Name + ' ' + Type + ' = (SELECT CAST(Value AS ' + Type + ') FROM @p1 WHERE Name = ''' + Name + ''')'
FROM @Table

SET @YourQuery = @ParameterAssignment + ';' + @YourQuery

EXEC SP_EXECUTESQL @YourQuery, N'@p1 SqlVariantTable READONLY', @Table

现在您可以简单地将参数插入到@Table 变量中,它们将以其原始名称和类型出现在 SP_EXECUTESQL 中执行的查询中。只需确保不使用 VARCHAR(MAX) 或 NVARCHAR(MAX) 变量类型,因为 SQL_VARIANT 不支持它们。改用(例如)VARCHAR(4000)

【讨论】:

    【解决方案2】:

    你试图在抽象层面上工作太高了。

    任意参数需要动态 SQL,也就是通过字符串构建 SQL,这使得参数的整个意义没有实际意义。

    相反,这应该在调用代码中作为参数处理,例如C#,它允许您在字符串中获取任何SQL语句,应用任意参数个数,然后执行它。

    【讨论】:

    • 我担心可能是这种情况。问题是查询存储在数据库中的表中。但是我们可以将适当的查询拉回客户端代码,在那里应用参数并执行它。幸运的是性能不是问题。
    • "这使得参数的全部意义没有实际意义" 不是真的。虽然这可能是不寻常的,但没有根本原因为什么人们永远不想这样做——例如,我有兴趣根据元数据中的定义执行 SP——为了实现这一点,我需要原始问题中描述的功能。
    • 如果调用代码不是 C#,而是 TSQL 中试图动态构建字符串的存储产品怎么办?这需要能够将未知数量的本地 SQL 变量传递给 sp_executesql。需要有一种方法来调用 sp_executesql,而无需提前知道参数的数量,这似乎需要将某种参数数组传递给它。
    【解决方案3】:

    我也想过这个,找不到比这更好的了:

    BEGIN
      DECLARE
        @p1 int, @p2 int, @p3 int, @p4 int...;
    
      DECLARE
        @DynamicSQL NVARCHAR(MAX);
    
      SET
        @p1 = {some logic},
        @p2 = {some different logic},
        @p3 = {another logic},
        @p4 = {yet another logic},
        ...;
      
      
      SET
        @DynamicSQL =
        N'
          some statement
          doing
          somethin
          WHERE
            someColumn = @p1
            AND someAnotherColumn = @p2
            /*no more parameters used below this line*/
        ';
    
      exec sp_executesql
        @stmt = @DynamicSQL,
        @params = '@p1 int, @p2 int, @p3 int, @p4 int...'
        @p1 = @p1, @p2 = @p2, @p3 = @p3, @p4 = @p4, ...
    END;
    

    请注意,@DynamicSQL 仅使用 4 个可能参数中的 2 个。参数@p1 int, @p2 int, @p3 int, @p4 int... 表示您可以在@DynamicSQL 中使用的最大参数数。

    您必须预先定义可以使用的最大参数数量,并且您只使用其中的一部分来构建@DynamicSQL 语句。在@params 中定义但在@stmt 语句中不存在的参数将被忽略。

    它不是 100 % 通用的,但我猜使用 200 多个动态参数会产生代码异味。

    【讨论】:

    • 不是解决方案。仍然需要预先知道参数的数量。您将展示如何使用已知数量的固定参数调用动态 SQL 字符串。 OP 正在寻找一种解决方案来调用具有未知数量参数的动态 SQL。这可能需要 NESTING 动态 SQL 语句,因此您不仅可以生成 sp_executesql 和动态传递给它的参数,还可以获取传递的动态字符串中的数据。这可能需要通过一个单独的表值参数传递参数数据,就像另一个答案一样。
    • @Triynko:嗯,你不需要知道确切的数量,但需要知道参数的最大数量。 OP 可以准备 20 个VARCHAR 参数、20 个INTEGER 参数、20 个DATETIME 和 20 个TABLE 参数。他需要预先声明所有这些,在@params 中指定它们并将它们分配给sp_executesql。然后,他只能使用其中之一并仅使用其中之一生成 SQL。
    • @Triynko:我已经编辑了答案。你还觉得这回答不了问题吗?
    【解决方案4】:

    虽然这不能回答我的问题,但我认为它可能对处于类似情况的其他人有用。我发现了以下内容:

    如果您有固定数量的参数但不知道它们的名称,您可以通过位置而不是名称来传递参数值。以下将起作用:

    exec sp_ExecuteSql 
        @sql, 
        @paramdefs, 
        @SelectedUserName, @SelectedJobTitle;
    

    exec sp_ExecuteSql 
        @sql, 
        @paramdefs, 
        N'TEST%', N'%Developer%';
    

    【讨论】:

    • 这乍一看似乎可行,但我认为每个参数的不同数据类型会使其无法使用。
    • @tbone:是的,你是对的。例如,只有当您知道参数始终是 varchars 时,它才会起作用。所以它只在非常有限的情况下有用。
    • 当一种类型的进程被分配了它的计算,指定了计算(存储过程)和它的参数时,这个场景是非常有用的。否则它是不灵活的,通过这篇文章我已经解决了我的情况。
    【解决方案5】:

    请试试这个。

    Declare @UName varchar(50)
    Declare @Job varchar(50)
    Set @UName = 'TEST%'
    Set @Job = '%Developer%'
    exec sp_ExecuteSql @sql, @paramdefs, @UserName = @UName, @JobTitle = @Job;
    

    希望这会对你有所帮助。

    参考来自technet.Microsoft.com

    例如

    DECLARE @IntVariable int;
    DECLARE @SQLString nvarchar(500);
    DECLARE @ParmDefinition nvarchar(500);
    
    /* Build the SQL string one time.*/
    SET @SQLString = N'SELECT BusinessEntityID, NationalIDNumber, JobTitle, LoginID
                       FROM AdventureWorks2012.HumanResources.Employee 
                       WHERE BusinessEntityID = @BusinessEntityID';
    SET @ParmDefinition = N'@BusinessEntityID tinyint';
    
    /* Execute the string with the first parameter value. */
    SET @IntVariable = 197;
    EXECUTE sp_executesql @SQLString, @ParmDefinition,
                          @BusinessEntityID = @IntVariable;
    
    /* Execute the same string with the second parameter value. */
    SET @IntVariable = 109;
    EXECUTE sp_executesql @SQLString, @ParmDefinition,
                          @BusinessEntityID = @IntVariable;
    

    对于动态,你必须传递这样的东西

    EXECUTE sp_executesql N'Select * from Admin WHERE ID = @ID and FirstName=@FName',
                          N'@ID tinyint, @FName varchar(250)',
                          @ID = 2, @FName = 'admin';
    

    【讨论】:

    • 我的问题还不够清楚。我已经对其进行了编辑以尝试更好地解释。基本上,我希望能够将查询传递给具有任意数量参数的 sp_ExecuteSql,并且这些参数可以具有任意名称。
    • 好的,但是您必须在查询中定义该参数,然后将参数值传递给它
    • 这正是问题所在:有没有办法将带有任意数量参数的查询传入 sp_ExecuteSql 并为这些参数赋值?我发现我可以按位置传递参数值,调用 sp_ExecuteSql 时不需要使用参数名称。但我仍然需要提前知道有多少参数。
    • 对于动态来说,你必须首先存储在字符串中,然后执行类似这样的东西,你必须将查询和参数存储在字符串中,然后你必须执行该查询
    • 您的动态示例说明了问题:您必须提前知道有两个参数,称为@ID@FName。但是如果你不知道有多少个参数,或者它们叫什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-31
    相关资源
    最近更新 更多