Oracle SQL UNION ORDER BY

huangapple go评论68阅读模式
英文:

oracle sql union order by

问题

这是一个关于认证的问题,涉及到案例A和案例B。为什么案例B可以运行,但案例A不能运行?

案例A(不工作):

select col_a, col_b, 'b' from table_a
union
select col_a, col_b, 'a' from table_a
order by 'b';

案例B(工作):

select col_a, col_b, 'b' from table_a order by 'b';
英文:

This is a certification question case A and case B, why it works in case B, but doesn't work in case A

case A (not working)
select col_a, col_b, 'b' from table_a
union
select col_a, col_b, 'a' from table_a
order by 'b';

case B (working)
select col_a, col_b, 'b' from table_a order by 'b';

答案1

得分: 2

你正在使用字符串字面量 'a' 进行数据排序,这与任何其他字符串字面量一样,例如 'Littlefoot',两者都是无用的,但是是允许的:

SQL> select dname, 'a' from dept order by 'a';

DNAME '


ACCOUNTING a
RESEARCH a
SALES a
OPERATIONS a

SQL> select dname, 'a' from dept order by 'Littlefoot';

DNAME '


ACCOUNTING a
RESEARCH a
SALES a
OPERATIONS a

SQL>

order by 子句 - 用于 union 查询 - 适用于由该联合返回的整个数据集:

SQL> select dname, 'a' from dept where deptno <= 20
2 union
3 select dname, 'b' from dept where deptno > 20
4 order by dname;

DNAME '


ACCOUNTING a
OPERATIONS b
RESEARCH a
SALES b

SQL>

正如您已经知道的,按字符串字面量排序在这种情况下不起作用:

SQL> select dname, 'a' from dept where deptno <= 20
2 union
3 select dname, 'b' from dept where deptno > 20
4 order by 'Littlefoot';
order by 'Littlefoot'
*
ERROR at line 4:
ORA-01785: ORDER BY item must be the number of a SELECT-list expression

这个错误字面意思是:

> 您尝试执行一个包含 ORDER BY 子句的 SELECT 语句,该子句引用了不与您的 SELECT 列表中的有效列对应的列号。

正如您已经被告知的,您可以使用别名或列的位置,或者将该 union 用作子查询,然后再应用字符串字面量排序 - 在这种情况下,它将(再次)起作用:

SQL> select *
2 from (select dname, 'a' from dept where deptno <= 20
3 union
4 select dname, 'b' from dept where deptno > 20
5 )
6 order by 'Littlefoot';

DNAME '


ACCOUNTING a
OPERATIONS b
RESEARCH a
SALES b

SQL>

这是无用的(与以前一样),但是允许的。

