Перейти к основному содержимому

Правила для INSERT, UPDATE, и DELETE

примечание

Эта страница переведена при помощи нейросети GigaChat.

Правила, которые определены для INSERT, UPDATE, и DELETE существенно отличаются от правил представлений, описанных в предыдущем разделе. Во-первых, команда CREATE RULE позволяет больше:

  • Они могут не определять действия.
  • Они могут определять несколько действий.
  • Они могут быть INSTEAD или ALSO (по умолчанию).
  • Псевдосвязи NEW и OLD становятся полезными.
  • Они могут иметь условия применения.

Во-вторых, они не изменяют дерево запросов на месте. Вместо этого они создают ноль или более новых деревьев запросов и могут выбросить исходное.

Внимание

Во многих случаях задачи, которые могли бы быть выполнены с помощью правил INSERT/UPDATE/DELETE, лучше выполнять с использованием триггеров. Триггеры немного сложнее с точки зрения нотации, но их семантика гораздо проще для понимания. Правила имеют тенденцию давать неожиданные результаты, когда исходный запрос содержит изменчивые функции: изменчивые функции могут выполняться чаще, чем ожидалось, в процессе выполнения этих правил.

Также есть некоторые случаи, которые вообще не поддерживаются этими типами правил, включая WITH предложения в исходном запросе и множественное присваивание подзапросов SELECT в списке SET запросов UPDATE. Это связано с тем, что копирование этих конструкций в запрос правила приведет к многократной оценке подзапроса, вопреки явному намерению автора запроса.

Как работают правила обновления

Запомните синтаксис:

CREATE [ OR REPLACE ] RULE name AS ON event
TO table [ WHERE condition ]
DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) }

В дальнейшем, правила обновления означают правила, которые определены на INSERT, UPDATE, или DELETE.

Правила обновления применяются системой правил при равенстве результирующего отношения и типа команды дерева запросов объекту и событию, указанным в команде CREATE RULE. Для правил обновления система правил создает список деревьев запросов. Изначально список деревьев запросов пуст. Может быть ноль (NOTHING ключевое слово), одно или несколько действий. Чтобы упростить, рассмотрим правило с одним действием. Это правило может иметь квалификацию или нет, и оно может быть INSTEAD или ALSO (по умолчанию).

Что такое условие применения правила? Это ограничение, которое указывает, когда действия правила должны выполняться, а когда нет. Это условие может ссылаться только на псевдосвязи NEW и/или OLD, которые фактически представляют отношение, указанное в качестве объекта (но со специальным значением).

Итак, есть три случая, которые приводят к следующим деревьям запросов для правила с одним действием.

  • Без условия, с любым из ALSO или INSTEAD – дерево запросов из действия правила с добавлением уточнения исходного дерева запросов.
  • С условием применения в режиме ALSO – дерево запросов из действия правила с добавленной квалификацией правила и квалификацией исходного дерева запросов.
  • С условием применения в режиме INSTEAD – дерево запросов из правила действия с квалификацией правила и исходное дерево запросов с квалификацией; и исходное дерево запросов с добавленной отрицательной квалификацией правила.

Наконец, если правило является ALSO, неизмененное исходное дерево запросов добавляется в список. Поскольку только квалифицированные INSTEAD правила уже добавляют исходное дерево запросов, получается либо одно, либо два выходных дерева запросов для правила с одним действием.

Для правил ON INSERT исходный запрос (если он не подавляется INSTEAD) выполняется перед любыми действиями, добавляемыми правилами. Это позволяет действиям видеть вставленные строки(ы). Но для правил ON UPDATE и ON DELETE исходный запрос выполняется после действий, добавленных правилами. Это гарантирует, что действия могут видеть строки, которые должны быть обновлены или удалены; в противном случае действия могут ничего не делать, потому что они не находят строк, соответствующих их квалификации.

Деревья запросов, генерируемые из действий правила, снова выбрасываются в систему переписывания, и, возможно, применяются дополнительные правила, что приводит к созданию дополнительных или меньших деревьев запросов. Таким образом, действия правила должны иметь либо другой тип команды, либо другую результирующую связь, чем само правило, иначе этот рекурсивный процесс закончится бесконечным циклом. (Рекурсивное расширение правила будет обнаружено и сообщено об ошибке.)

