【问题标题】:Conditional order by clause有条件的 order by 子句
【发布时间】:2018-11-28 17:04:31
【问题描述】:

以下查询返回错误。帮我下错误和条件命令的实现方式。我试图在等级 >=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 );

错误:
按等级排序,(等级> = 8时的情况
*
第 18 行出现错误:
ORA-01785: ORDER BY 项目必须是 SELECT 列表表达式的编号

【问题讨论】:

  • 试过但没用。错误仍然存​​在。
  • 试试case when grade &gt;= 8 then name else marks end。原始 SQL 是否包含星号 ** ?它不应该。
  • 它也不应该在一个分支中有一个字符串值而在另一个分支中有一个数字;为此将标记转换为字符串是没有意义的。 TO_CHAR('NULL') 也没有,虽然真的;那应该是cast(null as varchar2(20)) 还是什么?
  • 乍一看,我认为这只是 order by 范围的错误,但再次检查后,您的查询似乎有几个问题。使用文本格式的示例数据告诉我们您希望看到的确切输出。
  • 请也向我们展示错误信息。

标签: oracle join sql-order-by union


【解决方案1】:

SQL 中有两种形式的CASE Expressions

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

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

【讨论】:

  • ERROR: order by grade desc,(CASE WHEN Grade >= 8 THEN name ELSE TO_CHAR(marks, '0999999999') END) * 第 18 行出现错误:ORA-01785: ORDER BY item must be SELECT 列表表达式的编号
  • 我们有一个特殊情况,因为我们有 UNION 选择。因此,我会接受@Alex Poole 的建议,并用 out 选择包装联合选择。
  • 如果您能提供查询,将会非常有帮助。 Oracle 新手 :)
  • 感谢您的支持
  • 亚历克斯的回答有一个这样的查询。
【解决方案2】:

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

您可以通过在订购前将查询包装在外部选择中来避免这种情况:

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:不一致的数据类型:预期的 CHAR 得到了 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 但似乎没什么意义。

【讨论】:

  • 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 order by grade desc跨度>
  • 这是您最初的错误。您是否将查询移动到子查询中?
  • @ShanmukhaReddy - 我添加了一个演示链接,展示了这项工作,使用了一些虚构的数据。
猜你喜欢
  • 2013-01-13
  • 2014-06-19
  • 1970-01-01
  • 1970-01-01
  • 2021-01-21
  • 1970-01-01
  • 2017-07-10
  • 2019-05-31
  • 1970-01-01
相关资源
最近更新 更多