【问题标题】:tsql - sp_executesql differencetsql - sp_executesql 的区别
【发布时间】:2015-02-17 09:15:39
【问题描述】:

我的 orm 工具执行程序的方式有问题。

这就是我的执行方式:

exec PostViewProc @Id='18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E',@AuthorId='5455D9B9-B25A-41BD-BD2C-C9CBAE87D629'

按预期返回 1 行..

这就是 orm 工具生成它的方式:

exec sp_executesql 
N'EXEC [PostViewProc] @Id, @AuthorId',
N'@Id uniqueidentifier,@AuthorId uniqueidentifier',
@Id='18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E',@AuthorId='5455D9B9-B25A-41BD-BD2C-C9CBAE87D629'

不返回任何内容!

第一个工作正常。但是 orm 生成的“sp_executesql”永远无法正常工作。如果我删除@AuthorId,orm 生成的可以正常工作,但是我添加了一些其他参数,它又会崩溃。

我还有其他一些程序,并且一遍又一遍地遇到同样的问题。不同的参数会导致意想不到的结果,即使是参数的数量也会导致意想不到的结果。甚至错误。 (当使用 sp_executesql 时,其他的工作都很好)

我需要对这里发生的事情进行一些很好的解释,因为程序本身没有任何问题,我现在很困惑。

【问题讨论】:

  • 实际程序中定义的参数顺序是怎样的?我能看到的唯一区别是,在您的查询中,您明确声明了参数,而动态 sql 只传递值(EXEC proc @Param = @ParamValue vs EXEC proc @ParamValue
  • @GarethD Id 1,AuthorId 8。我是否必须定义所有参数并为它们设置一些值,即使我不需要它们(因为它是这样工作的)。该过程处理空参数,实际上所有参数的默认值都是空的。我可以像“Exec PostViewProc”一样执行它而无需任何参数..
  • 不确定它是什么 ORM,但我会说解决方法是以正确的顺序传递所有参数,并在适当的地方传递 null。请记住,该过程不知道要传递的参数的名称 - 因此,尽管您传递了一个名为 @AuthorID 的变量,但该过程只看到它的值,因此无法猜测您想将其作为值传递给同名参数。
  • 看来我需要传递所有参数,我同意,但必须问一下;我在使用 sp_executesql 时确实传递了参数名称和类型,不是吗?为什么proc无法匹配它们?这 4 行 tsql 代码是做什么的?我不明白。我还应该对该 proc 定义什么以使其了解这里发生的情况? :)
  • 花了我更多的时间来回答所有这些问题,所以不得不添加一个答案,但希望它有所帮助。

标签: sql-server tsql stored-procedures exec sp-executesql


【解决方案1】:

好的,正如评论中指出的那样,问题在于参数的顺序。为了演示这个问题,想象一下以下简单的过程

CREATE PROCEDURE dbo.TestProc @A INT = NULL, @B INT = NULL, @C INT = NULL
AS
BEGIN
    SELECT TOP 1 A = @A, B = @B, C = @C;
END
GO

要记住的关键是变量和参数之间没有映射,例如

DECLARE @C INT = 1;
EXECUTE dbo.TestProc @C;

无法识别您的变量名为@C,因此您希望将其分配给同名参数。如果您在调用过程时没有显式地为参数分配值,那么它们将按照先到先得的原则分配,因此上面将返回:

A   B       C
------------------
1   NULL    NULL

事实上,由于您的变量只不过是一个占位符,因此上述查询与执行没有什么不同:

EXECUTE dbo.TestProc 1;

您当然不会期望该过程知道您打算将 1 作为 @C 而不是 @A 的值传递。您的 ORM 生成的查询有类似的问题。与上述过程等效的代码是:

EXECUTE sp_executesql N'EXEC dbo.TestProc @A, @C', N'@A INT, @C INT', @A = 1, @C = 2;

返回:

A   B   C
------------
1   2   NULL

现在在评论中回答你的一些问题。

我在使用 sp_executesql 时会传递参数名称和类型,不是吗?

是的,你可以,但这不是强制性的,以下两条语句将产生相同的结果:

EXECUTE sp_executesql 
    N'EXEC dbo.TestProc @A = @A, @B = @B, @C = @C', 
    N'@A INT, @B INT, @C INT', 
    @A = 1, @B = 2, @C = 3; 

EXECUTE sp_executesql 
    N'EXEC dbo.TestProc @A, @B, @C', 
    N'@A INT, @B INT, @C INT', 
    1, 2, 3; 

如果您只想传递某些值,那么建议使用命名参数,但不是强制性的。不同之处在于,如果您不使用参数名称,则必须以正确的顺序提供参数:

EXECUTE sp_executesql 
    N'EXEC dbo.TestProc @A = @A, @C = @C', 
    N'@A INT, @C INT', 
    @A = 1, @C = 3; 