Деревья запросов, найденные в действиях системы каталога pg_rewrite, являются лишь шаблонами. Поскольку они могут ссылаться на записи диапазона таблиц для NEW и OLD, перед их использованием необходимо произвести некоторые замены. Для любой ссылки на NEW выполняется поиск соответствующего элемента в списке целей исходного запроса. Если он найден, выражение этого элемента заменяет ссылку. В противном случае, NEW означает то же самое, что и OLD (для UPDATE) или заменяется значением NULL (для INSERT). Любая ссылка на OLD заменяется ссылкой на запись таблицы диапазонов, которая является результирующей связью.

После того как система применит правила обновления, она применит правила представления к полученному дереву (деревьям) запросов. Представления не могут добавлять новые действия обновления, поэтому нет необходимости применять правила обновления к результатам перезаписи представлений.

Первый шаг по правилам

Допустим, требуется отслеживать изменения в столбце sl_avail отношения shoelace_data. Поэтому настраиваем таблицу журнала и правило, которое условно записывает запись журнала при выполнении операции UPDATE над shoelace_data.

CREATE TABLE shoelace_log (
sl_name text, -- shoelace changed
sl_avail integer, -- new available value
log_who text, -- who did it
log_when timestamp -- when
);

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,
current_user,
current_timestamp
);

Теперь кто-то делает:

UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7';

смотрим на таблицу журнала:

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 shoelace_data.sl_name = 'sl7';

Существует правило log_shoelace, которое ON UPDATE с выражением квалификации правила:

NEW.sl_avail <> OLD.sl_avail

и действие:

