我正试图在CASE
子句中使用ORDER BY
列表中的DISTINCT
语句中放置SELECT
语句,并找到一些我不喜欢的奇怪行为不明白。这是一些代码:
select distinct Requester, ISO_ID as ISO, (ISO_ID - 5 + 50) AS 'someNum', BU
from LoanerHeader order by
CASE WHEN 'a' = 'b' then Requester
when 'b' = 'c' then BU
else ISO_ID
end
这很有效。但是,如果我将第4行更改为when 'b' = 'b' then BU
:
select distinct Requester, ISO_ID as ISO, (ISO_ID - 5 + 50) AS 'someNum', BU
from LoanerHeader order by
CASE WHEN 'a' = 'b' then Requester
when 'b' = 'b' then BU
else ISO_ID
end
它打破了错误:
如果SELECT DISTINCT为,则ORDER BY项必须出现在选择列表中 指定。
BU
显然在选择列表中。更奇怪的是当我将代码更改为:
select distinct Requester, ISO_ID as ISO, (ISO_ID - 5 + 50) AS 'someNum', BU
from LoanerHeader order by
CASE WHEN 'a' = 'b' then Requester
when 'b' = 'b' then BU
else BU --change is here
end
再次有效!这怎么有意义呢?有人可以帮我把这个脑袋包起来吗?
答案 0 :(得分:7)
CASE
的规则是结果应该转换为具有最高优先级的分支的数据类型。
对于第一个查询,它使用矛盾检测,只生成一个直接按ISO_ID
排序的计划。这已经是数字,所以不需要隐式转换,因此匹配选择列表中的表达式没有问题。
对于第二个查询,它可以在编译时再次确定它需要ORDER BY BU
。由于上述原因,它实际上需要ORDER BY CAST(BU AS NUMERIC)
。这意味着它需要ORDER BY
计算表达式不匹配SELECT
列表中的任何内容。因此问题。
您的第三个查询从CASE
中删除了更高优先级的表达式,从而消除了对隐式强制转换的需要(因此需要按计算表达式排序)。
由于计算表达式完全取决于SELECT DISTINCT
列表中的列,但您可以按如下方式重写第二个查询。
;WITH CTE AS
(
SELECT DISTINCT Requester,
ISO_ID AS ISO,
( ISO_ID - 5 + 50 ) AS 'someNum',
BU
FROM LoanerHeader
)
SELECT *
FROM CTE
ORDER BY CASE
WHEN 'a' = 'b' THEN Requester
WHEN 'b' = 'b' THEN BU
ELSE ISO
END
答案 1 :(得分:2)
您必须在CASE
列表中包含完整的SELECT
表达式,而不仅仅是组件部分。
如果Requester
,BU
和ISO_ID
的数据类型不同,我们在此处观察到的不稳定行为可能会发生。从示例中可以明显看出ISO_ID
是数字。我从行为中猜测,Requester
和BU
都不是数字,但它们都是相同类型(或兼容类型)。如果它们都是字符类型,则可能需要将ISO_ID
强制转换为CASE
表达式中的字符类型。
答案 2 :(得分:0)
我在SQL Server 2000中运行了类似你的东西,每次都失败了,当我去SQL Server 2005时,我开始看到你的问题了。我将代码更改为:
select distinct Requester, ISO_ID as ISO, (ISO_ID - 5 + 50) AS 'someNum', BU,
CASE WHEN 'a' = 'b' then Requester
when 'b' = 'c' then BU
else ISO_ID
end AS SortByField
from LoanerHeader order by
CASE WHEN 'a' = 'b' then Requester
when 'b' = 'c' then BU
else ISO_ID
end
我收到如下错误: 将nvarchar值'xxxxxxxxx'转换为数据类型tinyint时转换失败。
我不知道你的数据类型是什么,但似乎它们都必须是相同的,看起来它将else部分中字段的数据类型作为所有这些类型的类型什么时候没有指定。
当我做了类似的事情时:
select distinct Requester, ISO_ID as ISO, (ISO_ID - 5 + 50) AS 'someNum', BU,
CASE WHEN 'a' = 'b' then Requester
when 'b' = 'c' then BU
else convert(nvarchar(50), ISO_ID)
end AS SortByField
from LoanerHeader order by
CASE WHEN 'a' = 'b' then Requester
when 'b' = 'c' then BU
else convert(nvarchar(50), ISO_ID)
end
然后在两种情况下改变了字母,似乎工作正常。
可能更好的编码方式是:
select Requester, ISO, someNum, BU
FROM
(select distinct Requester, ISO_ID as ISO, (ISO_ID - 5 + 50) AS 'someNum', BU,
CASE WHEN 'a' = 'b' then Requester
when 'b' = 'c' then BU
else convert(nvarchar(50), ISO_ID)
end AS SortByField
from LoanerHeader) AS dt
order by SortByField
答案 3 :(得分:0)
我刚才在博客上发表过这篇文章。有一个非常简单的代码示例和对问题的详尽解释。