【问题标题】:Using sp_executesql with input and output paramters?使用带有输入和输出参数的 sp_executesql?
【发布时间】:2021-11-16 21:43:43
【问题描述】:

使用 SQL Server 2016。我正在编写存储过程。我在WHILE 循环中发生了以下代码

DELCARE @recordExists int;
SET @recordExistsQuery = 'SELECT @recordExists=COUNT(*) FROM @fullTableName WHERE validFrom <= CAST(@asOfDate as datetime)';
exec sp_executesql @recordExistsQuery, N'@recordExists INT OUT, @fullTableName varchar(60), @asOfdate datetime' @recordExists OUT

我得到错误`必须声明表变量@fullTableName但是我已经声明并设置了这个变量(在while循环之前)并在while循环之前多次使用它,所以我知道它存在并且是有效的。它是这样定义的 -

DECLARE @fullTableName varchar(60);
SET @fullTableName = (SELECT CONCAT(@schema, '.', @TableName));

我在 while 循环之前打印了它,它看起来很好,我每个循环都有它打印机,它也可以工作。

我这里的动态 sql 有什么问题?我正在尝试使用参数化方法而不是使用引号构建字符串,因为我正在处理日期时间并且希望通过比一堆引号更好的做法来做到这一点。那可能吗?我该如何重写

SET @recordExistsQuery = 'SELECT @recordExists=COUNT(*) FROM @fullTableName WHERE validFrom <= CAST(@asOfDate as datetime)';
exec sp_executesql @recordExistsQuery, N'@recordExists INT OUT, @fullTableName varchar(60), @asOfdate datetime' @recordExists OUT

让它按预期工作?

更新:硬编码表名而不是将其作为参数传递。我现在从以下代码中收到以下错误 -

SET @recordExistsQuery = 'SELECT @recordExists=COUNT(*) FROM ' + @fullTableName + ' WHERE validFrom<=CAST(@asOfDate as datetime)';
PRINT @recordExistsQuery
exec sp_executesql @recordExistsQuery, N'@asOfDate datetime, @recordExists INT OUT', @recordExistsOut`

打印语句显示

SELECT @recordExists=COUNT(*) FROM [MySchema].[MyTable] WHERE validFrom<=(@asOfDate as datetime)

我现在得到的错误是

Msg 8162, Level 16, State 2, Line 0
The formal parameter "@asOfDate" was not declared as an OUTPUT paramter, but the actual paramter passed in requested output.

我将@asOfDate 作为我的存储过程的参数,定义为 @asOfDate DATETIME=NULL 如果没有传入,我的 sp 的第一行设置一个默认值

IF @asOfdate IS NULL 
    SET @asOfDate = GETDATE();

有人知道现在出了什么问题吗?

更新 2: 改用这条线

'SELECT @recordExists=COUNT(*) FROM '+ @fullTableName +' WHERE validFrom <= CAST('+@asOfDate'+' as datetime)'

现在得到错误Msg 241, Level 16, State 1, Conversion failed when converting date and/or time from character string.

@asOfDate 被定义为我的 sp 的参数,如 @asOfDate datetime=NULL 并初始化为 getdate()。我正在使用@asOfDate=null 对此进行测试,因此它使用 getdate() 来初始化值。

【问题讨论】:

  • 您根本不能使用表名作为参数,您需要预先构建这部分查询字符串。
  • 另外,请使用QUOTENAME:SELECT @fullTableName = CONCAT(QUOTENAME(@schema), '.', QUOTENAME(@TableName))。没有必要因为不寻常的名字而无缘无故地打破。
  • @Arvo 谢谢你,我不知道,我能够通过这些信息克服这个错误。
  • @JeroenMostert 好主意,感谢您提供的信息。不知道 QUOTENAME,对用 SQL 编写逻辑非常陌生。

标签: sql sql-server stored-procedures


【解决方案1】:

尝试像这样拆分字符串


'SELECT COUNT(*) FROM'+ @fullTableName +'WHERE validFrom <= CAST('+@asOfDate+' as datetime)'

因为@fullTableName 是作为字符串而不是变量,所以它不起作用。如果您更改为上述内容,则最终字符串将具有您想要的表名

此更新应修复 asofdate 问题

另外,您需要删除@recordExists 并将执行的响应用作@recordExists

【讨论】:

  • 谢谢你让我克服了第一道障碍。能否请您看一下更新,如果您知道现在出了什么问题,请告诉我?
  • @BrianKarabinchak 请检查更新的行,我已将 asofdate 作为变量包含在内
  • 好的,现在给我一个错误Conversion failed when converting date and/or time from character string. 我使用datetime 作为@asOfDate 的类型,或者最初你认为分配给GETDATE() 有问题吗?
  • 抱歉,我之前的更新中有一个额外的 ' ,我已更正。您可以尝试使用最新的字符串并给我输出吗?
  • 我已经抓住了那个额外的 ' 并处理了它。我的查询当前看起来像您的查询,除了我在 select 子句中有 @recordExists=COUNT(*),并且我在 FROM 之后和 `WHERE` 之前有一个空格来分隔表名。否则我的查询现在看起来就像你的一样,错误是一样的。
【解决方案2】:

无法让它按原样工作,所以我最终做了以下事情 - 当我初始化日期时,我还制作了它的格式化版本 -

paramters - 
@asOfDate DATETIME=NULL,
...
BEGIN

IF @asOfdate IS NULL
    SET @asOfDate = GETDATE()
DECLARE @asOfDateFormatted NVARCHAR(30);
SET @asOfDateFormatted = CONVERT(NVARCHAR(30), @asOfDate, 126);
...
Dynamic sql - 
SET @recordExistsQuery = 'SELECT COUNT(*) FROM '+ @fullTableName +' WHERE validFrom <= '''+asOfDateFormatted+'''';
exec sp_executesql @recordExistsQuery, N'@recordExists INT OUT', @recordExists OUT

如果有人知道更好的方法,请告诉我,但这就是我现在使用它的方式。

【讨论】:

  • FROM'+ @fullTableName +'WHERE 不,那肯定没有“工作”。也许您需要空格来分隔关键字和名称。
  • 是的,从远程虚拟机手动复制到这里的转置错误。
【解决方案3】:

使用动态 SQL 时,请记住以下几点:

  • 表名(和其他对象,如列)不能作为参数传递,它们必须注入。
  • 仅使用QUOTENAME 执行此操作以确保正确转义
  • 对象名称应存储在sysnamenvarchar(128) 的别名)和nvarchar(max) 中的查询
  • 数据应始终作为参数传递,而不是注入
DECLARE @asOfDate DATETIME='20210923',

IF @asOfdate IS NULL
    SET @asOfDate = GETDATE();

DECLARE @recordExistsQuery nvarchar(max) = N'
SELECT @recordExists = COUNT(*)
FROM ' + QUOTENAME(@schema) + '.' + QUOTENAME(@TableName) + '
WHERE validFrom <= @asOfdate;
';

PRINT @recordExistsQuery; -- for testing

EXEC sp_executesql
  @recordExistsQuery,
  N'@recordExists INT OUT, @asOfdate DATETIME',
  @recordExists = @recordExists OUT,
  @asOfdate = @asOfdate;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-16
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2015-12-07
    相关资源
    最近更新 更多