有条件的 order by 子句

Conditional order by clause

以下查询返回错误。帮我下错误和实现条件排序的方法。我试图在年级 >=8 时按年级和姓名排序,在年级 <8 时按年级和分数排序。

SELECT
    name, grade, marks
FROM
    students, grades
WHERE
    min_mark <= marks
    AND   marks <= max_mark
    AND   marks >= 70
UNION
SELECT
    TO_CHAR('NULL') AS name, grade, marks
FROM
    students, grades
WHERE
    min_mark <= marks
    AND   marks <= max_mark
    AND   marks <= 69
order by grade desc,(case when grade >= 8 
                     then  name 
                     when grade < 8 
                     then marks  
                     end );

ERROR:
order by grade desc,(case when grade >= 8
*
ERROR at line 18:
ORA-01785: ORDER BY item must be the number of a SELECT-list expression

SQL中CASE Expressions有2种形式。

CASE <expression> WHEN <v1> THEN <ret1> WHEN <v2> THEN <ret2> ... END

另一个是

CASE WHEN <comp1> THEN <ret1> WHEN <comp2> THEN <ret2> ... END

不同之处在于,在案例 1 中,您指定要比较一次的值或表达式并将其与其他值或表达式进行比较,而对于变体 2,您为每个 WHEN 指定一个比较表达式。

将表达式更改为

CASE WHEN grade >= 8 THEN name ELSE TO_CHAR(marks, '0999999999') END

我还将标记编号格式化为带有前导零的字符串,使其可以作为文本与名称一起排序。

这似乎是错误 5695629,它似乎已针对 10g 提出并且似乎尚未修复(截至 12cR2;我还没有 18 可以玩),这是不寻常的.

您可以通过在订购前将查询包装在外部 select 中来避免它:

select name, grade, marks
from
(
    SELECT
        name, grade, marks
    FROM
        students, grades
    WHERE
        min_mark <= marks
        AND   marks <= max_mark
        AND   marks >= 70
    UNION
    SELECT
        TO_CHAR('NULL') AS name, grade, marks
    FROM
        students, grades
    WHERE
        min_mark <= marks
        AND   marks <= max_mark
        AND   marks <= 69
)
order by grade desc,case when grade >= 1 
                     then  name 
                     when grade < 1 
                     then  marks
                     end ;

但由于 namemarks 是(大概)不同的数据类型 - 字符串和数字 - 而是会得到

ORA-00932: inconsistent datatypes: expected CHAR got NUMBER

您可以将 marks 转换为字符串,但如果这样做,则需要对其进行填充,以便按字母顺序对结果字符串进行排序仍然与数字顺序匹配 - 混乱但合理,因为标记可以(同样,大概- 如果是百分比?)最多只能是三位数:

select name, grade, marks
from
(
    ...
    <the main part of your query here as a subquery, as above>
    ...
)
order by grade desc,case when grade >= 8 
                     then  name 
                     when grade < 8 
                     then  to_char(marks, 'FM000')
                     end ;

db<>fiddle demo 使用一些通过 CTE 提供的虚拟数据。

如果标记可以超过三位数,则更改格式掩码以匹配可能的最大长度。


TO_CHAR('NULL') 部分也很奇怪,因为它将在这些行的名称列中为您提供文字字符串 "NULL"。由于您从字符串文字开始,因此 TO_CHAR() 部分毫无意义,只需直接使用 'NULL' AS name 即可。如果您真的希望它为空,那么您可以只使用 null AS name ,它将匹配联合的第一个分支中匹配列表达式的数据类型(并且也会选择它的别名)。您可以显式转换为字符串类型,例如cast(null as varchar2(20)) AS name不过好像没什么意义