kbk*_*bkb 10 postgresql order-by
在 SELECT 语句中,如果未指定 ORDER BY 子句,则不能保证返回行的顺序。这适用于“普通”表。
对于使用 WITH 表达式 (CTE) 生成的有序表也是如此吗?函数返回的有序表?我认为不是。这是文档中明确说明的地方吗?
具体来说,我可以假设这个(更有效的)查询:
WITH ordered AS ( SELECT * FROM table1 ORDER BY col1 )
SELECT sum(col2) result FROM
generate_series(0,50) nr,
LATERAL (SELECT * FROM ordered LIMIT 100 OFFSET nr*100) a
GROUP BY nr ORDER BY nr;
Run Code Online (Sandbox Code Playgroud)
将等同于这个查询:
SELECT sum(col2) result FROM
generate_series(0,50) nr,
LATERAL (SELECT * FROM table1 ORDER BY col1 LIMIT 100 OFFSET nr*100) a
GROUP BY nr ORDER BY nr;
Run Code Online (Sandbox Code Playgroud)
对于函数:
如果我有这样的功能:
CREATE FUNCTION do_sort(name text[]) RETURNS TABLE(name text) AS $$
SELECT name.name FROM unnest($1) name ORDER BY name.name ASC;
$$ LANGUAGE SQL IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)
我可以制作这样的包装函数并假设数组总是正确排序吗?
CREATE FUNCTION do_sort_returns_array(name text[]) RETURNS text[] AS $$
SELECT array_agg(name) FROM do_sort($1);
$$ LANGUAGE SQL IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)
ype*_*eᵀᴹ 11
在SELECT语句中,如果ORDER BY未指定子句,则不能保证返回行的顺序。这适用于所有表、简单或复杂的查询。
现在,以此为基础,我们应该考虑 Postgres 以一种特殊的方式实现了 CTE。它们总是被物化(参见:PostgreSQL 的 CTE 是优化栅栏)。
这并不意味着您应该依赖于此。当使用 CTE 时,就像在第一个查询中一样,ORDER BY在引用 CTE 时也应该添加一个,因为 CTE 的ORDER BY内部可能会被删除(我不是说它总是会被删除,因为我的测试表明排序是执行。但它可能会在未来的优化器更改中删除,因为ORDER BY没有LIMIT是多余的):
WITH ordered AS ( SELECT * FROM table1 ORDER BY col1 ) -- redundant ORDER BY
SELECT sum(col2) result FROM
generate_series(0,50) nr,
LATERAL (SELECT * FROM ordered ORDER BY col1 -- ORDER BY added
LIMIT 100 OFFSET nr*100) a
GROUP BY nr ORDER BY nr;
Run Code Online (Sandbox Code Playgroud)
更新,来自 Craig Ringer,在我在他上面链接的博客文章中发表评论后:
就像很多其他情况一样 - 现在 PostgreSQL 将始终按照 CTE 输出的顺序返回行,但从技术上讲,您不应该依赖它。谁知道未来的哪些功能会改变这种情况?
不幸的是,如果您在外部查询中添加另一个,IIRC 也不够聪明,无法识别出这些行的顺序是正确的
ORDER BY。(尚未测试,但很确定)。因此,以“正确”的方式进行操作不一定是免费的。
因此,如果您希望按特定顺序对结果进行排序,请ORDER BY在最终的SELECT. 您现在可以离开它(并且您可能会获得一些轻微的性能提升),但不能保证这在未来的版本中不会改变。
显式使用的另一个原因ORDER BY是您可能并不总是维护您现在编写的代码。另一位开发人员可能会尝试在没有 CTE 的情况下使用派生表或LATERAL连接重新组织查询。他们需要知道您的查询依赖于 CTE 提供的顺序,因此需要在最终版本中使用SELECT或在某处进行注释或记录。
现在,另一种编写查询的方法是LIMIT在 CTE 中添加 a并使用ROW_NUMBER():
WITH ordered AS
( SELECT col2, (ROW_NUMBER() OVER (ORDER BY col1) - 1) / 100 AS nr
FROM table1
ORDER BY col1 LIMIT 5100 )
SELECT n.nr, sum(o.col2) AS result
FROM generate_series(0, 50) AS n (nr)
LEFT JOIN ordered AS o
ON n.nr = o.nr
GROUP BY n.nr
ORDER BY n.nr ;
Run Code Online (Sandbox Code Playgroud)
这样,CTE 将只为您想要的 5100 行实现,而不是为表的所有(可能是数百万)行实现。如果有索引就更好了(col1, col2)。您甚至可以删除generate_series()和/或删除 CTE:
SELECT o.nr, sum(o.col2) AS result
FROM
( SELECT col2, (ROW_NUMBER() OVER (ORDER BY col1) - 1) / 100 AS nr
FROM table1
ORDER BY col1 LIMIT 5100
) AS o
GROUP BY o.nr
ORDER BY o.nr ;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2983 次 |
| 最近记录: |