web-dev-qa-db-ja.com

LEFT JOINを使用して作成されたビューでFOR UPDATEを使用する方法

単純なテーブルを想定しています。

\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を使用できます。

2
Gajus

私は現在postgresが機能していないのでテストされていませんが、コンセプトは明確でなければなりません:

ベーステーブルに対して別の内部結合でビューを使用してから、ベーステーブルを更新してください。

select *
from task as t
inner join outstanding_tasks as o on t.id=o.id
for update of task
1
til_b

行をめったにロックしない場合はあまり意味がありませんが、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句が同じ行のセットをロックする場合も同様です。

1
dezso