在PostgreSQL中UNION之后是否保留了顺序?

qed*_*qed 6 sql postgresql union sql-order-by

这是代码:

CREATE TABLE audit_trail (
      old_email TEXT NOT NULL,
      new_email TEXT NOT NULL
);

INSERT INTO audit_trail(old_email, new_email)
  VALUES ('harold_gim@yahoo.com', 'hgimenez@hotmail.com'),
         ('hgimenez@hotmail.com', 'harold.gimenez@gmail.com'),
         ('harold.gimenez@gmail.com', 'harold@heroku.com'),
         ('foo@bar.com', 'bar@baz.com'),
         ('bar@baz.com', 'barbaz@gmail.com');


WITH RECURSIVE all_emails AS (
  SELECT  old_email, new_email
    FROM audit_trail
    WHERE old_email = 'harold_gim@yahoo.com'
  UNION
  SELECT at.old_email, at.new_email
    FROM audit_trail at
    JOIN all_emails a
      ON (at.old_email = a.new_email)
)
SELECT * FROM all_emails;

        old_email         |        new_email
--------------------------+--------------------------
 harold_gim@yahoo.com     | hgimenez@hotmail.com
 hgimenez@hotmail.com     | harold.gimenez@gmail.com
 harold.gimenez@gmail.com | harold@heroku.com
(3 rows)

select old_email, new_email into iter1
from audit_trail where old_email = 'harold_gim@yahoo.com';
select * from iter1;
--       old_email       |      new_email
-- ----------------------+----------------------
--  harold_gim@yahoo.com | hgimenez@hotmail.com
-- (1 row)

select a.old_email, a.new_email into iter2
from audit_trail a join iter1 b on (a.old_email = b.new_email);
select * from iter2;
--       old_email       |        new_email
-- ----------------------+--------------------------
--  hgimenez@hotmail.com | harold.gimenez@gmail.com
-- (1 row)

select * from iter1 union select * from iter2;
--       old_email       |        new_email
-- ----------------------+--------------------------
--  hgimenez@hotmail.com | harold.gimenez@gmail.com
--  harold_gim@yahoo.com | hgimenez@hotmail.com
-- (2 rows)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,递归代码以正确的顺序给出结果,但非递归代码却没有.
他们都用union,为什么不同?

Erw*_*ter 10

基本上,您的查询开头是不正确的.使用UNION ALL,不是,UNION否则您将错误地删除重复的条目.(没有什么可说的,跟踪不能在同一封电子邮件之间来回切换.)

Postgres实现UNION ALL保证在序列中返回值 - 只要您不在ORDER BY末尾添加或对结果执行任何其他操作.
但请注意,SELECT除非ORDER BY附加,否则每个都以任意顺序返回行.表中没有自然顺序.

同样是正确的UNION,它必须处理所有的行删除可能的重复.有多种方法可以确定重复,行的结果顺序取决于所选择的算法,并且取决于实现并且完全不可靠 - 除非再次ORDER BY附加.

所以改用:

SELECT * FROM iter1
UNION ALL  -- union all!
SELECT * FROM iter2;
Run Code Online (Sandbox Code Playgroud)

要获得可靠的排序顺序,并"模拟增长记录",您可以跟踪这样的级别:

WITH RECURSIVE all_emails AS (
   SELECT  *, 1 AS lvl
   FROM    audit_trail
   WHERE   old_email = 'harold_gim@yahoo.com'

   UNION ALL  -- union all!
   SELECT t.*, a.lvl + 1
   FROM   all_emails  a
   JOIN   audit_trail t ON t.old_email = a.new_email
)
TABLE  all_emails
ORDER  BY lvl;
Run Code Online (Sandbox Code Playgroud)

SQL小提琴.

旁白:如果old_email没有UNIQUE以某种方式定义,您可以获得多个路径.您需要一个唯一的列(或列的组合)来保持其明确性.如果所有其他方法都失败了,你可以(ab-)使用内部元组ID ctid来说明路径分开.但你应该使用自己的专栏.(在小提琴中添加了例子.)

考虑:

  • 你有引用 Postgres 保证用 union all 维护秩序吗?这些文档似乎并不能保证这一点,我需要知道我是否可以依赖它。 (3认同)

Gor*_*off 5

在任何合理的数据库中进行任何操作后,顺序都不会保留。如果您希望结果集按特定顺序排列,请使用ORDER BY。时期。

在 . 之后尤其如此UNIONUNION删除重复项,并且该操作很可能会改变行的顺序。