sql >> データベース >  >> RDS >> PostgreSQL

他の子から参照されていない場合は、親を削除します

    PostgreSQLの場合9.1以降 これは、データ変更CTEを使用して1つのステートメントで実行できます。 。これは一般的にエラーが発生しにくいです。 最小化 競合状態が発生する2つのDELETE間の時間枠 同時操作で驚くべき結果につながる可能性があります:

    WITH del_child AS (
        DELETE FROM child
        WHERE  child_id = 1
        RETURNING parent_id, child_id
        )
    DELETE FROM parent p
    USING  del_child x
    WHERE  p.parent_id = x.parent_id
    AND    NOT EXISTS (
       SELECT 1
       FROM   child c
       WHERE  c.parent_id = x.parent_id
       AND    c.child_id <> x.child_id   -- !
       );
    

    SQLフィドル。

    いずれの場合も、子は削除されます。マニュアルを引用します:

    WITHのデータ変更ステートメント 一度だけ実行され、常に完了する 、プライマリクエリが出力のすべて(または実際にいずれか)を読み取るかどうかに関係なく。これはSELECTのルールとは異なることに注意してください WITH :前のセクションで述べたように、SELECTの実行 プライマリクエリがその出力を要求する場合にのみ実行されます。

    親が削除されるのは、その他がない場合のみです。 子供。
    最後の条件に注意してください。予想に反して、これは必要です。理由は次のとおりです。

    WITHのサブステートメント 同時に実行されます お互いにそしてメインクエリと。したがって、WITHでデータ変更ステートメントを使用する場合 、指定された更新が実際に発生する順序は予測できません。すべてのステートメントは同じスナップショットで実行されるため(第13章を参照)、ターゲットテーブルに対する互いの影響を「見る」ことはできません。

    大胆な強調鉱山。
    列名parent_idを使用しました わかりにくいidの代わりに 。

    競合状態を解消する

    上記の競合状態の可能性を排除するために完全に 、親行を最初にロックします 。もちろん、すべて 同様の操作を機能させるには、同じ手順に従う必要があります。

    WITH lock_parent AS (
       SELECT p.parent_id, c.child_id
       FROM   child  c
       JOIN   parent p ON p.parent_id = c.parent_id
       WHERE  c.child_id = 12              -- provide child_id here once
       FOR    NO KEY UPDATE                -- locks parent row.
       )
     , del_child AS (
       DELETE FROM child c
       USING  lock_parent l
       WHERE  c.child_id = l.child_id
       )
    DELETE FROM parent p
    USING  lock_parent l
    WHERE  p.parent_id = l.parent_id
    AND    NOT EXISTS (
       SELECT 1
       FROM   child c
       WHERE  c.parent_id = l.parent_id
       AND    c.child_id <> l.child_id   -- !
       );
    

    この方法では、1つのみ 一度にトランザクションを実行すると、同じ親をロックできます。したがって、複数のトランザクションが同じ親の子を削除し、他の子を表示して親を惜しまない一方で、その後すべての子が削除されることはあり得ません。 (キー以外の列の更新は、FOR NO KEY UPDATEでも引き続き許可されます。 。)

    そのようなケースが発生しない場合、または発生する可能性がある場合(ほとんど発生しない場合)、最初のクエリの方が安価です。それ以外の場合、これは安全なパスです。

    FOR NO KEY UPDATE Postgres9.4で導入されました。マニュアルの詳細。古いバージョンでは、より強力なロックFOR UPDATEを使用します 代わりに。



    1. MySQLは外部キー制約を追加できません

    2. postgresで自動インクリメントカウンターをリセットする

    3. 主キーと一意キーの違い

    4. OBJECTPROPERTY()を使用して、SQLServerの外部キーによってテーブルが参照されているかどうかを確認します。