PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Postgres pg_try_advisory_lock blockiert alle Datensätze

Sie rufen pg_try_advisory_lock() einmal pro Zeile im gesamten Satz auf, der gescannt wird (als Teil der Filterung, die in where auftritt -Klausel), wohingegen sie nur einmal pro Zeile in table1 aufgerufen werden soll, die von der Abfrage zurückgegeben wird.

Sie könnten stattdessen versuchen, eine Unterabfrage oder einen CTE zu verwenden:

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);

Aber verlassen Sie sich auch nicht darauf, dass es unbedingt wie erwartet funktioniert:Postgres sollte versucht sein, es so umzuschreiben, wie Ihre ursprüngliche Abfrage war.

Eine andere Möglichkeit ist dies, da die select Teil einer Anweisung wird sehr spät in der Abfrage ausgewertet:

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;

Das eigentliche Problem in der Praxis ist, dass pg_try_advisory_lock() ist etwas, das Sie normalerweise im App-Land oder in einer Funktion finden würden, und nicht in einer Abfrage, wie Sie es tun. Apropos, je nachdem, was Sie tun, sind Sie sicher, dass Sie select … for update nicht verwenden sollten ?

Zu Ihrem Update:

Ja. Aufgrund des limit 1 , es wird eine Übereinstimmung finden und sofort anhalten. Was jedoch wahrscheinlich passiert, ist, dass es das where nicht auswertet Klausel in der gleichen Reihenfolge, abhängig von Ihren Fragen. SQL bietet keine Garantie dafür, dass a <> 0 Teil in a <> 0 and b / a > c wird zuerst ausgewertet. Auf Ihren Fall angewendet, bietet es keine Garantie dafür, dass die Hinweissperre nach erhalten wird die Reihe von a wird mit b verbunden.