按照标准的SQL UNION / UNION ALL,如果没有外部ORDER BY子句,就不会保证任何特定的排序顺序--就像没有ORDER BY保证排序顺序的地方一样。
但是,Postgres对UNION ALL的普通情况使用一个“追加”步骤,因此第一个支腿的结果(即使在分区中没有排序)总是在下一个支路之前出现,等等。Postgres只是按给定的顺序追加来自每个支腿的结果。这与LIMIT条款特别相关:
SELECT 1 FROM tbl -- or any complex query
UNION ALL
SELECT 2
LIMIT 1显然,这不适用于UNION (没有ALL)。但除此之外,我从未见过Postgres按顺序返回,即从上述查询中返回“2”,而第一个SELECT也将返回行(S)。即使第一条腿非常昂贵,也不会。
我以前也有过基于这种行为的疑问。现在,我收到了一份索赔 Postgres可能会在这里不正常地返回行,但是没有实际的证据。
当前的Postgres手册在这个问题上有这样的说法:
UNION有效地将query2的结果附加到query1的结果(尽管不能保证这是实际返回行的顺序)。此外,它以与DISTINCT相同的方式从其结果中消除重复行,除非使用UNION ALL。
这还不清楚。引用的命令是否适用于SELECT子句的列表,或每个子句中的行,还是仅适用于返回的集合?此外,UNION ALL仅在第二句中提到,因此尚不清楚是否最重要的第一句应该适用于UNION ALL .
有人能给我们举一个例子吗?在哪里,行被按顺序返回,打破了UNION ALL子句的顺序?任何版本的Postgres。(即使最新版本将是最有趣的。)
如果不是这样的话,是否有理由相信这种情况可能会在未来的版本中发生改变?
ORDER BY不是眼前的问题。问题是多个UNION ALL子句是否以给定的顺序返回行(在LIMIT可以启动并停止进一步的分支执行之前)。
发布于 2022-09-13 11:11:39
最近在pgsql-docs邮件列表中也有类似的问题,
我试图理解--如果有的话--关于组合查询排序的保证(UNION/UNION ALL/.)。从这条消息1来看,UNION似乎确实保留了操作数查询的顺序,而UNION则没有(大概也不这样做)。文档2没有提到这一点,我建议添加一个说明来澄清这一点。
汤姆·莱恩(和其他人)回答说:
因为文件没有保证,所以没有保证。如果您想要有序输出,请使用ORDER。不,没有保证。只是UNION今天是这样工作的(保持子选择的顺序)--我甚至不确定,它可能不会在所有情况下都保持顺序,有不同的索引、分区或并行计划等等。无论如何,不能保证行为在未来不会因为计划者的改进而改变。是啊,那个。您今天可以为UNION获得一个并行化的计划:=#解释,分析select,select * from,union,all select* foo;查询计划PLAN(cost=0.00..208552.05 rows=5120008 width=244) (实际time=0.652..390.135 rows=5120000 loops=1)工人计划:2名工作人员启动:2 ->并行追加(cost=0.00..208552.05 rows=2133336 width=244) (实际time=0.021..228.848 rows=1706667 loops=3),->并行扫描对foo (cost=0.00..98942.68 rows=1066668 width=244) (实际time=0.453..78.084 rows=853333 loops=3) -> Parallel对foo foo_1 (cost=0.00..98942.68 loops=3)(实际on 20#)计划时间: 0.094 ms执行时间: 488.352 ms在简单的非并行化情况下,我们将执行第一个查询,然后再执行第二个查询,但是SQL并不保证这是真的,Postgres也是如此。
发布于 2022-09-13 21:54:15
已经确定,不能保证在下一个UNION ALL项的行之前返回来自第一个UNION ALL项的行。
干净的解决办法是不依赖从句的顺序。
对于上面所示的简单查询(不包括外部ORDER BY或JOIN),在将Parallel Append添加到Postgres 11并行计划之前,都会观察到序列。仅仅因为Append是唯一的计划选项--即(是)以这种方式实现的。
在目前的Postgres 15中,只要Parallel Append不参与,它仍能正常工作--这只发生在大场景中。您可以通过禁用该选项来确保这一点。手册:
enable_parallel_append可用于禁用此功能。
您可以在本地设置此选项,甚至只是为了使当前事务能够快速地用以下方法修补旧代码:
SET LOCAL enable_parallel_append = off;不要被这样一个临时的解决办法所困扰。最好正确地修复SQL代码。
弗拉迪奇评论道:
...这里的问题是,如果您使用的是
ORDER BY,您将失去“在接收到一定数量的行之前按特定顺序执行”的方便功能。除了按顺序执行多个查询(这可能效率较低,而且一般还需要动态SQL )之外,我看不到有任何其他方法可以做到这一点。
我能想到的下一个最好的事情是构建这个集合的PL/pgSQL函数。您可以使用GET DIAGNOSTICS检查每次查询后的结果行数,为下一个查询修改(减少) LIMIT,并在找到足够多时立即修改( RETURN )。可以在不使用动态SQL (因此不需要EXECUTE )的情况下完成,因为LIMIT接受参数。请参见:
更多的代码。更多的计划开销。但好的一面是:动态适应的LIMIT甚至可以为后续查询生成更好的查询计划。
另一个例子是,在循环中重复相同的查询,也不需要动态SQL:
为动态LIMIT编写代码示例,同时在循环中使用带有EXECUTE的动态SQL:
https://dba.stackexchange.com/questions/316818
复制相似问题