Psql 查询:联合不保留来自 cte 的行的顺序

Psql query : Union is not preserving the order of rows coming from cte

我有这个问题:

with cte1 as (select salary from employees order by salary desc), cte2 as (select 850) select * from cte1 union select * from cte2;

cte1 按工资列排序,但 cte2 不是。我希望将 cte2 的结果附加到 cte1 的结果,同时保留 cte1 结果的顺序。但这并没有发生。

如果我 运行 上面的查询没有与第二个 cte 并集,结果将按预期顺序显示,但当有并集时,顺序会混乱。

没有联合的查询:

with cte1 as (select salary from employees order by salary desc), cte2 as (select 850) select * from cte1;
 salary 
--------
   1000
    900
    800
    700
    600
    500

联合:

with cte1 as (select salary from employees order by salary desc), cte2 as (select 850) select * from cte1 union select * from cte2;
 salary 
--------
    850
    800
    700
    900
    500
    600
   1000

谁能解释一下为什么会这样?

documentation 很明显 union 不保证行的顺序:

UNION effectively appends the result of query2 to the result of query1 (although there is no guarantee that this is the order in which the rows are actually returned).

如果你希望结果是有序的,那么在外层查询中使用order by;对于您的用例,这需要跟踪每行来自哪个 cte

with 
    cte1 (salary, which) as (select salary, 1 from employees), 
    cte2 (salary, which) as (select 850, 2) 
select salary from cte1 
union all
select salary from cte2
order by which, salary desc;

注意我把union改成了union all;您似乎不想对行进行重复数据删除(前者确实如此),因此后者足够好(并且效率更高)。

order by 不是 select 的一部分。 select给出一个table,这是一个集合,没有顺序。你可以在最后做一个 order by ,显然也可以在联合中做,但是例如你不能加入一个有序集(至少在 sybase 中不行)。

UNION 子句减少重复行。这种减少可以通过两种技术来完成:

  1. 使用 table - 散列不保留顺序
  2. 使用排序并删除相等的后续行 - 这种排序打破了原始顺序。

如果您想阻止此行为,请使用 UNION ALL 子句,这不会减少冗余行(重复)。