人们希望像这样的东西能够起作用(通过隐式数据转换,Oracle 将字符串 &#39;2&#39;(字符串)转换为 2(数字)并按位置排序),但是 - 不起作用:

SQL> select dname, 'a' from dept where deptno <= 20
2 union
3 select dname, 'b' from dept where deptno > 20
4 order by '2';
order by '2'
*
ERROR at line 4:
ORA-01785: ORDER BY item must be the number of a SELECT-list expression

位置排序当然可以使用:

SQL> select dname, 'a' from dept where deptno <= 20
2 union
3 select dname, 'b' from dept where deptno > 20
4 order by 2;

DNAME '


RESEARCH a
ACCOUNTING a
SALES b
OPERATIONS b

SQL>

因此,我怀疑union 引起了问题。也许问题在于 UNION 返回了 DISTINCT 数据集,并且在其中 - ORDER BY 子句不能引用不是 SELECT 列列表的列,字面意义上如此。

英文:

You're sorting data by a string literal, &#39;a&#39; which is as good as any other string literal, such as &#39;Littlefoot&#39;: both are useless, but are allowed:

SQL&gt; select dname, &#39;a&#39; from dept order by &#39;a&#39;;

DNAME          &#39;
-------------- -
ACCOUNTING     a
RESEARCH       a
SALES          a
OPERATIONS     a

SQL&gt; select dname, &#39;a&#39; from dept order by &#39;Littlefoot&#39;;

DNAME          &#39;
-------------- -
ACCOUNTING     a
RESEARCH       a
SALES          a
OPERATIONS     a

SQL&gt;

order by clause - applied to unioned queries - works for the whole data set returned by that union:

SQL&gt; select dname, &#39;a&#39; from dept where deptno &lt;= 20
  2  union
  3  select dname, &#39;b&#39; from dept where deptno &gt; 20
  4  order by dname;

DNAME          &#39;
-------------- -
ACCOUNTING     a
OPERATIONS     b
RESEARCH       a
SALES          b

SQL&gt;

As you already know, sorting by string literal won't work in this case:

SQL&gt; select dname, &#39;a&#39; from dept where deptno &lt;= 20
  2  union
  3  select dname, &#39;b&#39; from dept where deptno &gt; 20
  4  order by &#39;Littlefoot&#39;;
order by &#39;Littlefoot&#39;
         *
ERROR at line 4:
ORA-01785: ORDER BY item must be the number of a SELECT-list expression


SQL&gt;

That error literally means:

> You tried to execute a SELECT statement that included a ORDER BY clause that referenced a column number that did not correspond to a valid column in your SELECT list.

As you were already told, you could use an alias or column's position, or use that union as a subquery and then apply sorting by a string literal - in that case, it'll (again) work:

SQL&gt; select *
  2  from (select dname, &#39;a&#39; from dept where deptno &lt;= 20
  3        union
  4        select dname, &#39;b&#39; from dept where deptno &gt; 20
  5       )
  6  order by &#39;Littlefoot&#39;;

DNAME          &#39;
-------------- -
ACCOUNTING     a
OPERATIONS     b
RESEARCH       a
SALES          b

SQL&gt;

That's useless (as it was before), but it is allowed.

One could hope that something like this would work ("hope" by means of implicit data conversion, where Oracle would convert &#39;2&#39; (a string) to 2 (a number) and sort by position), but - nope:

SQL&gt; select dname, &#39;a&#39; from dept where deptno &lt;= 20
  2  union
  3  select dname, &#39;b&#39; from dept where deptno &gt; 20
  4  order by &#39;2&#39;;
order by &#39;2&#39;
         *
ERROR at line 4:
ORA-01785: ORDER BY item must be the number of a SELECT-list expression

Positional sorting works, of course:

SQL&gt; select dname, &#39;a&#39; from dept where deptno &lt;= 20
  2  union
  3  select dname, &#39;b&#39; from dept where deptno &gt; 20
  4  order by 2;

DNAME          &#39;
-------------- -
RESEARCH       a
ACCOUNTING     a
SALES          b
OPERATIONS     b

SQL&gt;

Therefore, I suspect that it is the union that causes problems. Maybe it is about the fact that UNION returns DISTINCT data set, and - with it - ORDER BY clause can't refer columns that aren't part of the SELECT column list, literally.

答案2

得分: 1

&#39;b&#39; 是一个字符串文字,而不是列的标识符(因为列的标识符是用引号括起来的 &quot;&#39;B&#39;&quot;)。

您可以使用列编号:

select col_a, col_b, &#39;b&#39; from table_a union
select col_a, col_b, &#39;a&#39; from table_a
order by 3;

或者使用带引号的标识符并将文字转换为大写:

select col_a, col_b, &#39;b&#39; from table_a union
select col_a, col_b, &#39;a&#39; from table_a
order by &quot;&#39;B&#39;&quot;;

或者为列设置一个别名并使用它:

select col_a, col_b, &#39;b&#39; AS b from table_a union
select col_a, col_b, &#39;a&#39; from table_a
order by b;

请参阅 SELECT 文档

集合运算符: UNION, UNION ALL, INTERSECT, MINUS

[...]

order_by_clause

使用 ORDER BY 子句对语句返回的行进行排序。如果没有 order_by_clause,则不能保证多次执行相同查询会以相同的顺序检索行。

[...]

expr

expr 根据其对 expr 的值对行进行排序。表达式基于 select 列表中的列或 FROM 子句中的表、视图或物化视图中的列。

[...]

ORDER BY 子句的限制

ORDER BY 子句有以下限制:

  • 如果在此语句中指定了 DISTINCT 操作符,则此子句不能引用列,除非它们出现在 select 列表中。

您正在使用 UNION(而不是 UNION ALL),它隐含地应用了 DISTINCT 操作符,因此限制适用于您“引用列,除非它们出现在 select 列表中”。在 select 列表中,列的标识符是用引号括起来的 &quot;&#39;B&#39;&quot;,而不是字符串文字 &#39;b&#39;(也不是字符串文字 &#39;B&#39;),您需要将其视为列标识符(或按照 select 列表中的位置或使用别名引用它)。

注意:即使不考虑隐含的 DISTINCT 附加限制,UNION 查询的基本限制是“该表达式基于 select 列表中的列或 FROM 子句中的表、视图或物化视图中的列”,而文字 &#39;b&#39; 不是 select 列表中的列,也不来自 FROM 子句的对象,因此如果使用 UNION ALL 而不是 UNION,它将是有效的。

英文:

&#39;b&#39; is a string literal and not an identifier for the column (as the identifier for the column is the quoted identifier &quot;&#39;B&#39;&quot;).

You can either use the column number:

select col_a, col_b, &#39;b&#39; from table_a union
select col_a, col_b, &#39;a&#39; from table_a
order by 3;

or use a quoted identifier and convert the literal to upper-case:

select col_a, col_b, &#39;b&#39; from table_a union
select col_a, col_b, &#39;a&#39; from table_a
order by &quot;&#39;B&#39;&quot;;

or give the column an alias and use that:

select col_a, col_b, &#39;b&#39; AS b from table_a union
select col_a, col_b, &#39;a&#39; from table_a
order by b;

See the SELECT documentation

> ### Set Operators: UNION, UNION ALL, INTERSECT, MINUS
>
> [...]
>
> #### order_by_clause
>
> Use the ORDER BY clause to order rows returned by the statement. Without an order_by_clause, no guarantee exists that the same query executed more than once will retrieve rows in the same order.
>
> [...]
>
> expr
>
> expr orders rows based on their value for expr. The expression is based on columns in the select list or columns in the tables, views, or materialized views in the FROM clause.
>
> [...]
>
> ### Restrictions on the ORDER BY Clause
>
> The following restrictions apply to the ORDER BY clause:
>
> - If you have specified the DISTINCT operator in this statement, then this clause cannot refer to columns unless they appear in the select list.

You are using UNION (and not UNION ALL) which is implicitly applying the DISTINCT operator so the restriction applies that you "refer to columns unless they appear in the select list." The identifier for the column in the select list is the quoted identifier &quot;&#39;B&#39;&quot; and not the string literal &#39;b&#39; (and also not the string literal &#39;B&#39;) and you need to refer to it as that (or by the position in the select list or by an alias).

Note: Even without applying the additional restriction from the implicit DISTINCT, the base restriction for UNION queries is "The expression is based on columns in the select list or columns in the tables, views, or materialized views in the FROM clause" and the literal &#39;b&#39; is not a column in the select list or from an object of the FROM clause so it would be valid if you used UNION ALL instead of UNION.

huangapple
  • 本文由 发表于 2023年1月9日 16:51:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/75054911.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定