是否可以在不知道要排序的字段的情况下每次 select 不同的行?
Is it possible to select different rows each time without knowing any fields to sort?
我有一个查询,比方说 X_QUERY
,这个查询可以是任何东西(我们不知道任何字段)并且可以有一亿行。我需要将结果分成最多 16 个块,因此每个块有 完全不同的 行 X_QUERY
。我们可以假设在此过程中 table 中没有任何更新。
我在 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)。
那么,有没有办法避免某种顺序?
如果没有,有没有办法每次都select 不同的行不知道要排序的字段?
您可以向名为 row_number() 的 X_QUERY 添加一个 window 函数。
这是我会寻找 SQL 光标 的罕见情况之一。 The manual on DECLARE
:
DECLARE
allows a user to create cursors, which can be used to retrieve a small number of rows at a time out of a larger query.
它不关心底层排序顺序和查询生成它的序列中的 returns 行。
使用 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:
Unless WITH HOLD
is specified, the cursor created by this command
can only be used within the current transaction. Thus, DECLARE
without WITH HOLD
is useless outside a transaction block: the cursor
would survive only to the completion of the statement. Therefore
PostgreSQL reports an error if such a command is used outside a
transaction block. Use BEGIN
and COMMIT
(or ROLLBACK
) to define
a transaction block.
如果您的查询是 string,您可以在 PL/pgSQL 函数或 DO
语句中使用动态 SQL 来创建一个SQL 游标 (WITH HOLD
?) 动态地,或使用 PL/pgSQL cursor 开始(相关但单独的实现)。
I have tried using CTID in postgres, but did not work.
那是因为 ctid
是一个标识元组物理位置的系统列。除非在 SELECT
列表中明确列出,否则它不会包含在查询结果中。所以它通常不在给定查询的结果中,并且在派生的 table 中不一定是唯一的。因此,ctid
可用于在没有并发写入的情况下遍历 table,但这对您的目的没有好处。
更多细节:
我有一个查询,比方说 X_QUERY
,这个查询可以是任何东西(我们不知道任何字段)并且可以有一亿行。我需要将结果分成最多 16 个块,因此每个块有 完全不同的 行 X_QUERY
。我们可以假设在此过程中 table 中没有任何更新。
我在 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)。
那么,有没有办法避免某种顺序?
如果没有,有没有办法每次都select 不同的行不知道要排序的字段?
您可以向名为 row_number() 的 X_QUERY 添加一个 window 函数。
这是我会寻找 SQL 光标 的罕见情况之一。 The manual on DECLARE
:
DECLARE
allows a user to create cursors, which can be used to retrieve a small number of rows at a time out of a larger query.
它不关心底层排序顺序和查询生成它的序列中的 returns 行。
使用 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:
Unless
WITH HOLD
is specified, the cursor created by this command can only be used within the current transaction. Thus,DECLARE
withoutWITH HOLD
is useless outside a transaction block: the cursor would survive only to the completion of the statement. Therefore PostgreSQL reports an error if such a command is used outside a transaction block. UseBEGIN
andCOMMIT
(orROLLBACK
) to define a transaction block.
如果您的查询是 string,您可以在 PL/pgSQL 函数或 DO
语句中使用动态 SQL 来创建一个SQL 游标 (WITH HOLD
?) 动态地,或使用 PL/pgSQL cursor 开始(相关但单独的实现)。
I have tried using CTID in postgres, but did not work.
那是因为 ctid
是一个标识元组物理位置的系统列。除非在 SELECT
列表中明确列出,否则它不会包含在查询结果中。所以它通常不在给定查询的结果中,并且在派生的 table 中不一定是唯一的。因此,ctid
可用于在没有并发写入的情况下遍历 table,但这对您的目的没有好处。
更多细节: