INSERT、 UPDATE および DELETE についてのルール

ビュールールに対しての差異

ON INSERT、UPDATE および DELETE でのルールは前節で解説 したビューのルールとは全く異なります。第一点として、これらの CREATE RULE コマンドは数多くの設定を取り仕切っています。

第二点として、その場所でパースツリーを修正しません。そのかわりに 新規の複数(ゼロの場合もありますが)のパースツリーを生成して、 オリジナルを破棄します。

これらのルールの動作k

    CREATE RULE rule_name AS ON event
        TO object [WHERE rule_qualification]
        DO [INSTEAD] [action | (actions) | NOTHING];
上記構文を覚えておいて下さい。以下では、 "ルールの更新" は 定義された ON INSERT、 UPDATE または DELETE ルールを意味します。

ルールの更新はルールシステムによって、結果リレーションと パースツリーのコマンドタイプがオブジェクトと CREATE RULE で 与えられるイベントと等しい場合に適用されます。 ルールの更新に対してルールシステムはパースツリーのリストを生成 します。初めの段階でパースツリーリストは空です。 ゼロ(キーワードが NOTHING)か、一つかまたは複数のアクションが 有効です。簡単にするため、ここでは一つのアクションのルールを 取り上げます。このルールは条件を持っていても持っていなくても、 また INSTEAD であっても、無くても構いません。

ルール条件とはどんなものでしょうか。それはルールのアクション を行われなければならない時と、行ってはならない時を指定する 条件です。基本的に(特別な意味合いをもった)オブジェクトとして 与えられるリレーションである NEW そして、あるいは OLD 疑似 リレーションのみを、この条件は参照します。

一つのアクションルールに対し、以下のパースツリーを生成する四つの 事例があげられます。

最終的に、ルールが INSTEAD でない場合、変更されていない オリジナルのパースツリーがリストに付け加えられます。 条件に合った INSTEAD ルールのみが既にオリジナルのパースツリーに追加を しているので、最後は一つのアクションに対して最大合わせて二つの パースツリーに行きつきます。

ルールアクションで生成されたパースツリーは再度書き換えシステムに 手渡され、相当数のパースツリーの結果をもたらす、より多くのルールの 適用を受けることもあります。ですから、ルールアクションにおける パースツリーは別のコマンドタイプか、別の結果リレーションを持って いなければなりません。さもないとこの再帰的手順はループになって しまいます。現在 10 回までの再帰処理反復制限が組み込まれています。 10 回反復処理を行った後にもまだ適用すべき更新ルールがあった場合は、、 ルールシステムは複数のルール定義にまたがったループであると想定し、 そのトランザクションを停止します。

pg_rewrite システムカタログのアクションで 見受けられるパースツリーは単なるテンプレートです。これらは NEW と OLD に対する範囲テーブルの項目を参照することが出来るため 使用される前に何らかの置換措置がとられていなければなりません。 NEW に対するどんな参照でも、オリジナルの問合せの目的リストは 関連する項目があるかどうか検索されます。 項目が見つかった場合には、その項目式は参照の中に組み込まれます。 項目がなかった場合、NEW は OLD と同じ意味になります。 OLD に対するどんな参照でも結果リレーションである範囲テーブルの 項目に対する参照に取って代わられます。

最初のルール ステップ・バイ・ステップ

shoelace_data リレーションの sl_avail カラムの変化を追跡してみたいと思います。 そこでログ用テーブルと毎回の入力および shoelace_data に対して行われる UPDATE を記録するルールを用意しました。

    CREATE TABLE shoelace_log (
        sl_name    char(10),      -- 変更された靴紐
        sl_avail   integer,       -- 新規の有効な値
        log_who    name,          -- 誰が変更したか
        log_when   datetime       -- いつ変更したか
    );

    CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data
        WHERE NEW.sl_avail != OLD.sl_avail
        DO INSERT INTO shoelace_log VALUES (
                                        NEW.sl_name,
                                        NEW.sl_avail,
                                        getpgusername(),
                                        'now'::text
                                    );
ひとつの興味ある項目として、INSERT アクションルールの中で、 'now' をテキスト型にキャストすることです。 そうしないとパーサは CREATE RULE の time を見に行き、 shoelace_log にあるターゲットの型は datetime 型と判断してそれから定数を生成しようとします。 そして生成に成功します。そして、datetime の定数値が アクションルールに記憶され、全てのログ項目はその CREATE RULE 文の time を持つことになります。これは望まれる事ではありません。 キャストによりパーサは datetime('now'::text) をそのまま構築する ようになり、ルールが実行される時にこれ(datetime('now'::text))が 評価されます。

