【问题标题】:Error converting nvarchar to int inside a dynamic ORDER BY在动态 ORDER BY 中将 nvarchar 转换为 int 时出错
【发布时间】:2014-01-02 18:23:55
【问题描述】:

SQL 代码:

DECLARE @SortOrder nvarchar(max); 
SET @SortOrder = 'name';
SELECT * FROM [database_name].[schema_name].[table_name] 
ORDER BY CASE @SortOrder
    WHEN 'id' THEN id 
    WHEN 'name' THEN name 
    END;

输出: 消息 245,第 16 层,状态 1,第 3 行 将 nvarchar 值 'foo' 转换为数据类型 int 时转换失败。

当我改为使用 @SortOrder = 'id' 时,它可以完美运行。当我执行正常的 SELECT 和 ORDER BY 名称时,它也可以完美运行。

为什么它会尝试将该 nvarchar 值转换为数据类型 int,我该如何阻止它这样做?

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    CASE 是一个返回特定数据类型的单个值的表达式。潜在值必须兼容,并且由于您的名称值必须为 foo,而无法转换为 INT,因此您会收到此错误。解决此问题的最简单方法是按数据类型分隔 ORDER BY 表达式:

    ORDER BY CASE @SortOrder WHEN 'id' THEN id END,
             CASE @SortOrder WHEN 'name' THEN name END;
    

    另一种选择(只要您启用了optimize for ad hoc workloads,它可能更适合计划缓存)是动态 SQL:

    DECLARE @sql NVARCHAR(MAX) = N'SELECT ... ORDER BY ' + QUOTENAME(@SortOrder) + ';';
    
    EXEC sp_executesql @sql;
    

    也不知道为什么要给 @SortOrder 一个最大数据类型 - 没有任何列名可以接近这么大。

    【讨论】:

    • 我更喜欢选项 1,因为:SET @SortOrder =N'1; DROP TABLE tblname;';
    • @EdwardDortland 当然,我认为我们不需要将整个动态 SQL 对话的范围扩大到这个范围内。在性能更重要的地方,这些输入可能会受到足够的保护,并且希望执行用户首先没有DROP 权限 - 这完全是一个不同的问题。无论如何,QUOTENAME() 应该会有所帮助。
    • 谢谢,这已经解决了。
    【解决方案2】:

    通过 case 语句的所有路由必须输出相同的数据类型。尝试将 ID 转换为这样的字符串,它会起作用。

    DECLARE @SortOrder nvarchar(max); 
    SET @SortOrder = 'name';
    SELECT * FROM [database_name].[schema_name].[table_name] 
    ORDER BY CASE @SortOrder
        WHEN 'id' THEN cast(id AS NVARCHAR(20))
        WHEN 'name' THEN name 
        END;
    

    【讨论】:

    • 不,这根本不会很好地工作。例如,9 将排在 32 之后。
    • 确实,我刚刚发现了这个缺陷。
    猜你喜欢
    • 2012-08-16
    • 2011-08-27
    • 2014-02-01
    • 2015-03-14
    • 2015-12-24
    • 1970-01-01
    • 2017-04-08
    • 1970-01-01
    相关资源
    最近更新 更多