Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vl2BF-0061a1-1A for pgsql-general@arkaria.postgresql.org; Wed, 28 Jan 2026 09:49:46 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vl2BC-000e9P-2u for pgsql-general@arkaria.postgresql.org; Wed, 28 Jan 2026 09:49:43 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vl2BC-000e9E-0N for pgsql-general@lists.postgresql.org; Wed, 28 Jan 2026 09:49:42 +0000 Received: from sonic305-19.consmr.mail.ir2.yahoo.com ([77.238.177.81]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vl2B8-002iKt-0c for pgsql-general@lists.postgresql.org; Wed, 28 Jan 2026 09:49:41 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.fr; s=s2048; t=1769593775; bh=yswKeerBN1u3EU/FnvPU3QSwvfp8zXnFL2J1Nt8xYHY=; h=Date:From:To:Subject:References:From:Subject:Reply-To; b=ipYpTzasFL5JtB7ww/GpsKpFOShIG8razxLBcXq8nTRXiqB4Ki7mjmSlLPUXlHStuHxN8OEyo6fs+xKhdokt4o60uqaMBXTbjGVr+xBVd3bYTcAGoyWMQIpaDecgtbWwEECLwrE6btCould4UiGRJUzZTAu3sTJoLzglzarYmNmKbh9Rz9FejT2+h4dYvsRGTEWm6ECBlEDqrG03fw9oVWWGCTpmwnsGvBleFMuIsE2u3evLl67ZshVfs96HzyLEuo7hDXlQLV4dtjOLI7rffZ1athmAmoHWa8hkYjUEWX7WF32GLdTkEhTPip3sRAIJWEDaGQKcOPenX2D5i2MU8g== X-SONIC-DKIM-SIGN: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1769593775; bh=5qzVmdD+sf91cRmvpQ/0yIdWp6RGrkZRFx4QM4VGDHO=; h=X-Sonic-MF:Date:From:To:Subject:From:Subject; b=eJmQ/k0foflDZZmvZ8wfirSCPAOW+K/Z9Sz7XrHIQXX7JFLPCNb2gZ33iA/VxmB7/M5/0iqWIZQNkzw+nqp2hpVvE3GjadSlo2m1MtqXgQ+QYAUlEusiwrihLmY7oG/PYK2U2JsoDfizxj5k+lo3jat2xPD4fWuQXqXEwn+2Me0nUc6utOuBRQY7/clx96UUfiBDCwU1wHcnv2UDUVU4GA/uo7CI3CjMTi9WgUyYKcemu7X7crh+egH1VMmxkdDSQmwQjLV7aF416St6ml8Ph17p/GHDreT40ZHHWIRxIHW+flOHNhtQFoW77EwMG3pD2LX5Q2uKNk2k8UvAES74hw== X-YMail-OSG: GUdtCvQVM1nzo9lq7PQYNCvfM59mV6mDnCEEJKOC1f5adHIc4PX2ibyrjk3vCPz kV60PiKxEixgtG6WjJDEyUM42kk5OexmCZbuYUYPXwQjBwJLhEHJztapIV0_Yx3zxTiPnglCfC_x Yc26IkJqbmNqQA2LD7O4CUMKUeXpa2kJCC20F4Ve0KqmtgpjPXkDxBwmhkaN7vPDSsmBUYEzZAvD LCD7pWCKCv4yxRR6fMlBHfzjXPdNX_aru6gN.zeq5y8se5CiN3gAqAkOLj.o8dmRxAmPMvdfk2b0 fM.pzgfwZE9dkqKYiGA1po.ddKFzimRU8U2UR0FTrJI9vEnolBlH24_7s.CpiqnP3aKkLO53kyGA KIl_k3jtFmdSyHmlKF0QjtHDroiIUWEcsSB.R9oc3jHnSwuS4enFaI.IxyIVpuyE6CYEdsMF8PS1 ZRKtHd_XSsr_3YZctiX54T6RanHHhAWlsQzSOjRR5flekbmnJSwEaJnUxn75Z3FylNPldI6bvgBr fFQyo2MaqfCwRvcRE7etwjhIkHxi9qQht6GvR4nZGOrnCjUtiQXhh6Ij7jWandWzEbrw0in32pkq xy4p4xf1EEuC1eeJDKkbZ1yV5HMGENC9BBz5hROaataZzbdO2Ds1cJoWShz3GmQF4EB799YTlHVj XPAvMhsAuG3B4d_iqB_vTWVTZ9Hh9oilpRS5RMLjF8cbiBpegqJRNxse83huj8SAyTeolAxj3Nn0 TnEc0uiXcf105xw4_UeGKOGWkuxdXvvBINR9SStSVzgArvlBbHlMHhhFL76XUnJY6jmJWrv0xxc1 8scXrIQu0g3mfWcGr00bWn_5_qfHCm.9jWfI9_57xY1NqYuVeMs.btG5owd7AnD90qfh2aKvYI4_ V3O1LcabsihrXN6asl7F9z67UH10YGLNBlFUGQwzMDsShIUeQNevbOHrGw9MQNkHmnJZimTIMWgj HyxevQtQ7XmJyFWDGxoL228fAfIb30l6SEUXpelQl8fTMJmqhMJ2F2KkKEsx02N1klLeeBZbcGIo V5lK.S8IdnyW9Mz1WCb5hTeu2m0E0Stxv5cjisDz4mIZseK5fSmtPMXj0d1bY5e4D_5A9bAXpWda Kw47ONgwgJG.UIfSml3GJht85QTmzgdXjUqn.eXbiBc5B_ESjzOtkGMOhATEgWu4pVRAGwJDXMHg 2.cU3DKTYl1a1GYexlnYyqlXrt1GHQJZGynzB0Kzcr3sIxGjYdae3aahcSgTP2TL04u2jSrCFm8p .YUwilUO9pDipFlDZTAWg3RXW6hwoiGbUuYYFXzOSp1loaWXHRnta87mqtVgFWTqTwaLh.WJbDD6 snaUdKRDVl8roAu0h89Sc_YRmdY2BP1XO9mCKP6vWmyN3JiEmhPbiXD4CZfZE36wl5VZBGf2zL3W kCT5kZE_tDJnEO_kxKRT49BRl2uYNWhHg4mSYBWDoH9YIHKlB_7Dn9DHiDU30kno6UZf3i5D7ZxF Qqm_qAudRbFAaWMudxj1N5x2WfzBQzuDTYJtQXlXLN9CtP3NPG.cjIfj9qk1hsCbNvwTczv06Uqx qNNVn03q0bb7bMdqr7u3UCShF3uXSFKyHtWDr61C_ZVm6Aa_CsfDegSd4FjP0BD99NOUwRICrkiv K2yboNXimcUj3GY62wVEp3XtHj5Mb.7IfLd7lTIbFC.dVnnZJ_v7qo6FiYbqT0rMsUNzYOK8lEfr Y_.f44BkDJG1iU3FHCVzns9F4dfqP7Sd4UwJDqVzCDE1BoUZMuM1eg_OS7o5_R_X.zkIaVcB5aBk uCo_TjRjvOM4NzwqjVmDQU.19xsefBmwHxHhVEYEnNLj2BdxtAnsvtWMsYnoFiAD3djuGK.ho1nM PONB_HGH98CAumvuL0CArADAJFqY0EfVU32LeONzRPDPQuhGcnY0ZfSSEAjkMegA15Oxo1oTLsJo Pq6tHpH12S_c2.3AKpm.lXsp9rlx6xL9YrneakOocA3.sWFDGExznA489YWzJ7q7gtTBzqB6Y0JP n8tqmc1AlmWO9VWE1ywWEltqXBFcB7f5Ittei3jzZghXgIUZsQAzy4QfkSsfxZHm8EwNofW4AfaL RoqIc7SMn7pptiv04imptqMVP_Ws2uu9RDSiVC3LjHdPRQpbQskPNRPMGSCH5yMO.ORUCdkPTl2k r.wP1F6XjfiH7Dg3KQh8oTNrw1lvp7J0ejfDqrf45pgiDsL5HWltcOqsNK.UknXOZ_OiuBPNv0X0 rQCkBwJDNov6N61r5UnQ1Kr.3jEjV0w7cWuR1y0o8 X-Sonic-MF: X-Sonic-ID: 4a2aade6-b074-42d8-91d1-d12712f9dac3 Received: from sonic.gate.mail.ne1.yahoo.com by sonic305.consmr.mail.ir2.yahoo.com with HTTP; Wed, 28 Jan 2026 09:49:35 +0000 Date: Wed, 28 Jan 2026 09:48:49 +0000 (UTC) From: QUINCEROT Emmanuel To: "pgsql-general@lists.postgresql.org" Message-ID: <316192095.8713932.1769593729038@mail.yahoo.com> Subject: Efficient batched iteration over hash/list partitioned tables MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_Part_8713931_1961726620.1769593729037" References: <316192095.8713932.1769593729038.ref@mail.yahoo.com> X-Mailer: WebService/1.1.24987 YMailNovation Content-Length: 5254 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk ------=_Part_8713931_1961726620.1769593729037 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Hello dear community, Hash partitioning is useful for very large datasets when the main access pa= tterns are on the partition key. However, we sometimes need to backfill thi= s data in an online fashion, which presents a challenge. When backfilling a non-partitioned table, we can iterate over the primary k= ey in batches until all rows are processed. This works well because the pri= mary key is unique and ordered. The query looks like this: =C2=A0 =C2=A0 SELECT *=C2=A0 =C2=A0 FROM table=C2=A0 =C2=A0 WHERE pk_col > = :last_pk_value=C2=A0 =C2=A0 ORDER BY pk_col=C2=A0 =C2=A0 LIMIT batch_size; However, when working with hash-partitioned tables, this strategy is ineffi= cient because the primary key is not ordered across partitions. The query p= lanner must retrieve the first N rows from each partition, sort them global= ly, and then return only enough rows to fill the batch size. A workaround is to process each partition independently, but this has drawb= acks:- It requires additional logic to track progress across multiple parti= tions- The logic differs between partitioned and non-partitioned tables, ma= king the client partitioning-aware **Proposed solution:** Could we make ordering by `tableoid, [primary key columns]` work efficientl= y for partitioned tables? In other words, something like this: =C2=A0 =C2=A0 SELECT tableoid, *=C2=A0 =C2=A0 FROM table=C2=A0 =C2=A0 WHERE= (tableoid, pk_col) > (:last_tableoid, :last_pk_value)=C2=A0 =C2=A0 ORDER B= Y tableoid, pk_col=C2=A0 =C2=A0 LIMIT batch_size; Currently, from PG 15 to PG 18, the planner doesn't handle ordering by tabl= eoid efficiently: !ALL! rows are fetched from each partition, then appended= , sorted, and limited. Could we optimize the planner to handle `ORDER BY tableoid` efficiently in = this context? Note: This problem primarily concerns hash and list partitioning, as range = partitioning can be batched efficiently by ordering on the partition key it= self. Many thanks, Emmanuel ------=_Part_8713931_1961726620.1769593729037 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
= Hello dear community,

Hash partitioning is useful for ver= y large datasets when the main access patterns are on the partition key. Ho= wever, we sometimes need to backfill this data in an online fashion, which = presents a challenge.

When backfilling a non-parti= tioned table, we can iterate over the primary key in batches until all rows= are processed. This works well because the primary key is unique and order= ed.

The query looks like this:

    SELECT *
    FROM table
&= nbsp;   WHERE pk_col > :last_pk_value
    ORDER= BY pk_col
    LIMIT batch_size;


However, when working with hash-partitioned tables, this = strategy is inefficient because the primary key is not ordered across parti= tions. The query planner must retrieve the first N rows from each partition= , sort them globally, and then return only enough rows to fill the batch si= ze.

A workaround is to process each partition inde= pendently, but this has drawbacks:
- It requires additional logic= to track progress across multiple partitions
- The logic differs= between partitioned and non-partitioned tables, making the client partitio= ning-aware

**Proposed solution:**

Could we make ordering by `tableoid, [primary key columns]` work e= fficiently for partitioned tables?

In other words, something like this:

    SELECT tableoid, *
    FROM table
    WHERE (tableoid, pk_col) > (:last_tableoid, :last= _pk_value)
    ORDER BY tableoid, pk_col
&nbs= p;   LIMIT batch_size;

Currently, from PG 15 = to PG 18, the planner doesn't handle ordering by tableoid efficiently: !ALL= ! rows are fetched from each partition, then appended, sorted, and limited.=

Could we optimize the planner to handle `ORDER BY= tableoid` efficiently in this context?

Note: This= problem primarily concerns hash and list partitioning, as range partitioni= ng can be batched efficiently by ordering on the partition key itself.

Many thanks,
<= div dir=3D"ltr" data-setdir=3D"false">
Emmanuel
------=_Part_8713931_1961726620.1769593729037--