そこでアルは、

    al_bundy=> UPDATE shoelace_data SET sl_avail = 6                       
    al_bundy->     WHERE sl_name = 'sl7';
と入力しました。ログテーブルを見てみましょう。
    al_bundy=> SELECT * FROM shoelace_log;
    sl_name   |sl_avail|log_who|log_when                        
    ----------+--------+-------+--------------------------------
    sl7       |       6|Al     |Tue Oct 20 16:14:45 1998 MET DST
    (1 row)
思った結果が出ました。裏ではパーサがパースツリーを生成しました。 (今回、操作の基本は更新ルールにたいするルールアクションのため、 基となったパースツリーの部分部分は強調されています。)
    UPDATE shoelace_data SET sl_avail = 6
      FROM shoelace_data shoelace_data
     WHERE bpchareq(shoelace_data.sl_name, 'sl7');
ルールの条件式を持つ ON UPDATE の 'log_shoelace' ルールと、
    int4ne(NEW.sl_avail, OLD.sl_avail)
一つのアクション
    INSERT INTO shoelace_log SELECT 
           *NEW*.sl_name, *NEW*.sl_avail,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data *NEW*, shoelace_data *OLD*,
           shoelace_log shoelace_log;
があります。 pg_rules システムビューの出力を信用しては行けません。 INSERT における NEW と OLD のみを参照する状態を取り扱い、 INSERT の VALUES フォーマットを出力するものです。 パースツリーのレベルでは INSERT ... VALUE と INSERT ... SELECT の間に違いはありません。それらは共に 範囲テーブル、目的リストそしてたぶん条件その他を持っています。 オプティマイザは、型の結果、 逐次スキャン、インデックススキャン、 ジョインあるいはそのパースツリーのどれにでもに対する実行プランを 生成するかどうか後に決定します。パースツリーに範囲テーブル項目 を参照するものが残っていない場合、それが結果として (INSERT ... VALUES バージョンの)実行プランになります。 このルールアクションは二つの異なった結果を実際引き起こします。

ルールは非 INSTEAD ルールで条件づけられるため、ルールシステム は二つのパースツリーを戻さなければなりません。変更されたルール アクションと元のパースツリーです。第一のステップで元の問合せの 範囲テーブルはルールアクションパースツリーと関連づけられます。 そして、次の結果を生みます。

    INSERT INTO shoelace_log SELECT 
           *NEW*.sl_name, *NEW*.sl_avai,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data shoelace_data, shoelace_data *NEW*,
           shoelace_data *OLD*, shoelace_log shoelace_log;
ステップ 2 で、ルールの条件が付け加えられ、結果セットは sl_avail が変更された行に限定されます。
    INSERT INTO shoelace_log SELECT 
           *NEW*.sl_name, *NEW*.sl_avai,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data shoelace_data, shoelace_data *NEW*,
           shoelace_data *OLD*, shoelace_log shoelace_log
     WHERE int4ne(*NEW*.sl_avail, *OLD*.sl_avail);
ステップ 3 で、元のパースツリーの条件が付け加えられ、 結果セットは更に元のパースツリーに付帯する行のみに制限を かけます。
    INSERT INTO shoelace_log SELECT 
           *NEW*.sl_name, *NEW*.sl_avai,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data shoelace_data, shoelace_data *NEW*,
           shoelace_data *OLD*, shoelace_log shoelace_log
     WHERE int4ne(*NEW*.sl_avail, *OLD*.sl_avail)
       AND bpchareq(shoelace_data.sl_name, 'sl7');
ステップ 4 は、オリジナルのパースツリーの目的リスト項目または 結果リレーションの該当する変数参照で NEW の参照を置換します。
    INSERT INTO shoelace_log SELECT 
           shoelace_data.sl_name, 6,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data shoelace_data, shoelace_data *NEW*,
           shoelace_data *OLD*, shoelace_log shoelace_log
     WHERE int4ne(6, *OLD*.sl_avail)
       AND bpchareq(shoelace_data.sl_name, 'sl7');
ステップ 5 は、OLD 参照を結果リレーション参照に置き換えます。
    INSERT INTO shoelace_log SELECT 
           shoelace_data.sl_name, 6,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data shoelace_data, shoelace_data *NEW*,
           shoelace_data *OLD*, shoelace_log shoelace_log
     WHERE int4ne(6, shoelace_data.sl_avail)
       AND bpchareq(shoelace_data.sl_name, 'sl7');
