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
选择的列的类型转换为第一个WHEN
column 的类型。
这里举两个例子。为简单起见,我删除了变量。
这行得通:
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 语句导致类型转换错误的主要内容,如果未能解决你的问题,请参考以下文章
Oracle order by case when 多条件排序
sqlserver复杂排序(order by case when)
mysql根据某个字段排序,order by case when使用