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

问题描述 投票:0回答:3

我在 SQL Server 中有一个动态存储过程,可以很好地转换表:

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 + '

并再次运行存储过程,我收到此错误:

转换 nvarchar 值时转换失败' 选择 * 从 ( 选择 [resp_id], [益处], [重量] 从 Segment_Responses(其中 Survey_id=')到数据类型 int。

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

sql-server stored-procedures dynamic
3个回答
3
投票

为了更清楚起见,当您将两种不同的类型加在一起时,SQL Server 将(在可能的情况下)隐式地将一种类型转换为另一种类型 - 毕竟结果必须是单一类型。

它根据优先顺序决定将哪个“转换为另一个”。

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

您需要明确并将 int 转换为 varchar

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


3
投票

这就是强烈建议反对语法

EXEC (@SQL)
的原因。使用
sys.sp_executesql
并参数化您的语句:

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

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

0
投票

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

例如这个

select 1 + '2'

工作并返回 3.

使用 CONCAT 而不是

+
,或在 int 上使用 显式转换

例如

WHERE survey_id = ' + cast(@SurveyID as varchar(20)) + '
© www.soinside.com 2019 - 2024. All rights reserved.