これで終りです。max まで縮小されて、ルールシステムが戻すのは、 命令文と同じ二つのパースツリーのリストです。
    INSERT INTO shoelace_log SELECT
           shoelace_data.sl_name, 6,
           getpgusername(), 'now'
      FROM shoelace_data
     WHERE 6 != shoelace_data.sl_avail
       AND shoelace_data.sl_name = 'sl7';

    UPDATE shoelace_data SET sl_avail = 6
     WHERE sl_name = 'sl7';
この二つは順番通りに処理され、ルールが定義したことと全く 同一です。追加された置換と条件はオリジナルのの問合せが下記 のようであったか確認します。
    UPDATE shoelace_data SET sl_color = 'green'
     WHERE sl_name = 'sl7';
今回は、sl_avail に対する目的リスト項目がオリジナルの パースツリーには存在しないのでログ項目は書き出されず、 NEW.sl_avail が shoelace_data.sl_avail に置き換えられて、 特別の問合せになります。
    INSERT INTO shoelace_log SELECT
           shoelace_data.sl_name, shoelace_data.sl_avail,
           getpgusername(), 'now'
      FROM shoelace_data
     WHERE shoelace_data.sl_avail != shoelace_data.sl_avail
       AND shoelace_data.sl_name = 'sl7';
そして、その条件は真実ではありえません。 パースツリーレベルでは INSERT ... SELECT と INSERT ... VALUES の間に違いがないので、オリジナルの問合せが複数の行に変更を加えても 同じように動作します。そこで、アルが以下のコマンドを入れたとします。
    UPDATE shoelace_data SET sl_avail = 0
     WHERE sl_color = 'black';
確かに四つの行 (sl1, sl2, sl3 and sl4) が更新されました。 しかし、 sl3 は既に sl_avail = 0 を所有しています。 ここでは、オリジナルのパースツリー条件は異なっていて、特別の パースツリーになります。
    INSERT INTO shoelace_log SELECT
           shoelace_data.sl_name, 0,
           getpgusername(), 'now'
      FROM shoelace_data
     WHERE 0 != shoelace_data.sl_avail
       AND shoelace_data.sl_color = 'black';
このパースツリーは確実に三つの新しいログ項目を挿入します。 これは全く正しい動作です。

オリジナルのパースツリーが最後に実行されるということは重要です。 Postgres の"交通警官"は、二つの パースツリーの実行の間のコマンドカウンタを初めの実行による 変更を二番目が解るように増やします。もし UPDATE が先に 実行されたとしたら、全ての行はゼロにセットされて、 0 != shoelace_data.sl_avail である行を INSERT の段階で 見つけ出せなくなります。

ビューとの協調

どこかのユーザが見えないデータにたいし INSERT、UPDATE および DELETE を発行するといった、既述の可能性からビューリレーション を守る簡単な方法はそれらのパースツリーを破棄してしまうことです。 ルールを作ります。

    CREATE RULE shoe_ins_protect AS ON INSERT TO shoe
        DO INSTEAD NOTHING;
    CREATE RULE shoe_upd_protect AS ON UPDATE TO shoe
        DO INSTEAD NOTHING;
    CREATE RULE shoe_del_protect AS ON DELETE TO shoe
        DO INSTEAD NOTHING;
アルがビューのリレーション shoe に 上記の操作を加えようとすると、ルールシステムはルールを適用 します。ルールにはアクションがなく INSTEAD ですから、結果の パースツリーリストは空で、ルールシステムの処理が完了した後に 最適化されるものや実行されるべきものが何も残っていませんので、 全ての問合せは無効となります。

注意事項: この事実はデータベースに何の変化ももたらさず、従いバック エンドは問合せに対する回答を何も戻さないため、 フロントエンドアプリケーションがじらされる可能性があります。 libpq では PGRES_EMPTY_QUERY だけでなく似たようなものも 許容されます。psql では何も起こりません。将来変更される かもしれません。

