簡體   English   中英

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

[英]Is order preserved after UNION in PostgreSQL?

這是代碼:

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)

正如您所看到的,遞歸代碼以正確的順序給出結果,但非遞歸代碼沒有。
他們都使用union ,為什么不同?

基本上,您的查詢一開始就不正確。 使用UNION ALL ,而不是 UNION 否則您會錯誤地刪除重復條目。 (沒有什么可以說路徑不能在相同的電子郵件之間來回切換。)

UNION ALL的 Postgres 實現會按照附加的序列返回值 - 只要您不在最后添加ORDER BY或對結果執行任何其他操作。
但請注意,除非附加ORDER BY否則每個SELECT都會以任意順序返回行。 表中沒有自然順序。

UNION情況並非如此,它必須處理所有行以刪除可能的重復項。 有多種方法可以確定重復項,結果行的順序取決於所選算法並且依賴於實現並且完全不可靠 - 除非再次附加ORDER BY

所以改用:

SELECT * FROM iter1
UNION ALL  -- union all!
SELECT * FROM iter2;

要獲得可靠的排序順序並“模擬增長記錄”,您可以像這樣跟蹤級別:

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;

db<> 在這里擺弄
舊的sqlfiddle

old_email :如果old_email沒有以某種方式定義為UNIQUE ,您可以獲得多個路徑。 您需要一個唯一的列(或列的組合)以保持其明確性。 如果所有其他方法都失敗了,您可以 (ab-) 使用內部元組 ID ctidctid路徑。 但是您應該使用自己的列。 (在小提琴中添加了示例。)

考慮:

在任何合理的數據庫中進行任何操作后都不會保留排序。 如果您希望按特定順序顯示結果集,請使用ORDER BY 時期。

UNION之后尤其如此。 UNION刪除重復項,並且該操作很可能會更改行的順序。

如果在所有 unions 語句之后都可以通過,則保留順序,如下所示:

select "ClassName","SectionName","Students","OrderNo" from table
UNION 
select '----TOTAL----' as "ClassName",'----' as "SectionName",sum("Total Students"),9999 as "OrderNo" from table
ORDER BY "OrderNo"

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM