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
,为什么不同?
在任何合理的数据库中进行任何操作后都不会保留顺序。如果您希望结果集按特定顺序排列,请使用 ORDER BY
。期间.
这 尤其是 在 UNION
之后是正确的。 UNION
删除重复项并且该操作很可能会更改行的顺序。
基本上,您的查询一开始就不正确。使用 UNION ALL
,而不是 UNION
,否则您会错误地删除重复条目。 (没什么好说的,trail不能在相同的邮件之间来回切换。)
UNION ALL
returns 值的 Postgres 实现如所附的序列 - 只要你 不 添加 ORDER BY
在结束或对结果做任何其他事情。
请注意,每个 SELECT
returns 行都以任意顺序排列,除非附加了 ORDER BY
。表中没有自然顺序。
not对于UNION
也是如此,它必须处理所有行以删除可能的重复项。有多种方法可以确定重复项,生成的行顺序取决于所选算法并且依赖于实现并且完全不可靠 - 除非再次附加 ORDER BY
。
所以改用:
SELECT * FROM iter1
UNION ALL -- union all!
SELECT * FROM iter2;
要获得可靠的排序顺序,并“模拟增长记录”,您可以这样跟踪级别:
WITH RECURSIVE all_emails AS (
SELECT *, <b>1 AS lvl</b>
FROM audit_trail
WHERE old_email = 'harold_gim@yahoo.com'
UNION ALL -- union all!
SELECT t.*, <b>a.lvl + 1</b>
FROM all_emails a
JOIN audit_trail t ON t.old_email = a.new_email
)
TABLE all_emails
<b>ORDER BY lvl</b>;
旁白:如果 old_email
未以某种方式定义 UNIQUE
,您可以获得多条轨迹。您将需要一个唯一的列(或列的组合)来保持它的明确性。如果所有其他方法都失败了,您可以(滥用)使用内部元组 ID ctid
来区分路径。但是您应该使用自己的专栏。 (在 fiddle 中添加了示例。)
- In-order sequence generation
考虑:
- How to return records in correct order in PostgreSQL
如果在所有联合语句之后都可以通过,则保留顺序,如下所示:
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"
代码如下:
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
,为什么不同?
在任何合理的数据库中进行任何操作后都不会保留顺序。如果您希望结果集按特定顺序排列,请使用 ORDER BY
。期间.
这 尤其是 在 UNION
之后是正确的。 UNION
删除重复项并且该操作很可能会更改行的顺序。
基本上,您的查询一开始就不正确。使用 UNION ALL
,而不是 ,否则您会错误地删除重复条目。 (没什么好说的,trail不能在相同的邮件之间来回切换。)UNION
UNION ALL
returns 值的 Postgres 实现如所附的序列 - 只要你 不 添加 ORDER BY
在结束或对结果做任何其他事情。
请注意,每个 SELECT
returns 行都以任意顺序排列,除非附加了 ORDER BY
。表中没有自然顺序。
not对于UNION
也是如此,它必须处理所有行以删除可能的重复项。有多种方法可以确定重复项,生成的行顺序取决于所选算法并且依赖于实现并且完全不可靠 - 除非再次附加 ORDER BY
。
所以改用:
SELECT * FROM iter1
UNION ALL -- union all!
SELECT * FROM iter2;
要获得可靠的排序顺序,并“模拟增长记录”,您可以这样跟踪级别:
WITH RECURSIVE all_emails AS (
SELECT *, <b>1 AS lvl</b>
FROM audit_trail
WHERE old_email = 'harold_gim@yahoo.com'
UNION ALL -- union all!
SELECT t.*, <b>a.lvl + 1</b>
FROM all_emails a
JOIN audit_trail t ON t.old_email = a.new_email
)
TABLE all_emails
<b>ORDER BY lvl</b>;
旁白:如果 old_email
未以某种方式定义 UNIQUE
,您可以获得多条轨迹。您将需要一个唯一的列(或列的组合)来保持它的明确性。如果所有其他方法都失败了,您可以(滥用)使用内部元组 ID ctid
来区分路径。但是您应该使用自己的专栏。 (在 fiddle 中添加了示例。)
- In-order sequence generation
考虑:
- How to return records in correct order in PostgreSQL
如果在所有联合语句之后都可以通过,则保留顺序,如下所示:
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"