PostgreSQL 中的操作顺序是什么?

elk*_*uis 4 postgresql query execution-plan

我是一名数据库学生,我执行了以下查询来同时学习一些内容(LEFT/RIGHT JOIN、UNION、WHERE + RegEx)。令我困扰的是执行顺序。我有两个表,如下所示:

create table practicaleft(
    id smallint primary key,
    nombre varchar,
    cumple date
);

create table practicaright(
    id smallint primary key,
    apellido varchar,
    cumpleanios date
);
Run Code Online (Sandbox Code Playgroud)

然后,我插入一些随机数据:

INSERT INTO practicaleft VALUES
(1, 'John', CURRENT_DATE - 1),
(5, 'Alice', CURRENT_DATE - 5),
(3, 'Bob', CURRENT_DATE - 3),
(7, 'Eva', CURRENT_DATE - 7);

INSERT INTO practicaright VALUES
(5, 'Doe', CURRENT_DATE - 5),
(6, 'Smith', CURRENT_DATE - 6),
(3, 'Johnson', CURRENT_DATE - 3),
(4, 'Brown', CURRENT_DATE - 4);
Run Code Online (Sandbox Code Playgroud)

之后,我执行此查询:

select id, nombre
from practicaleft
where nombre similar to 'A%'
union
select pr.id, pr.apellido
from practicaright pr
where pr.id = 4 or pr.apellido ilike '_o%'
union all
select id, apellido
from practicaright 
where cumpleanios > current_date - 5;
Run Code Online (Sandbox Code Playgroud)

结果?干得好:

4   "Brown"
5   "Alice"
5   "Doe"
3   "Johnson"
3   "Johnson"
4   "Brown"
Run Code Online (Sandbox Code Playgroud)

TL;DR:该查询分为三部分,结果使用运算符 UNION ALL 合并。

现在问题来了。人们可能认为这是逐条指令执行的,因此顺序应该是:

5   "Alice"
5   "Doe"
3   "Johnson"
4   "Brown"
3   "Johnson"
4   "Brown"
Run Code Online (Sandbox Code Playgroud)

但这并没有发生。解决这个问题的唯一方法是添加一些随机字符串作为字段,如下所示:

select id, nombre, 'part1' as query_part
from practicaleft
where nombre similar to 'A%'
union
select pr.id, pr.apellido, 'part2' as query_part
from practicaright pr
where pr.id = 4 or pr.apellido ilike '_o%'
union all
select id, apellido, 'part3' as query_part
from practicaright 
where cumpleanios > current_date - 25;
Run Code Online (Sandbox Code Playgroud)

怎么了?我是否跳过了一些真正重要的 SQL 机制?

Lau*_*lbe 22

Jasen 的答案是正确的 - PostgreSQL 可以自由地以任何顺序返回行,除非您添加一个ORDER BY子句:

\n
(SELECT ... UNION SELECT ... UNION ALL SELECT ...) ORDER BY ...;\n
Run Code Online (Sandbox Code Playgroud)\n

但让我解释一下为什么 PostgreSQL 不按照您期望的顺序返回行。原因是第一个UNION不是UNION ALL。如果你在任何地方都使用过UNION ALL,PostgreSQL 将执行如下查询:

\n
(SELECT ... UNION SELECT ... UNION ALL SELECT ...) ORDER BY ...;\n
Run Code Online (Sandbox Code Playgroud)\n

也就是说,PostgreSQL 将执行三个查询并简单地附加结果,最终会得到您期望的排序。

\n

但你使用了union第一次,并union消除了重复。其执行方式如下:

\n
EXPLAIN (COSTS OFF)\nselect id, nombre\nfrom practicaleft\nwhere nombre similar to \'A%\'\nunion all\nselect pr.id, pr.apellido\nfrom practicaright pr\nwhere pr.id = 4 or pr.apellido ilike \'_o%\'\nunion all\nselect id, apellido\nfrom practicaright  \nwhere cumpleanios > current_date - 5;\n\n                            QUERY PLAN                            \n\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\n Append\n   ->  Seq Scan on practicaleft\n         Filter: ((nombre)::text ~ \'^(?:A.*)$\'::text)\n   ->  Seq Scan on practicaright pr\n         Filter: ((id = 4) OR ((apellido)::text ~~* \'_o%\'::text))\n   ->  Seq Scan on practicaright\n         Filter: (cumpleanios > (CURRENT_DATE - 5))\n(7 rows)\n
Run Code Online (Sandbox Code Playgroud)\n

PostgreSQL 使用哈希聚合来删除前两个分支中的重复项。结果行按照它们在哈希表中的顺序返回,这是相当随机的(好的哈希函数的行为是这样的)。

\n

  • 顺便说一句,依赖任何顺序作为 seq 扫描的输出是一个非常糟糕的主意。Postgres 堆存储是“无序的”,即使您控制如何将行插入表中,它们也可能在压缩过程中被打乱。如果您需要特定订单,请使用 ORDER BY。 (8认同)

Jas*_*sen 21

SQL 不保证结果排序,除非order by查询中有子句。

如果您不说“order by”,您的结果将以查询规划器和数据库引擎决定的最有效(或足够有效)的任何顺序出现。

并行表扫描是多个查询同时扫描同一个表的情况。但您的示例表可能太短了。

当我有一个union all想要按顺序排列的字符串时,我会向查询添加一个具有常量值的排序列。 1 as sort 2 as sort 3 as sort order by sort