再帰クエリ(!!)にWITH
句を使用する方法は理解していますが、その一般的な使用法/能力を理解するのに問題があります。
たとえば、次のクエリは、タイムスタンプで最初のレコードのIDを返すサブクエリを使用してIDが決定される1つのレコードを更新します。
update global.prospect psp
set status=status||'*'
where psp.psp_id=(
select p2.psp_id
from global.prospect p2
where p2.status='new' or p2.status='reset'
order by p2.request_ts
limit 1 )
returning psp.*;
これは、比較的醜いサブクエリの代わりにWITH
ラッパーを使用するのに適した候補でしょうか?もしそうなら、なぜですか?
関連するテーブルへの同時書き込みアクセスが存在する可能性がある場合、上記の次のクエリには競合状態があります。考えてみましょう:
あなたの例can CTE(共通テーブル式)を使用しますが、サブクエリで実行できなかったものは何もありません。
WITH x AS (
SELECT psp_id
FROM global.prospect
WHERE status IN ('new', 'reset')
ORDER BY request_ts
LIMIT 1
)
UPDATE global.prospect psp
SET status = status || '*'
FROM x
WHERE psp.psp_id = x.psp_id
RETURNING psp.*;
ところで、返される行はpdatedバージョンになります。
返された行を別のテーブルに挿入したい場合、ここでWITH句が不可欠になります。
WITH x AS (
SELECT psp_id
FROM global.prospect
WHERE status IN ('new', 'reset')
ORDER BY request_ts
LIMIT 1
), y AS (
UPDATE global.prospect psp
SET status = status || '*'
FROM x
WHERE psp.psp_id = x.psp_id
RETURNING psp.*
)
INSERT INTO z
SELECT *
FROM y
CTEを使用したデータ変更クエリは、PostgreSQL9.1以降で可能です。
読む 優れたマニュアルの詳細 。
WITH
を使用すると、SELECT
クエリで使用する「一時テーブル」を定義できます。たとえば、最近、次のようなクエリを作成して、2つのセット間の変化を計算しました。
_-- Let o be the set of old things, and n be the set of new things.
WITH o AS (SELECT * FROM things(OLD)),
n AS (SELECT * FROM things(NEW))
-- Select both the set of things whose value changed,
-- and the set of things in the old set but not in the new set.
SELECT o.key, n.value
FROM o
LEFT JOIN n ON o.key = n.key
WHERE o.value IS DISTINCT FROM n.value
UNION ALL
-- Select the set of things in the new set but not in the old set.
SELECT n.key, n.value
FROM o
RIGHT JOIN n ON o.key = n.key
WHERE o.key IS NULL;
_
上部に「テーブル」o
とn
を定義することで、式things(OLD)
とthings(NEW)
の繰り返しを回避することができました。
確かに、_UNION ALL
_を使用して_FULL JOIN
_を削除することはできますが、特定のケースではそれを実行できませんでした。
私があなたの質問を正しく理解しているなら、それはこれをします:
ステータスが「new」または「reset」であるglobal.prospectの最も古い行を検索します。
ステータスにアスタリスクを追加してマークします
行を返します(status
への微調整を含む)。
WITH
があなたのケースで何かを単純化するとは思わない。ただし、FROM
句を使用する方が少しエレガントかもしれません。
_update global.prospect psp
set status = status || '*'
from ( select psp_id
from global.prospect
where status = 'new' or status = 'reset'
order by request_ts
limit 1
) p2
where psp.psp_id = p2.psp_id
returning psp.*;
_
テストされていません。動作するかどうか教えてください。
以下を除いて、それはあなたがすでに持っているものとほとんど同じです。
これを簡単に拡張して、複数の行を更新できます。サブクエリ式を使用するバージョンでは、サブクエリが複数の行を生成するように変更された場合、クエリは失敗します。
サブクエリで_global.prospect
_のエイリアスを作成しなかったため、少し読みやすくなりました。これはFROM
句を使用するため、更新中のテーブルを誤って参照するとエラーが発生します。
ご使用のバージョンでは、サブクエリ式はすべてのアイテムで検出されます。 PostgreSQLはこれを最適化し、式を1回だけ評価する必要がありますが、誤ってpsp
の列を参照したり、揮発性の式を追加したりすると、この最適化はなくなります。