tsql - sp_executesql的区别

时间:2015-02-17 09:15:40

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

我对自己的工具执行程序的方式有疑问。

这是我执行的方式:

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'

返回NOTHING!

第一个工作正常。但是orm生成的那个,' sp_executesql'一个永远不会正常工作。如果我删除@AuthorId,orm生成的一个工作正常,但然后我添加一些其他参数,它再次吹。

我还有其他一些程序,一遍又一遍地生活着同样的问题。不同的参数会导致意外的结果,即使参数的计数也会导致意外的结果。甚至错误。 (当使用sp_executesql时,另一个工作就好了)

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

3 个答案:

答案 0 :(得分:0)

我们确实讨论了这种情况,并确定最佳解决方案是按照确切的严格顺序传递所有参数。如果该过程为其自己的参数处理空值,则只需将它们设置为null或任何其他最适合的值。

感谢您的时间,耐心和兴趣。 @GarethD& @Mukund

答案 1 :(得分:0)

好的,正如评论中所指出的那样,问题是参数的顺序。为了证明这个问题,想象一下以下简单的程序

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),这些参数的分配顺序与参数的定义相同

我还应该为该流程定义什么才能让它得到正在发生的事情?

我不知道你正在使用什么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 :(得分:-1)

尝试以下脚本,

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