是否可以在不知道要排序的任何字段的情况下每次选择不同的行?

问题描述

我有一个查询,比方说 X_QUERY,这个查询可以是任何东西(我们不知道任何字段)并且可以有 1 亿行。我需要将结果分成最多 16 个块,因此每个块都有 完全不同的 X_QUERY 行。我们可以假设在此过程中任何表都没有更新。

我在 oracle 中使用 ROWID 作为 order by 子句解决了同样的问题,所以我尝试在 Postgres 中使用 CTID,但没有奏效。

SELECT * 
FROM (X_QUERY) as origin ORDER BY ctid -- <-- ctid does not exist here
limit 6250000 offset 0; -- <-- next offsets should be 6250000,12500000 etc.

理想情况下,我想避免 order by 的额外成本,但我没有找到任何其他方法(至少使用 Oracle)。

那么,有没有办法避免某种顺序?
如果没有,有没有办法在不知道要排序的任何字段的情况下每次选择不同的行?

解决方法

这是我查看 SQL 游标 的少数情况之一。 The manual on DECLARE

DECLARE 允许用户创建游标,该游标可用于从较大的查询中一次检索少量行。

它不关心底层的排序顺序,并按照查询产生的顺序返回行。

使用 FETCH 获取下一组行。

示例:

BEGIN;
DECLARE x_cursor CURSOR FOR <X_QUERY>;  -- your query string here

FETCH 6250000 FROM x_cursor;
FETCH 6250000 FROM x_cursor;
FETCH 6250000 FROM x_cursor;
-- repeat until no more rows;

-- CLOSE x_cursor;  -- optional
COMMIT;
-- or ROLLBACK;  -- does not make a difference in this case

除非您声明 corsor WITH HOLD,否则必须在事务中完成所有操作。 The manual

除非指定了WITH HOLD,否则此命令创建的游标 只能在当前事务中使用。因此,DECLARE 没有 WITH HOLD 在事务块之外是无用的:游标 只会在语句完成后继续存在。所以 如果这样的命令在外部使用,PostgreSQL 会报告错误 交易区块。使用 BEGINCOMMIT(或 ROLLBACK)来定义 一个交易块。

如果您的查询是字符串,您可以在 PL/pgSQL 函数或 DO 语句中使用动态 SQL 来动态创建 SQL 游标 (WITH HOLD?) ,或使用 PL/pgSQL cursor 开头(相关但独立的实现)。


我曾尝试在 postgres 中使用 CTID,但没有奏效。

这是因为 ctid 是标识元组物理位置的系统列。除非在 SELECT 列表中明确列出,否则它不会包含在查询结果中。因此,它通常不在给定查询的结果中,并且在派生表中不一定是唯一的。因此,ctid 可用于在没有并发写入的情况下遍历表,但这不符合您的目的。
更多血腥细节:

,

您可以向 X_QUERY 添加一个名为 row_number() 的窗口函数。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...