PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Postgres pg_try_advisory_lock bloque tous les enregistrements

Vous appelez pg_try_advisory_lock() une fois par ligne dans l'ensemble entier qui est analysé (dans le cadre du filtrage qui se produit dans le where clause), alors que vous ne voulez qu'elle soit appelée une fois par ligne dans la table1 renvoyée par la requête.

Vous pouvez essayer d'utiliser une sous-requête ou un CTE à la place :

with rows as (
SELECT a.id
FROM table1 a
JOIN table2 b ON a.table1_id = b.id
WHERE table2.id = 1
)
select rows.*
from rows
where pg_try_advisory_lock('table1'::regclass::integer, rows.id);

Mais ne vous fiez pas non plus à cela pour qu'il fonctionne comme prévu :Postgres devrait être tenté de le réécrire comme votre requête initiale.

Une autre possibilité est celle-ci, puisque le select une partie d'une instruction est évaluée très tard dans la requête :

with rows as (
SELECT a.id,
       pg_try_advisory_lock('table1'::regclass::integer, a.id) as locked
FROM table1 a
JOIN table2 b ON a.table1_id = b.id
WHERE table2.id = 1
)
select rows.id
from rows
where rows.locked;

Le vrai problème en pratique est que pg_try_advisory_lock() est quelque chose que vous trouverez normalement dans l'app land ou dans une fonction, plutôt que dans une requête comme vous le faites. En parlant de cela, selon ce que vous faites, êtes-vous sûr de ne pas utiliser select … for update ?

Concernant votre mise à jour :

Oui. En raison de la limit 1 , il va trouver une correspondance et s'arrêter immédiatement. Ce qui se passe probablement, cependant, c'est qu'il n'évalue pas le where clause dans le même ordre en fonction de vos requêtes. SQL n'offre aucune garantie que le a <> 0 partie en a <> 0 and b / a > c est évalué en premier. Appliqué à votre cas, il n'offre aucune garantie que le verrou consultatif soit obtenu après la ligne de a est jointe à b.