INSERT INTO shoelace_log VALUES (
new.sl_name, new.sl_avail,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old;

Это выглядит немного странно, так как обычно нельзя написать INSERT ... VALUES ... FROM. Пункт FROM здесь просто указывает на то, что есть записи таблицы диапазонов в дереве запросов для new и old. Они необходимы для того, чтобы они могли быть упомянуты переменными в дереве запросов команды INSERT.

Правило является квалифицированным правилом ALSO, поэтому система правил должна вернуть два дерева запросов: измененное правило действия и исходное дерево запросов. На шаге 1 таблица диапазона исходного запроса включается в дерево запросов действия правила. Это приводит к:

INSERT INTO shoelace_log VALUES (
new.sl_name, new.sl_avail,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old,
shoelace_data shoelace_data;

На шаге 2 добавляется квалификация правила, поэтому результирующий набор ограничивается строками, где изменяется sl_avail:

INSERT INTO shoelace_log VALUES (
new.sl_name, new.sl_avail,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old,
shoelace_data shoelace_data
WHERE new.sl_avail <> old.sl_avail;

(Это выглядит еще более странно, поскольку у INSERT ... VALUES нет и WHERE условия, но планировщик и исполнитель не будут испытывать никаких трудностей с этим. Им все равно нужно поддерживать эту же функциональность для INSERT ... SELECT .)

На шаге 3 к исходному дереву запросов добавляется квалификация, которая дополнительно ограничивает результирующий набор только теми строками, которые были бы затронуты исходным запросом:

INSERT INTO shoelace_log VALUES (
new.sl_name, new.sl_avail,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old,
shoelace_data shoelace_data
WHERE new.sl_avail <> old.sl_avail
AND shoelace_data.sl_name = 'sl7';

Шаг 4 заменяет ссылки на NEW элементами списка назначения из исходного дерева запросов или соответствующими ссылками на переменные из результирующего отношения:

INSERT INTO shoelace_log VALUES (
shoelace_data.sl_name, 6,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old,
shoelace_data shoelace_data
WHERE 6 <> old.sl_avail
AND shoelace_data.sl_name = 'sl7';

Шаг 5 изменяет OLD ссылки на результат отношения ссылок:

INSERT INTO shoelace_log VALUES (
shoelace_data.sl_name, 6,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old,
shoelace_data shoelace_data
WHERE 6 <> shoelace_data.sl_avail
AND shoelace_data.sl_name = 'sl7';

Вот и все. Поскольку правило является ALSO, также выводим исходное дерево запросов. Короче говоря, выходной сигнал от системы правил представляет собой список из двух деревьев запросов, которые соответствуют этим утверждениям:

INSERT INTO shoelace_log VALUES (
shoelace_data.sl_name, 6,
current_user, current_timestamp )
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 VALUES (
shoelace_data.sl_name, shoelace_data.sl_avail,
current_user, current_timestamp )
FROM shoelace_data
WHERE shoelace_data.sl_avail <> shoelace_data.sl_avail
AND shoelace_data.sl_name = 'sl7';

и эта квалификация никогда не будет верной.

Это также сработает, если исходный запрос изменяет несколько строк. Так что если кто-то выдал команду:

UPDATE shoelace_data SET sl_avail = 0
WHERE sl_color = 'black';

фактически обновляются четыре строки (sl1, sl2, sl3 и sl4). Но sl3 уже имеет sl_avail = 0. В этом случае квалификация исходного дерева запросов отличается, и это приводит к дополнительному дереву запросов:

INSERT INTO shoelace_log
SELECT shoelace_data.sl_name, 0,
current_user, current_timestamp
FROM shoelace_data
WHERE 0 <> shoelace_data.sl_avail
AND shoelace_data.sl_color = 'black';

будет сгенерировано правилом. Это дерево запросов обязательно вставит три новых записи журнала. И это абсолютно правильно.

Здесь можно увидеть, почему важно, чтобы исходное дерево запросов выполнялось последним. Если бы UPDATE было выполнено первым, все строки уже были бы установлены на ноль, поэтому запись в журнал INSERT не нашла бы ни одной строки, где 0 <> shoelace_data.sl_avail.

Сочетание с представлениями

Простой способ защитить отношения представлений от упомянутой возможности того, что кто-то может попытаться запустить 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, результирующий список деревьев запросов будет пустым, а весь запрос станет ничем, потому что после того, как с ним покончено правило системы, не остается ничего, что можно было бы оптимизировать или выполнять.

Более сложный способ использования системы правил заключается в создании правил, которые переписывают дерево запроса таким образом, чтобы оно выполняло правильную операцию над реальными таблицами. Чтобы сделать это для представления 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;

Если хотите поддерживать запросы RETURNING к представлению, то нужно сделать так, чтобы правила включали RETURNING предложения, вычисляющие строки представления. Обычно это довольно тривиально для представлений одной таблицы, но немного утомительно для представлений соединений, таких как 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
)
RETURNING
shoelace_data.*,
(SELECT shoelace_data.sl_len * u.un_fact
FROM unit u WHERE shoelace_data.sl_unit = u.un_name);

Обратите внимание, что одно это правило поддерживает как запросы INSERT, так и INSERT RETURNING к представлению – предложение RETURNING просто игнорируется для INSERT.

Теперь предположим, что время от времени в магазин поступает упаковка шнурков и большой список деталей вместе с ними. Но не хочется вручную обновлять представление shoelace каждый раз. Вместо этого настраиваем две маленькие таблицы: одну, в которую можно вставить элементы из списка деталей, и другую со специальным трюком. Команды создания для них следующие:

CREATE TABLE shoelace_arrive (
arr_name text,
arr_quant integer
);

CREATE TABLE shoelace_ok (
ok_name text,
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;

Теперь можете заполнить таблицу shoelace_arrive данными из списка деталей:

SELECT * FROM shoelace_arrive;

arr_name | arr_quant
----------+-----------
sl3 | 10
sl6 | 20
sl8 | 20
(3 rows)

Быстро взгляните на текущие данные:

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
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)

Теперь переместите прибывшие шнурки внутрь:

INSERT INTO shoelace_ok SELECT * FROM shoelace_arrive;

и проверьте результаты:

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)

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 = 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 shoelace.sl_name = shoelace_arrive.arr_name;

и отбрасывает исходный INSERT на shoelace_ok. Этот переписанный запрос снова передается системе правил, и второе примененное правило shoelace_upd дает:

UPDATE shoelace_data
SET sl_name = shoelace.sl_name,
sl_avail = 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 shoelace_data
WHERE shoelace.sl_name = shoelace_arrive.arr_name
AND shoelace_data.sl_name = shoelace.sl_name;

Снова это правило и предыдущее дерево запроса уничтожено. Обратите внимание, что этот запрос все еще использует представление . Но система правил еще не закончила с этим шагом, поэтому она продолжает и применяет правило к нему, и получаем:

UPDATE shoelace_data
SET sl_name = s.sl_name,
sl_avail = 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 shoelace_data,
shoelace old, shoelace new,
shoelace_data s, unit u
WHERE s.sl_name = shoelace_arrive.arr_name
AND shoelace_data.sl_name = s.sl_name;

Наконец, применяется правило , производя дополнительное дерево запросов:

INSERT INTO shoelace_log
SELECT s.sl_name,
s.sl_avail + shoelace_arrive.arr_quant,
current_user,
current_timestamp
FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
shoelace_ok old, shoelace_ok new,
shoelace shoelace, shoelace old,
shoelace new, shoelace_data shoelace_data,
shoelace old, shoelace new,
shoelace_data s, unit u,
shoelace_data old, shoelace_data new
shoelace_log shoelace_log
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;

После этого система правил исчерпывает правила и возвращает сгенерированные деревья запросов.

Таким образом, остается два конечных дерева запросов, которые эквивалентны операторам :

INSERT INTO shoelace_log
SELECT s.sl_name,
s.sl_avail + shoelace_arrive.arr_quant,
current_user,
current_timestamp
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 будет

Nested Loop
-> Merge Join
-> Seq Scan
-> Sort
-> Seq Scan on s
-> Seq Scan
-> Sort
-> Seq Scan on shoelace_arrive
-> Seq Scan on shoelace_data

в то время как исключение дополнительной записи в таблицу диапазонов привело бы к

Merge Join
-> Seq Scan
-> Sort
-> Seq Scan on s
-> Seq Scan
-> Sort
-> Seq Scan on shoelace_arrive

что дает точно такие же записи в таблице журнала. Таким образом, система правил вызвала одно дополнительное сканирование таблицы shoelace_data, которое абсолютно не нужно. И такое же избыточное сканирование выполняется еще раз в UPDATE. Но это была действительно сложная работа, чтобы сделать все это возможным вообще.

Теперь проведем окончательную демонстрацию системы правил PostgreSQL и ее возможностей. Скажем, в базу данных добавляются шнурки с необычными цветами:

INSERT INTO shoelace VALUES ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
INSERT INTO shoelace VALUES ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);

Создадим представление для проверки того, какие записи shoelace не соответствуют ни одному цвету обуви. Представление для этого выглядит так:

CREATE VIEW shoelace_mismatch AS
SELECT * FROM shoelace WHERE NOT EXISTS
(SELECT shoename FROM shoe WHERE slcolor = sl_color);

Его вывод следующий:

SELECT * FROM shoelace_mismatch;

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

Теперь настроим его таким образом, чтобы несоответствующие шнурки, которых нет в наличии, удалялись из базы данных. Чтобы немного усложнить задачу для PostgreSQL, не удаляем их напрямую. Вместо этого создаем еще одно представление:

CREATE VIEW shoelace_can_delete AS
SELECT * FROM shoelace_mismatch WHERE sl_avail = 0;

и сделайте это следующим образом:

DELETE FROM shoelace WHERE EXISTS
(SELECT * FROM shoelace_can_delete
WHERE sl_name = shoelace.sl_name);

Результаты следующие:

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)

A DELETE на представление с квалификацией подзапроса, которая в общей сложности использует 4 вложенных/объединенных представления, где одно из них само имеет квалификацию подзапроса, содержащую представление, и где используются вычисляемые столбцы представления, переписывается в одно-единственное дерево запросов, которое удаляет запрошенные данные из реальной таблицы.

Вероятно, есть лишь несколько ситуаций в реальном мире, когда такая конструкция необходима. Но это дает уверенность в том, что она работает.