EXECUTE sp_executesql 
    N'EXEC dbo.TestProc @A, @B, @C', 
    N'@A INT, @B INT, @C INT', 
    1, NULL, 3; 

为什么 proc 不能匹配它们?

如上所述,程序不知道你的变量名,只知道它的值,所以下面两个语句是等价的:

DECLARE @C INT = 1;
EXECUTE dbo.TestProc @c;

EXECUTE dbo.TestProc 1;

如果您检查前一个查询的执行计划 XML,您可以确认:

<ParameterList>
    <ColumnReference Column="@C" ParameterCompiledValue="NULL" ParameterRuntimeValue="NULL" />
    <ColumnReference Column="@B" ParameterCompiledValue="NULL" ParameterRuntimeValue="NULL" />
    <ColumnReference Column="@A" ParameterCompiledValue="NULL" ParameterRuntimeValue="(1)" />
</ParameterList>

对传递给过程的变量名称的任何引用都将丢失。此外,即使它没有丢失,也没有理由匹配它,如果不匹配,你会如何处理它,例如

EXECUTE dbo.TestProc @D, @A, 2;

@A 匹配参数名称,但@D 不匹配,常量2 也不匹配。没有明智的方法来解决这个问题,而且 AFIK 没有语言可以从传递的变量的名称中推断出参数。

这 4 行 tsql 代码的作用是什么?

1.  exec sp_executesql 
2.  N'EXEC [PostViewProc] @Id, @AuthorId',
3.  N'@Id uniqueidentifier,@AuthorId uniqueidentifier',
4.  @Id='18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E',@AuthorId='5455D9B9-B25A-41BD-BD2C-C9CBAE87D629'
  • 第 1 行只是对过程 sp_executesql 的调用。
  • 第 2 行是语句参数 - 这是要执行的 sql。
  • 第 3 行是@params 参数,其中包含语句中使用的所有参数的定义。
  • 第 4 行是@params 中使用的参数的值,除非显式分配(例如@A = 1, @B = 2),否则这些参数的分配顺序与定义参数的顺序相同

我还应该为那个 proc 定义什么才能让它知道这里发生了什么?

我不知道您使用的是什么 ORM,所以我不确定如何强制它生成正确的代码,它应该正在生成:

EXEC sp_executesql 
N'EXEC [PostViewProc] @Id = @Id, @AuthorID = @AuthorId',
N'@Id uniqueidentifier,@AuthorId uniqueidentifier',
@Id='18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E',@AuthorId='5455D9B9-B25A-41BD-BD2C-C9CBAE87D629';

与第二行不同的是

N'EXEC [PostViewProc] @Id, @AuthorId',

N'EXEC [PostViewProc] @Id = @Id, @AuthorID = @AuthorId',

如果您无法强制使用 named parameters,那么您将必须以正确的顺序传递所有值,包括 NULL 值。

【讨论】:

    【解决方案2】:

    我们确实讨论了这种情况,并决定最好的解决方案是以精确的顺​​序传递所有参数。如果过程为自己的参数处理 null 值,那么只需将它们设置为 null 或任何其他最适合的值。

    感谢你们的时间、耐心和兴趣。 @GarethD & @Mukund

    【讨论】:

      【解决方案3】:

      试试下面的脚本,

      exec sp_executesql '18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E','5455D9B9-B25A-41BD-BD2C-C9CBAE87D629'
      

      【讨论】:

      • Msg 214,级别 16,状态 2,过程 sp_executesql,第 1 行过程需要类型为“ntext/nchar/nvarchar”的参数“@statement”。
      • 如果你的存储过程有更多的参数,你可以修改exec语句。
      • 声明不是过程的参数。有比这两个更多的参数,但 proc 处理空参数,实际上每个参数的默认值为空。你能解释一下'修改',我的意思是我应该怎么做?
      • 这个过程在我的北风数据库中,CREATE PROCEDURE CustOrderHist '@CustomerID nchar(5) AS SELECT ProductName, Total=SUM(Quantity) FROM Products P, [Order Details] OD, Orders O, Customers C WHERE C.CustomerID = @CustomerID AND C.CustomerID = O.CustomerID AND O.OrderID = OD.OrderID AND OD.ProductID = P.ProductID GROUP BY ProductName 我可以执行这个 sp 作为 exec dbo.CustOrderHist ALFKI
      • 哦,好的,我明白了。不,这不是这里的情况,我有 11 个参数,其中一些将为空。例如,在第 x 页我需要第 1 和第 5 个参数,在第 y 页我需要第 3 和第 8 个参数,其余为空。
      猜你喜欢
      • 1970-01-01
      • 2013-01-21
      • 1970-01-01
      • 2013-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-11
      相关资源
      最近更新 更多