より洗練されたルールシステムの使用法は、パースツリーを 実テーブルに正確な操作を行うものに書き換える事です。 shoelace ビューにこのことを適用する ため以下のルールを作ります。

    CREATE RULE shoelace_ins AS ON INSERT TO shoelace
        DO INSTEAD
        INSERT INTO shoelace_data VALUES (
               NEW.sl_name,
               NEW.sl_avail,
               NEW.sl_color,
               NEW.sl_len,
               NEW.sl_unit);

    CREATE RULE shoelace_upd AS ON UPDATE TO shoelace
        DO INSTEAD
        UPDATE shoelace_data SET
               sl_name = NEW.sl_name,
               sl_avail = NEW.sl_avail,
               sl_color = NEW.sl_color,
               sl_len = NEW.sl_len,
               sl_unit = NEW.sl_unit
         WHERE sl_name = OLD.sl_name;

    CREATE RULE shoelace_del AS ON DELETE TO shoelace
        DO INSTEAD
        DELETE FROM shoelace_data
         WHERE sl_name = OLD.sl_name;
今、アルの店に靴紐のケースが分厚い送り状とともに届けられました。 アルは計算が得意でないので、 shoelace のビューの更新を手作業で やらせるわけには行きません。代わりに、送り状から品目を挿入する テーブルと特殊な仕掛けのテーブルの二つの小さなテーブルを用意し ました。
    CREATE TABLE shoelace_arrive (
        arr_name    char(10),
        arr_quant   integer
    );

    CREATE TABLE shoelace_ok (
        ok_name     char(10),
        ok_quant    integer
    );

    CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok
        DO INSTEAD
        UPDATE shoelace SET
               sl_avail = sl_avail + NEW.ok_quant
         WHERE sl_name = NEW.ok_name;
アルのすることは特にありません。以下の結果を入手する まで何をしていても構いません。
    al_bundy=> SELECT * FROM shoelace_arrive;
    arr_name  |arr_quant
    ----------+---------
    sl3       |       10
    sl6       |       20
    sl8       |       20
    (3 rows)
結果は送り状に記載されたものと全く同じになっています。 現在のデータをちょっと見てみます。
    al_bundy=> SELECT * FROM shoelace ORDER BY sl_name;
    sl_name   |sl_avail|sl_color  |sl_len|sl_unit |sl_len_cm
    ----------+--------+----------+------+--------+---------
    sl1       |       5|black     |    80|cm      |       80
    sl2       |       6|black     |   100|cm      |      100
    sl7       |       6|brown     |    60|cm      |       60
    sl3       |       0|black     |    35|inch    |     88.9
    sl4       |       8|black     |    40|inch    |    101.6
    sl8       |       1|brown     |    40|inch    |    101.6
    sl5       |       4|brown     |     1|m       |      100
    sl6       |       0|brown     |   0.9|m       |       90
    (8 rows)
入荷した靴紐のデータを移動させます。
    al_bundy=> INSERT INTO shoelace_ok SELECT * FROM shoelace_arrive;
結果をチェックします。
    al_bundy=> SELECT * FROM shoelace ORDER BY sl_name;
    sl_name   |sl_avail|sl_color  |sl_len|sl_unit |sl_len_cm
    ----------+--------+----------+------+--------+---------
    sl1       |       5|black     |    80|cm      |       80
    sl2       |       6|black     |   100|cm      |      100
    sl7       |       6|brown     |    60|cm      |       60
    sl4       |       8|black     |    40|inch    |    101.6
    sl3       |      10|black     |    35|inch    |     88.9
    sl8       |      21|brown     |    40|inch    |    101.6
    sl5       |       4|brown     |     1|m       |      100
    sl6       |      20|brown     |   0.9|m       |       90
    (8 rows)

    al_bundy=> SELECT * FROM shoelace_log;
    sl_name   |sl_avail|log_who|log_when                        
    ----------+--------+-------+--------------------------------
    sl7       |       6|Al     |Tue Oct 20 19:14:45 1998 MET DST
    sl3       |      10|Al     |Tue Oct 20 19:25:16 1998 MET DST
    sl6       |      20|Al     |Tue Oct 20 19:25:16 1998 MET DST
    sl8       |      21|Al     |Tue Oct 20 19:25:16 1998 MET DST
    (4 rows)
一つの INSERT ... SELECT から三つの結果まで長い道のりでした。 このドキュメントでの解説はこれが最後です。(最後の例ではありま せんが。) 初めにパーサの出力があって、
    INSERT INTO shoelace_ok SELECT
           shoelace_arrive.arr_name, shoelace_arrive.arr_quant
      FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok;
