sql >> Base de Datos >  >> RDS >> PostgreSQL

Postgres pg_try_advisory_lock bloquea todos los registros

Está llamando a pg_try_advisory_lock() una vez por fila en todo el conjunto que se escanea (como parte del filtrado que ocurre en el where cláusula), mientras que solo desea que se llame una vez por fila en la tabla 1 devuelta por la consulta.

Podría intentar usar una subconsulta o un CTE en su lugar:

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

Pero tampoco confíe en que eso funcione necesariamente como se esperaba:Postgres debería tener la tentación de reescribirlo de la forma en que fue su consulta inicial.

Otra posibilidad es esta, ya que el select parte de una declaración se evalúa muy tarde en la consulta:

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;

El verdadero problema en la práctica es que pg_try_advisory_lock() es algo que normalmente encontraría en la aplicación o en una función, en lugar de una consulta como la que está haciendo. Hablando de eso, dependiendo de lo que estés haciendo, ¿estás seguro de que no deberías usar select … for update? ?

Con respecto a su actualización:

Sí. Debido al limit 1 , encontrará una coincidencia e inmediatamente se detendrá. Sin embargo, lo que probablemente esté sucediendo es que no está evaluando el where cláusula en el mismo orden dependiendo de sus consultas. SQL no ofrece ninguna garantía de que a <> 0 parte en a <> 0 and b / a > c se evalúa primero. Aplicado a su caso, no ofrece ninguna garantía de que el bloqueo de aviso se obtenga después la fila de a se une con b.