Use bloqueio em nível de linha explícito em subconsultas ordenadas em todas as consultas concorrentes .
(
SELECT
não compete com bloqueios de gravação.) DELETE
DELETE FROM table_name t
USING (
SELECT id_A, id_B
FROM table_name
WHERE id_A = ANY(array_of_id_A)
AND id_B = ANY(array_of_id_B)
ORDER BY id_A, id_B
FOR UPDATE
) del
WHERE t.id_A = del.id_A
AND t.id_B = del.id_B;
UPDATE
UPDATE table_name t
SET val_1 = 'some value'
, val_2 = 'some value'
FROM (
SELECT id_A, id_B
FROM table_name
WHERE id_A = ANY(array_of_id_A)
AND id_B = ANY(array_of_id_B)
ORDER BY id_A, id_B
FOR NO KEY UPDATE -- Postgres 9.3+
-- FOR UPDATE -- for older versions or updates on key columns
) upd
WHERE t.id_A = upd.id_A
AND t.id_B = upd.id_B;
Dessa forma, as linhas são bloqueadas em ordem consistente, conforme recomendado no manual.
Supondo que
id_A
, id_B
nunca são atualizados, mesmo as complicações raras do caso de canto, como detalhadas na caixa "Cuidado" no manual, não são possíveis. Embora não esteja atualizando as colunas de chave, você pode usar o modo de bloqueio mais fraco
FOR NO KEY UPDATE
. Requer Postgres 9.3 ou posterior. O outro (lento e com certeza) é usar o Nível de Isolamento Serializável para transações concorrentes. Você teria que se preparar para falhas de serialização e, nesse caso, seria necessário repetir o comando.