最初のルール 'shoelace_ok_ins' が適用され、結果は
    UPDATE shoelace SET
           sl_avail = int4pl(shoelace.sl_avail, shoelace_arrive.arr_quant)
      FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
           shoelace_ok *OLD*, shoelace_ok *NEW*,
           shoelace shoelace
     WHERE bpchareq(shoelace.sl_name, showlace_arrive.arr_name);
となって、shoelace_ok に対する元の INSERT を破棄します。この書き換えられた問合せは再びルールシステムに わたされて、二番目に適用される 'shoelace_upd' ルールが生成されました。
    UPDATE shoelace_data SET
           sl_name = shoelace.sl_name,
           sl_avail = int4pl(shoelace.sl_avail, shoelace_arrive.arr_quant),
           sl_color = shoelace.sl_color,
           sl_len = shoelace.sl_len,
           sl_unit = shoelace.sl_unit
      FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
           shoelace_ok *OLD*, shoelace_ok *NEW*,
           shoelace shoelace, shoelace *OLD*,
           shoelace *NEW*, shoelace_data showlace_data
     WHERE bpchareq(shoelace.sl_name, showlace_arrive.arr_name)
       AND bpchareq(shoelace_data.sl_name, shoelace.sl_name);
再び INSTEAD ルールで、以前のパースツリーは破棄されます。 この問合せは view shoelace を引続き使用 しますが、このループによるルールシステムは終了していないので ルール '_RETshoelace' の適用を継続して下記を得ます。
    UPDATE shoelace_data SET
           sl_name = s.sl_name,
           sl_avail = int4pl(s.sl_avail, shoelace_arrive.arr_quant),
           sl_color = s.sl_color,
           sl_len = s.sl_len,
           sl_unit = s.sl_unit
      FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
           shoelace_ok *OLD*, shoelace_ok *NEW*,
           shoelace shoelace, shoelace *OLD*,
           shoelace *NEW*, shoelace_data showlace_data,
           shoelace *OLD*, shoelace *NEW*,
           shoelace_data s, unit u
     WHERE bpchareq(s.sl_name, showlace_arrive.arr_name)
       AND bpchareq(shoelace_data.sl_name, s.sl_name);
再度更新ルールが適用されました。方向転換をして書き換え 第三ラウンドに突入です。ここではルール 'log_shoelace' が 特別のパースツリーを生成したものに適用されます。
    INSERT INTO shoelace_log SELECT
           s.sl_name,
           int4pl(s.sl_avail, shoelace_arrive.arr_quant),
           getpgusername(),
           datetime('now'::text)
      FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
           shoelace_ok *OLD*, shoelace_ok *NEW*,
           shoelace shoelace, shoelace *OLD*,
           shoelace *NEW*, shoelace_data showlace_data,
           shoelace *OLD*, shoelace *NEW*,
           shoelace_data s, unit u,
           shoelace_data *OLD*, shoelace_data *NEW*
           shoelace_log shoelace_log
     WHERE bpchareq(s.sl_name,  showlace_arrive.arr_name)
       AND bpchareq(shoelace_data.sl_name, s.sl_name);
       AND int4ne(int4pl(s.sl_avail, shoelace_arrive.arr_quant),
                                                    s.sl_avail);
この後、ルールシステムはルールを使いきって生成されたパースツリー を返します。そして、SQL 文と同じ二つの 最終パースツリーで終結します。
    INSERT INTO shoelace_log SELECT
           s.sl_name,
           s.sl_avail + shoelace_arrive.arr_quant,
           getpgusername(),
           'now'
      FROM shoelace_arrive shoelace_arrive, shoelace_data shoelace_data,
           shoelace_data s
     WHERE s.sl_name = shoelace_arrive.arr_name
       AND shoelace_data.sl_name = s.sl_name
       AND s.sl_avail + shoelace_arrive.arr_quant != s.sl_avail;
           
    UPDATE shoelace_data SET
           sl_avail = shoelace_data.sl_avail + shoelace_arrive.arr_quant
     FROM shoelace_arrive shoelace_arrive,
          shoelace_data shoelace_data,
          shoelace_data s
    WHERE s.sl_name = shoelace_arrive.sl_name
      AND shoelace_data.sl_name = s.sl_name;
結果は、一つのリレーションから来たデータが別のリレーションに 挿入され、三つ目のリレーションの更新に変更され、四つ目の ために更新され、五つ目の最終更新のログ記録となって、最終的に 二つの問合せに縮小されます。

