単純なテーブルを想定しています。
\d+ task
Column | Type | Modifiers | Storage |
-------------------+--------------------------+---------------------------------------------------+----------+
id | integer | not null default nextval('task_id_seq'::regclass) | plain |
parent_task_id | integer | | plain |
cinema_id | integer | not null | plain |
venue_id | integer | | plain |
movie_id | integer | | plain |
event_id | integer | | plain |
result | json | | extended |
context | json | | extended |
guide | json | | extended |
started_at | timestamp with time zone | | plain |
ended_at | timestamp with time zone | | plain |
created_at | timestamp with time zone | not null default now() | plain |
updated_at | timestamp with time zone | not null default now() | plain |
Indexes:
"task_pkey" PRIMARY KEY, btree (id)
"public_task_cinema_id1_idx" btree (cinema_id)
"public_task_event_id4_idx" btree (event_id)
"public_task_movie_id3_idx" btree (movie_id)
"public_task_parent_task_id0_idx" btree (parent_task_id)
"public_task_started_at6_idx" btree (started_at)
"public_task_venue_id2_idx" btree (venue_id)
Foreign-key constraints:
"task_parent_task_id_fkey" FOREIGN KEY (parent_task_id) REFERENCES task(id) ON DELETE CASCADE
"task_cinema_id_fkey" FOREIGN KEY (cinema_id) REFERENCES cinema(id) ON DELETE CASCADE
"task_event_id_fkey" FOREIGN KEY (event_id) REFERENCES event(id) ON DELETE CASCADE
"task_movie_id_fkey" FOREIGN KEY (movie_id) REFERENCES movie(id) ON DELETE CASCADE
"task_venue_id_fkey" FOREIGN KEY (venue_id) REFERENCES venue(id) ON DELETE CASCADE
Referenced by:
TABLE "task" CONSTRAINT "task_parent_task_id_fkey" FOREIGN KEY (parent_task_id) REFERENCES task(id) ON DELETE CASCADE
Triggers:
update_task_updated_at BEFORE UPDATE ON task FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column()
親タスクがないか、親タスクが完了しているタスクを選択する単純なビューを想定します。
CREATE VIEW outstanding_task AS
SELECT
t1.*
FROM task t1
LEFT JOIN task t2 ON t2.id = t1.parent_task_id
WHERE
t2.id IS NULL OR
t2.result IS NOT NULL;
このビューをFOR UPDATE
で使用しようとしています:
SELECT *
FROM outstanding_task
LIMIT 1
FOR UPDATE SKIP LOCKED;
ただし、 postgres v10.4では、次のエラーが発生します。
エラー:FOR UPDATEは外部結合のNULL可能側には適用できません
FOR UPDATE
を使用して作成されたビューでLEFT JOIN
を使用する方法
注1:質問はビューに固有のものです。ビューがなければ、FOR UPDATE OF t1
を使用できます。
私は現在postgresが機能していないのでテストされていませんが、コンセプトは明確でなければなりません:
ベーステーブルに対して別の内部結合でビューを使用してから、ベーステーブルを更新してください。
select *
from task as t
inner join outstanding_tasks as o on t.id=o.id
for update of task
行をめったにロックしない場合はあまり意味がありませんが、FOR UPDATE OF
を含むビューを定義することは技術的に可能です。
私の例では、task
には列が1つしかなく、単純な結合条件を使用していますが、全体的なメカニズムは同じです。
CREATE OR REPLACE VIEW task_fu AS
SELECT t1.id
FROM task AS t1
LEFT JOIN task AS t2 ON t1.id * 10 = t2.id
FOR UPDATE OF t1 SKIP LOCKED;
次に、1つのセッションで
BEGIN;
BEGIN
SELECT * FROM task_fu LIMIT 5;
id
────
1
2
3
4
5
別の間に、これはあなたが得るものです:
SELECT * FROM task_fu LIMIT 5;
id
────
6
7
8
9
10
クエリでORDER BY
を使用した場合(SELECT * FROM task_fu ORDER BY id LIMIT 5;
)、2番目のセッションが0行を返すことが重要になる場合があります。潜在的なWHERE
句が同じ行のセットをロックする場合も同様です。