From 298c812838491408e6910f7535067ea147abe5fc Mon Sep 17 00:00:00 2001 From: Corey Huinker Date: Sat, 3 Feb 2024 14:38:50 -0500 Subject: [PATCH v2] Documentation: Show alternatives to LIMIT on UPDATE and DELETE Show examples of how to simulate UPDATE or DELETE with a LIMIT clause. These examples also serve to show the existence and utility of ctid self-joins. --- doc/src/sgml/ref/delete.sgml | 18 +++++++++++++++++ doc/src/sgml/ref/update.sgml | 38 +++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/ref/delete.sgml b/doc/src/sgml/ref/delete.sgml index 1b81b4e7d7..21aae30e91 100644 --- a/doc/src/sgml/ref/delete.sgml +++ b/doc/src/sgml/ref/delete.sgml @@ -234,6 +234,24 @@ DELETE FROM films In some cases the join style is easier to write or faster to execute than the sub-select style. + + While there is no LIMIT clause for + DELETE, it is possible to get a similar effect + using the method for UPDATE operations described + in greater detail here. + +WITH delete_batch AS ( + SELECT l.ctid + FROM user_logs AS l + WHERE l.status = 'archived' + ORDER BY l.creation_date + LIMIT 10000 + FOR UPDATE +) +DELETE FROM user_logs AS ul +USING delete_branch AS del +WHERE ul.ctid = del.ctid; + diff --git a/doc/src/sgml/ref/update.sgml b/doc/src/sgml/ref/update.sgml index 2ab24b0523..49e0dc29de 100644 --- a/doc/src/sgml/ref/update.sgml +++ b/doc/src/sgml/ref/update.sgml @@ -434,7 +434,6 @@ UPDATE wines SET stock = stock + 24 WHERE winename = 'Chateau Lafite 2003'; COMMIT; - Change the kind column of the table films in the row on which the cursor @@ -442,6 +441,43 @@ COMMIT; UPDATE films SET kind = 'Dramatic' WHERE CURRENT OF c_films; + + Updates affecting many rows can have negative effects on system performance, + such as table bloat, increased replica lag, increased lock contention, + and possible failure of the operation due to a deadlock. In such situations + it can make sense to perform the operation in smaller batches. Performing a + VACUUM operation on the table in between batches can help + reduce table bloat. The + SQL standard does + not define a LIMIT clause for UPDATE + operations, but it is possible get a similar effect through the use of a + Common Table Expression and an + efficient self-join via the system column + ctid: + +WITH exceeded_max_retries AS ( + SELECT w.ctid + FROM work_item AS w + WHERE w.status = 'active' + AND w.num_retries > 10 + ORDER BY w.retry_timestamp + FOR UPDATE + LIMIT 5000 +) +UPDATE work_item +SET status = 'failed' +FROM exceeded_max_retries AS emr +WHERE work_item.ctid = emr.ctid + + If lock contention is a concern, then SKIP LOCKED can + be added to the CTE. However, one final + UPDATE without SKIP LOCKED or + LIMIT will be needed to ensure that no matching rows + were overlooked. The use of an ORDER BY clause allows + the command to prioritize which rows will be locked and updated. This can + also reduce contention with other update operations if they use the same + ordering. + -- 2.43.0