ちょっと醜い些細な事項があります。出来て来た二つの問合せを 見ると、一つに縮小されたはずの shoelace_data リレーションが範囲テーブルに二度出て来ます。オプティマイザは処理 をしないので、INSERT のルールシステムの出力に対する実行プランは 次のようになります。

ネストしたループ
  ->  マージ ジョイン
        ->  逐次スキャン
              ->  ソート
                    ->  s を逐次スキャン
        ->  逐次スキャン
              ->  ソート
                    ->  shoelace_arrive を逐次スキャン
  ->  shoelace_data を逐次スキャン
一方特別な範囲テーブル項目を落すことで
マージ ジョイン
  ->  逐次スキャン
        ->  ソート
              ->  s を逐次スキャン
  ->  逐次スキャン
        ->  ソート
              ->  shoelace_arrive を逐次スキャン
の様にログリレーションに全く同じ項目が作られます。 ですから、ルールシステムは全く必要のない shoelace_data リレーションに対する 一つの特別なスキャンを行うことになります。そして UPDATE でも同様な不要のスキャンが再度実行されます。しかしながら、 これら全てを適していることにするのは大変な仕事です。

Postgres のルールシステムとその 効力にたいする最後の実証です。靴紐を売っている可愛いブロンド がいます。アルが判っていないことは、可愛いだけでなく賢い子 だということです。とても賢明な少女です。ですから、時として アルは全く売れない靴紐を注文してしまいます。 今回はマゼンタ色の靴紐 1000 組を注文しましたし、ほかの種類 が現在在庫に無いから、それは後で購入する約束までしました。 データベースにはピンクの靴紐を用意しました。

    al_bundy=> INSERT INTO shoelace VALUES 
    al_bundy->     ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
    al_bundy=> INSERT INTO shoelace VALUES 
    al_bundy->     ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);
こんなことがたびたび起こるので、お店にない靴用の靴紐の項目 を見なくてはなりません。毎回複雑な構文を打ち込むか、または ビューを作ります。ビューはこうなります。
    CREATE VIEW shoelace_obsolete AS
        SELECT * FROM shoelace WHERE NOT EXISTS
            (SELECT shoename FROM shoe WHERE slcolor = sl_color);
その出力は以下となります。
    al_bundy=> SELECT * FROM shoelace_obsolete;
    sl_name   |sl_avail|sl_color  |sl_len|sl_unit |sl_len_cm
    ----------+--------+----------+------+--------+---------
    sl9       |       0|pink      |    35|inch    |     88.9
    sl10      |    1000|magenta   |    40|inch    |    101.6
1000 のマゼンタ色の靴紐を捨てる前にアルに債務を負わせなければ なりませんが、それは別の問題です。ピンクの項目を削除します。 Postgres でちょっと面倒なことは、 直接削除をしないと言うことです。代わりにもう一つ別のビューを 作ります。
    CREATE VIEW shoelace_candelete AS
        SELECT * FROM shoelace_obsolete WHERE sl_avail = 0;
このようにします。
    DELETE FROM shoelace WHERE EXISTS
        (SELECT * FROM shoelace_candelete
                 WHERE sl_name = shoelace.sl_name);
さあ、出来ました。
    al_bundy=> SELECT * FROM shoelace;
    sl_name   |sl_avail|sl_color  |sl_len|sl_unit |sl_len_cm
    ----------+--------+----------+------+--------+---------
    sl1       |       5|black     |    80|cm      |       80
    sl2       |       6|black     |   100|cm      |      100
    sl7       |       6|brown     |    60|cm      |       60
    sl4       |       8|black     |    40|inch    |    101.6
    sl3       |      10|black     |    35|inch    |     88.9
    sl8       |      21|brown     |    40|inch    |    101.6
    sl10      |    1000|magenta   |    40|inch    |    101.6
    sl5       |       4|brown     |     1|m       |      100
    sl6       |      20|brown     |   0.9|m       |       90
    (9 rows)
ビュー上での合計四つのネスト/ジョインされたビューで、 その中の一つはビューを含む subselect 条件があって、かつ演算 を施されたビューのカラムが使われる場合、subselect 条件のつい たビューへの DELETE は実テーブルから、要求されたデータを削除 する単一のパースツリーに書き換えられます。

このような構造が必要な状況は実社会ではほとんど無いと考え ますが、実際に動くことを確認するのは快いものです。

The truth is: こんなことをやっていて、このドキュメントを書いている途中でもう 一つのバグを見つけました。バグを修正すると完璧に動いたので ちょっとびっくりしました。