如何 select 更新来自 table A 的一行和来自 table B 的所有连接行?
How to select for update one row from table A and all joined rows from table B in Postgres?
我有一个 table,它是一个任务队列,每个任务都需要独占访问多个资源。我希望我的查询 select 不需要其他类似会话声明的资源的单个任务。
如果每个任务都必须处理一个资源,我会这样写:
select *
from tasks t
inner join resources r
on r.id = t.resource_id
order by t.submitted_ts
limit 1
for update skip locked
但是由于我有多个资源,所以我不得不将它们全部锁定:
select *
from tasks t
inner join task_details td
on t.id = td.task_id
inner join resources r
on r.id = td.resource_id
order by t.submitted_ts, t.id
limit ???
for update skip locked
我不能限制为 1,因为我需要锁定 resources
.
的所有连接行
在我看来,我应该尝试锁定 resources
的 所有 行,所以它一定不是 skip locked
,而是 [=18] =] 用于资源,skip locked
用于 tasks
。
LIMIT 子句适用于加入的 table.
而不是 table 使用子查询和它自己的 LIMIT。
SELECT
"table a"."учебный год",
"table b".семестр
FROM
(SELECT
"Учебный год"."учебный год"
FROM
"Учебный год"
ORDER BY
"Учебный год"."учебный год"
LIMIT 1) "table a"
INNER JOIN "Семестр" "table b" ON "table b"."учебный год" = "table a"."учебный год"
首先,我必须创建一个辅助函数来锁定或不锁定所有链接的行:
create or replace function try_lock_resources(p_task_id bigint)
returns boolean
language plpgsql
as $$
begin
perform *
from task_details td
join resources r
on td.resource_id = r.resource_id
where td.task_id = p_task_id
for update of r nowait;
return true;
exception when lock_not_available then
return false;
end;
$$;
然后我需要为每一行调用这个函数:
select *
from tasks
where processing_status = 'Unprocessed'
and try_lock_resources(task_id)
order by created_ts
limit 1
for update skip locked
在这个查询运行之后,只有返回的行及其关联的资源被锁定。我验证了来自另一个会话的相同查询 returns 第一个未处理的任务与第一个会话返回的资源没有共同资源。
P.S.: 原始答案使用了不同的查询(你不应该按原样使用):
with unprocessed_tasks as materialized (
select *
from tasks t
where processing_status = 'Unprocessed'
order by created_ts
)
select *
from unprocessed_tasks
where try_lock_resources(task_id)
limit 1
for update skip locked
此查询的问题在于以下可能(并且确实发生):
- 会话 A 运行 查询,锁定任务 X 并开始处理它
- 会话 B 开始 运行 查询,物化 CTE 首先是 运行,在其他任务中返回任务 X
- 会话A提交事务并释放所有锁
- 会话 B 完成 运行查询,锁定任务 X 并开始处理它
我有一个 table,它是一个任务队列,每个任务都需要独占访问多个资源。我希望我的查询 select 不需要其他类似会话声明的资源的单个任务。
如果每个任务都必须处理一个资源,我会这样写:
select *
from tasks t
inner join resources r
on r.id = t.resource_id
order by t.submitted_ts
limit 1
for update skip locked
但是由于我有多个资源,所以我不得不将它们全部锁定:
select *
from tasks t
inner join task_details td
on t.id = td.task_id
inner join resources r
on r.id = td.resource_id
order by t.submitted_ts, t.id
limit ???
for update skip locked
我不能限制为 1,因为我需要锁定 resources
.
在我看来,我应该尝试锁定 resources
的 所有 行,所以它一定不是 skip locked
,而是 [=18] =] 用于资源,skip locked
用于 tasks
。
LIMIT 子句适用于加入的 table.
而不是 table 使用子查询和它自己的 LIMIT。
SELECT
"table a"."учебный год",
"table b".семестр
FROM
(SELECT
"Учебный год"."учебный год"
FROM
"Учебный год"
ORDER BY
"Учебный год"."учебный год"
LIMIT 1) "table a"
INNER JOIN "Семестр" "table b" ON "table b"."учебный год" = "table a"."учебный год"
首先,我必须创建一个辅助函数来锁定或不锁定所有链接的行:
create or replace function try_lock_resources(p_task_id bigint)
returns boolean
language plpgsql
as $$
begin
perform *
from task_details td
join resources r
on td.resource_id = r.resource_id
where td.task_id = p_task_id
for update of r nowait;
return true;
exception when lock_not_available then
return false;
end;
$$;
然后我需要为每一行调用这个函数:
select *
from tasks
where processing_status = 'Unprocessed'
and try_lock_resources(task_id)
order by created_ts
limit 1
for update skip locked
在这个查询运行之后,只有返回的行及其关联的资源被锁定。我验证了来自另一个会话的相同查询 returns 第一个未处理的任务与第一个会话返回的资源没有共同资源。
P.S.: 原始答案使用了不同的查询(你不应该按原样使用):
with unprocessed_tasks as materialized (
select *
from tasks t
where processing_status = 'Unprocessed'
order by created_ts
)
select *
from unprocessed_tasks
where try_lock_resources(task_id)
limit 1
for update skip locked
此查询的问题在于以下可能(并且确实发生):
- 会话 A 运行 查询,锁定任务 X 并开始处理它
- 会话 B 开始 运行 查询,物化 CTE 首先是 运行,在其他任务中返回任务 X
- 会话A提交事务并释放所有锁
- 会话 B 完成 运行查询,锁定任务 X 并开始处理它