@@ -441,7 +441,45 @@ COMMIT;
441441 <literal>c_films</literal> is currently positioned:
442442<programlisting>
443443UPDATE films SET kind = 'Dramatic' WHERE CURRENT OF c_films;
444- </programlisting></para>
444+ </programlisting>
445+ </para>
446+
447+ <para id="update-limit">
448+ Updates affecting many rows can have negative effects on system
449+ performance, such as table bloat, increased replica lag, and increased
450+ lock contention. In such situations it can make sense to perform the
451+ operation in smaller batches, possibly with a <command>VACUUM</command>
452+ operation on the table between batches. While there is
453+ no <literal>LIMIT</literal> clause for <command>UPDATE</command>, it is
454+ possible to get a similar effect through the use of
455+ a <link linkend="queries-with">Common Table Expression</link> and a
456+ self-join. With the standard <productname>PostgreSQL</productname>
457+ table access method, a self-join on the system
458+ column <link linkend="ddl-system-columns-ctid">ctid</link> is very
459+ efficient:
460+ <programlisting>
461+ WITH exceeded_max_retries AS (
462+ SELECT w.ctid FROM work_item AS w
463+ WHERE w.status = 'active' AND w.num_retries > 10
464+ ORDER BY w.retry_timestamp
465+ FOR UPDATE
466+ LIMIT 5000
467+ )
468+ UPDATE work_item SET status = 'failed'
469+ FROM exceeded_max_retries AS emr
470+ WHERE work_item.ctid = emr.ctid;
471+ </programlisting>
472+ This command will need to be repeated until no rows remain to be updated.
473+ Use of an <literal>ORDER BY</literal> clause allows the command to
474+ prioritize which rows will be updated; it can also prevent deadlock
475+ with other update operations if they use the same ordering.
476+ If lock contention is a concern, then <literal>SKIP LOCKED</literal>
477+ can be added to the <acronym>CTE</acronym> to prevent multiple commands
478+ from updating the same row. However, then a
479+ final <command>UPDATE</command> without <literal>SKIP LOCKED</literal>
480+ or <literal>LIMIT</literal> will be needed to ensure that no matching
481+ rows were overlooked.
482+ </para>
445483 </refsect1>
446484
447485 <refsect1>
0 commit comments