Wywołujesz pg_try_advisory_lock() raz na wiersz w całym zestawie, który jest skanowany (w ramach filtrowania, które występuje w where
klauzula), podczas gdy chcesz, aby była wywoływana tylko raz na wiersz w tabeli 1 zwróconej przez zapytanie.
Zamiast tego możesz spróbować użyć podzapytania lub CTE:
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);
Ale nie należy polegać na tym, aby koniecznie działać zgodnie z oczekiwaniami:Postgres powinien ulec pokusie, aby przepisać go tak, jak początkowe zapytanie.
Inna możliwość jest taka, ponieważ select
część instrukcji jest oceniana bardzo późno w zapytaniu:
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;
Prawdziwym problemem w praktyce jest to, że pg_try_advisory_lock()
jest czymś, co zwykle można znaleźć w aplikacji lub w funkcji, a nie w zapytaniu, jak to robisz. Skoro o tym mowa, w zależności od tego, co robisz, na pewno nie powinieneś używać select … for update
?
Jeśli chodzi o twoją aktualizację:
TAk. Ze względu na limit 1
, znajdzie dopasowanie i natychmiast się zatrzyma. Prawdopodobnie jednak dzieje się tak, że nie ocenia where
klauzula w tej samej kolejności w zależności od zapytań. SQL nie gwarantuje, że a <> 0
część w a <> 0 and b / a > c
jest oceniany jako pierwszy. Zastosowany w Twoim przypadku nie gwarantuje, że blokada doradcza zostanie uzyskana po wiersz od a jest połączony z b.