我在 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 将(在可能的情况下)隐式地将一种类型转换为另一种类型 - 毕竟结果必须是单一类型。
它根据优先顺序决定将哪个“转换为另一个”。
因此,当您尝试将 varchar 与 int 连接时,int 具有更高的优先级。当使用 case 表达式 在表达式的不同执行路径中混合类型时,这也是导致错误和 bug 的常见原因。
您需要明确并将 int 转换为 varchar。
理想情况下,您会使用参数化查询,它也会重用缓存的执行计划 - 如果数据的基数相似,这可能会很有用,但有时动态地使查询的值部分可能是有利的,这取决于使用情况-案例。
这就是强烈建议反对语法
EXEC (@SQL)
的原因。使用 sys.sp_executesql
并参数化您的语句:
SET @SQL = N'SELECT ...
FROM ...
WHERE survey_id = @SurveyID ...;';
EXEC sys.sp_executesql @SQL, N'@SurveyID int',@SurveyID;