ORDER BY 中带有 CASE WHEN 的 SQL 语句导致类型转换错误

Posted

技术标签:

【中文标题】ORDER BY 中带有 CASE WHEN 的 SQL 语句导致类型转换错误【英文标题】:SQL statement with CASE WHEN in ORDER BY causes type convert error 【发布时间】:2019-08-30 00:10:03 【问题描述】:

执行以下代码会触发错误:

System.Data.SqlClient.SqlException: '无法将 char 值转换为货币。 char 值的语法不正确。

在我添加用于排序目的的第二个参数之前,一切都很好。 为了清楚起见,代码进行了简化。

query as String = "
SELECT a, b, c
FROM DataTable
WHERE c = @PARAM
ORDER BY
    CASE @SORTCOLUMN
        WHEN 1 THEN a
        WHEN 2 THEN b
        WHEN 3 THEN c

    END"

Dim param As String = "myprarameter"
Dim sortcolumn as Integer = 1

result = connection.Query(Of MyType)(query, New With Key .PARAM = param, Key .SORTCOLUMN = sortcolumn)

更新:

经过数小时的测试后,我将其范围缩小到纯粹的 SQL 问题,而不是 Dapper 或 .NET Framework。

这是我的发现: 一切正常,直到CASE WHEN 中使用的所有列都属于相同类型(或类型甚至可以轻松相互转换的值)。一旦列类型不同,值无法转换,则返回类型转换错误。似乎它正在尝试将CASE WHEN 选择的列的类型转换为第一个WHENcolumn 的类型。

这里举两个例子。为简单起见,我删除了变量。

这行得通:

CREATE TABLE #TestTable1
(col1 money, col2 money)

INSERT INTO #TestTable1
(col1, col2)
VALUES
(1, 30),
(2, 20),
(3, 10)

SELECT col1, col2
FROM  #TestTable1
ORDER BY
    CASE 2
        WHEN 1 THEN col1
        WHEN 2 THEN col2
    END

DROP TABLE #TestTable1

但这不起作用。返回:

无法将 char 值转换为货币。 char 值的语法不正确。

CREATE TABLE #TestTable2
(col1 money, col2 varchar(20))

INSERT INTO #TestTable2
(col1, col2)
VALUES
(1, 'cc'),
(2, 'bb'),
(3, 'aa')

SELECT col1, col2
FROM  #TestTable2
ORDER BY
    CASE 2
        WHEN 1 THEN col1
        WHEN 2 THEN col2
    END

DROP TABLE #TestTable2

我正在使用兼容级别为 150 的 Azure SQL。 我已经相应地更新了标题和标签。

更新 2: 我试图以第二个参数的形式向@forpas 解决方案添加一个复杂性,它将告诉订单。我在CASE 中使用了CASE,但这会返回一些语法错误。 仅限 ORDER 部分。其余的没有改变。

ORDER BY
CASE @SORTORDER
WHEN 'a' THEN
   (CASE @SORTCOLUMN
        WHEN 1 THEN col1
        WHEN 2 THEN col2
    END,
    CASE @SORTCOLUMN
        WHEN 3 THEN col3
        WHEN 4 THEN col4
    END) ASC
WHEN 'd' THEN
    (CASE @SORTCOLUMN
        WHEN 1 THEN col1
        WHEN 2 THEN col2
    END,
    CASE @SORTCOLUMN
        WHEN 3 THEN col3
        WHEN 4 THEN col4
    END) DESC
END

【问题讨论】:

【参考方案1】:

一种解决方案是将CASE 语句拆分为所需数量的CASE 语句,这样每个语句都包含具有相同或相似可转换数据类型的列:

CREATE TABLE #TestTable2
(col1 money, col2 decimal(18, 2), col3 varchar(20))

INSERT INTO #TestTable2
(col1, col2, col3)
VALUES
(1, 5.5, 'cc'),
(2, 1.8, 'bb'),
(3, 3.3, 'aa');

DECLARE @SORTCOLUMN INT = 1; -- 1 or 2 or 3

SELECT col1, col2, col3
FROM  #TestTable2
ORDER BY
    CASE @SORTCOLUMN 
      WHEN 1 THEN col1
      WHEN 2 THEN col2 
    END,
    CASE @SORTCOLUMN 
      WHEN 3 THEN col3 
    END

DROP TABLE #TestTable2 

请参阅demo。

【讨论】:

如果ELSE 列是第一个CASE 中的一个,我该如何实现CASE ELSE 场景,所以不能将它作为ELSE 放在最后一个CASE 中,因为会触发同样的转换错误? 如果列是数字(int、decimal、money),它们可以转换。不要将数字与 varchar 和日期混合。如果你需要一个最终的 else ,那就再添加一个 case 语句:CASE @SORTCOLUMN WHEN 4 THEN col4 END 我给问题添加了一个复杂的问题 - 请参阅更新 2【参考方案2】:

只需将 SQL 创建为 nvarchar 并执行如下:

DECLARE @SQL NVARCHAR(MAX) = N'SELECT a, b, c FROM DataTable
    WHERE c = @PARAM
    ORDER BY ' + (CASE @SORTCOLUMN WHEN 1 THEN 'a' WHEN 2 THEN 'b' WHEN 3 THEN 'c' ELSE 'a' END);
EXEC sp_Executesql @SQL;

在 VB 中

query as String = "DECLARE @SQL NVARCHAR(MAX) = N'SELECT a, b, c FROM DataTable
        WHERE c = @PARAM
        ORDER BY ' + (CASE @SORTCOLUMN WHEN 1 THEN 'a' WHEN 2 THEN 'b' WHEN 3 THEN 'c' ELSE 'a' END);
    EXEC sp_Executesql @SQL;"

【讨论】:

这可行,但更喜欢@Forpas 提供的解决方案【参考方案3】:

试试这个。

query as String = "
SELECT a, b, c
FROM DataTable
WHERE c = @PARAM
ORDER BY
    CASE @SORTCOLUMN
        WHEN '1' THEN a
        WHEN '2' THEN b
        WHEN '3' THEN c

    END"

【讨论】:

不抛出异常,但也不排序。有什么办法可以看到参数替换后的最终SQL语句? 这是因为你将你的sortcolumn Dim sortcolumn设置为Integer = 1,这意味着它将按a排序 结果似乎没有按任何结果列排序。将参数更改为Dim sortcolumn as Integer = 2 没有任何区别。 知道了。 'WHEN' 应该看起来像WHEN '1' THEN a。还注意到'sortcolumn' 是Integer 还是String 并不重要。将您的答案标记为已接受,因为它帮助我解决了问题。 是的,我刚刚意识到'a'是一个列,我更新了答案

以上是关于ORDER BY 中带有 CASE WHEN 的 SQL 语句导致类型转换错误的主要内容,如果未能解决你的问题,请参考以下文章

ORDER BY 子句的 CASE WHEN 语句

Oracle order by case when 多条件排序

sqlserver复杂排序(order by case when)

mysql根据某个字段排序,order by case when使用

mysql根据某个字段排序,order by case when使用

mysql根据某个字段排序,order by case when使用