SQL 查询在 运行 和 sp_executesql 时表现不同

SQL query behaving differently when run with sp_executesql

我在 SQL Server 2012 中有一个查询,我首先 运行 不使用 sp_executesql,然后 运行 使用 sp_executesql 对其进行查询。奇怪的是,这两个 运行s 给出了不同的结果,当它们应该相同时,因为它们使用相同的 SQL 查询。

我什至尝试过使用 OUTPUT 作为 @productId,如下面的代码所示,但即便如此也没有任何区别。在此 post 末尾给出的屏幕截图中,前两个结果集应与后两个结果集重复,但它们不会。

EXECUTE sp_executesql   @qry,
                        N'@maxRows int,@startingRowNumber int,@productId bigint OUTPUT',
                        @maxRows = @maxRows,
                        @productId = @productId OUTPUT,
                        @startingRowNumber = @numberOfRowsToSkip

问题:是什么导致相同查询的结果不同?

DECLARE @startingRowNumber INT = 1;
DECLARE @productId BIGINT;
DECLARE @maxRows INT = 10;
DECLARE @qryCount NVARCHAR(MAX);
DECLARE @qry NVARCHAR(MAX);
DECLARE @numberOfRowsToSkip INT;
SET @numberOfRowsToSkip = @startingRowNumber - 1;

--RUN query batch without sp_executesql
SELECT  @productId = MAX(ProductId)
             FROM (SELECT TOP (@startingRowNumber)
                    ProductId
                    FROM dbo.Prods
                    WHERE [Product Cost] < 1005
                    ORDER BY ProductId ASC) x;
SELECT @productId AS ProductId;
SELECT ProductId,Product,Vendor,VendorId,[Product Cost]
FROM dbo.Prods WITH (NOLOCK)
WHERE ProductId >= @productId
AND [Product Cost] < 1005
ORDER BY [Product Cost] ASC OFFSET @numberOfRowsToSkip
ROWS FETCH NEXT @maxRows ROWS ONLY;

--RUN query batch using sp_executesql
SET @qry = N'SELECT @productId = MAX(ProductId)
             FROM (SELECT TOP (@startingRowNumber)
                    ProductId
                    FROM dbo.Prods
                    WHERE [Product Cost] < 1005
                    ORDER BY ProductId ASC) x;
SELECT @productId AS ProductId;
SELECT ProductId,Product,Vendor,VendorId,[Product Cost]
FROM dbo.Prods WITH (NOLOCK)
WHERE ProductId >= @productId
AND [Product Cost] < 1005
ORDER BY [Product Cost] ASC OFFSET @numberOfRowsToSkip
ROWS FETCH NEXT @maxRows ROWS ONLY';

EXECUTE sp_executesql   @qry,
                        N'@maxRows int,@startingRowNumber int,@productId bigint',
                        @maxRows = @maxRows,
                        @productId = @productId,
                        @startingRowNumber = @numberOfRowsToSkip
PRINT N'Executed select query'

查询结果截图

更新 1

有趣的是,我还注意到查询 1 returns 一个值,但查询 2 没有。我很确定这个问题的答案就是原始问题的答案。

查询 1

SELECT @productId = Max(x.ProductId) FROM
(SELECT TOP (@startingRowNumber)  ProductId FROM dbo.Prods WHERE
      [Product Cost] < 1005 ORDER BY ProductId ASC) x;
SELECT @productId AS ProductId

查询 2

SET @qry = N'SELECT @productId = Max(x.ProductId) FROM
(SELECT TOP (@startingRowNumber)  ProductId FROM dbo.Prods WHERE
      [Product Cost] < 1005 ORDER BY ProductId ASC) x;';
EXECUTE sp_executesql   @qry,
                        N'@maxRows int,@startingRowNumber int,@productId bigint OUTPUT',
                        @productId = @productId OUTPUT,
                        @startingRowNumber = @numberOfRowsToSkip,
                        @maxRows = @maxRows
SELECT @productId AS ProductId

我认为你的差异是由以下原因造成的:

No dynamic: {OFFSET @startingRowNumber} 
Dynamic: {OFFSET @numberOfRowsToSkip} = {OFFSET @startingRowNumber - 1;}

此外,@productId 在您的动态查询中分配,但也作为输入提供。您应该在查询中声明它并将其从 sp_executesql:

的参数列表中删除
DECLARE @productId BIGINT
SET @qry = N'SELECT @productId = MAX(ProductId)
             FROM (SELECT TOP (@startingRowNumber)
...

我的动态查询中出现了一个愚蠢的错误。以下 sp_executesql 导致正确执行。我不得不改变两件事。另外,我不必将 @productId 声明为 OUTPUT 类型参数。

  • 已将 @startingRowNumber = @numberOfRowsToSkip 更改为 @startingRowNumber = @startingRowNumber
  • @numberOfRowsToSkip 的另一个参数添加到 sp_executesql

使其工作的代码

EXECUTE sp_executesql   @qry,
                    N'@maxRows int,@startingRowNumber int,@productId bigint,@numberOfRowsToSkip int ',
                    @maxRows = @maxRows,
                    @productId = @productId,
                    @startingRowNumber = @startingRowNumber,
                    @numberOfRowsToSkip = @numberOfRowsToSkip