了解Sql Server查询 - ORDER BY子句中的CASE

时间:2011-11-09 19:14:34

标签: sql sql-server tsql

我正试图在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

再次有效!这怎么有意义呢?有人可以帮我把这个脑袋包起来吗?

4 个答案:

答案 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表达式,而不仅仅是组件部分。

如果RequesterBUISO_ID的数据类型不同,我们在此处观察到的不稳定行为可能会发生。从示例中可以明显看出ISO_ID是数字。我从行为中猜测,RequesterBU都不是数字,但它们都是相同类型(或兼容类型)。如果它们都是字符类型,则可能需要将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)

我刚才在博客上发表过这篇文章。有一个非常简单的代码示例和对问题的详尽解释。

SQL Server Case/When Data Type Problems