将 Int 传递给动态存储过程失败

Passing Int to dynamic stored procedure fails

我在 SQL 服务器中有一个动态存储过程,可以很好地转换 table:

CREATE PROCEDURE dbo.DynamicPivotTableInSql
    @ColumnToPivot  NVARCHAR(255),
    @ListToPivot    NVARCHAR(255),
    @SurveyID           INT=10
AS
BEGIN
    DECLARE @SqlStatement NVARCHAR(MAX)

    SET @SqlStatement = N'SELECT * 
                          FROM 
                              (SELECT
                                   [resp_id], [benefit], [weight]
                               FROM Segment_Responses) myResults
                          PIVOT 
                              (SUM([weight])
                                   FOR [' + @ColumnToPivot + ']
                                   IN (' + @ListToPivot + ')) AS PivotTable';
 
    EXEC (@SqlStatement)
END

我这样称呼它

EXEC DynamicPivotTableInSql 
         @ColumnToPivot = 'benefit',
         @ListToPivot = '[OBSERVABILITY], [COST], [EASE OF USE], [SERVICE]'

这是我 运行 遇到问题的地方。您会注意到我已经对 @SurveyID = 10 进行了硬编码,如果我尝试将其添加为存储过程中的 where 语句,如下所示:

FROM Segment_Responses 
WHERE survey_id = ' + @SurveyID + '

和 运行 存储过程再次出现此错误:

Conversion failed when converting the nvarchar value ' SELECT * FROM ( SELECT [resp_id], [benefit], [weight] FROM Segment_Responses where survey_id=' to data type int.

我尝试了很多方法来解决这个问题(例如,传递 Int 变量而不是对其进行硬编码)但总是得到相同的结果。有什么想法吗?

+ 仅适用于字符串。如果您使用数字,TSQL 会假定您正在尝试使用加法运算符,并尝试将字符串参数转换为 int。

例如这个

select 1 + '2'

有效并且returns 3.

对整数使用 CONCAT instead of +, or use an explicit conversion

例如

WHERE survey_id = ' + cast(@SurveyID as varchar(20)) + '

这就是为什么强烈建议不要使用语法 EXEC (@SQL) 的原因。使用 sys.sp_executesql 并参数化您的语句:

SET @SQL = N'SELECT ...
FROM ...
WHERE survey_id = @SurveyID ...;';

EXEC sys.sp_executesql @SQL, N'@SurveyID int',SurveyID;

只是为了说明一些问题,当您将两种不同的类型相加时,SQL 服务器将(在可能的情况下)隐式地将一种类型转换为另一种类型 - 毕竟结果必须是一种类型。

它根据 order of precedence 决定将哪个“转换为另一个”。

因此,当您尝试将 varcharint 连接时,int具有更高的优先顺序。当在表达式的不同执行路径中混合类型时,这也是使用 case 表达式 时出现错误和错误的常见原因。

您需要显式地将 int 转换为 varchar

理想情况下,您会使用 参数化查询,它也会重用缓存的执行计划 - 如果数据的基数相似但有时使值成为动态查询可能是有利的,这取决于用例。