Чтобы прояснить мою проблему, я создал тестовую таблицу со следующей структурой.
CREATE TABLE public.test_large (
id int4 NOT NULL, -- Primary Key
grp int4 NULL, -- Group ID
descr text NULL,
CONSTRAINT pk_test_large PRIMARY KEY (id)
);
CREATE INDEX ix_tl_grp ON public.test_large USING btree (grp);
Значения в столбце grp назначаются таким образом, чтобы в каждой записи было не более 1000 записей с одинаковыми значениями.
Всего 1 000 000 записей.
Следующий оператор обновления является упрощенной версией реального оператора:
update
test_large d
set
descr = s.descr
from
(
select
ctid,
*
from
test_large
where
grp = 1) s
where
s.ctid = d.ctid;
План выполнения:
Update on test_large d (cost=139792.82..144832.96 rows=1000 width=53)
-> Merge Join (cost=139792.82..144832.96 rows=1000 width=53)
Merge Cond: (d.ctid = test_large.ctid)
-> Sort (cost=136816.68..139329.25 rows=1005029 width=14)
Sort Key: d.ctid
-> Seq Scan on test_large d (cost=0.00..19443.29 rows=1005029 width=14)
-> Sort (cost=2976.14..2978.64 rows=1000 width=39)
Sort Key: test_large.ctid
-> Bitmap Heap Scan on test_large (cost=20.18..2926.31 rows=1000 width=39)
Recheck Cond: (grp = 1)
-> Bitmap Index Scan on ix_tl_grp (cost=0.00..19.93 rows=1000 width=0)
Index Cond: (grp = 1)
Как видите, для обновления выполняется Seq Scan всех 1 000 000 строк, чтобы затем выполнить объединение слиянием вместо прямого доступа к строке через CTID.
Есть ли способ повлиять на это? Конечно, я мог бы использовать первичный ключ в условии WHERE, но тогда потребовался бы дополнительный доступ к индексу.