INSERT, UPDATE y DELETE #
Las reglas que se definen para INSERT, UPDATE
y DELETE son significativamente diferentes de las reglas de vistas
descritas en las secciones anteriores. En primer lugar, su comando CREATE
RULE permite más cosas:
Se les permite no tener ninguna acción.
Pueden tener múltiples acciones.
Pueden ser INSTEAD o ALSO (el valor predeterminado).
Las pseudorelaciones NEW y OLD se vuelven útiles.
Pueden tener condiciones (qualifications) de regla.
En segundo lugar, no modifican el árbol de consulta directamente. En su lugar, crean cero o más árboles de consulta nuevos y pueden desechar el original.
En muchos casos, las tareas que podrían realizarse mediante reglas
en INSERT/UPDATE/DELETE se hacen mejor
con disparadores (triggers). Los disparadores (triggers) son un poco más complicados en cuanto a notación, pero su
semántica es mucho más sencilla de entender. Las reglas tienden a tener resultados sorprendentes
cuando la consulta original contiene funciones volátiles: las funciones volátiles
pueden ejecutarse más veces de lo esperado en el proceso de llevar a cabo las reglas.
Además, hay algunos casos que no están admitidos por estos tipos de reglas en absoluto,
incluyendo notablemente las cláusulas WITH en la consulta original y
las subconsultas SELECT de asignación múltiple en la lista SET
de las consultas UPDATE. Esto se debe a que copiar estas construcciones
en una consulta de regla daría como resultado múltiples evaluaciones de la subconsulta,
lo cual es contrario a la intención expresa del autor de la consulta.
Ten en cuenta la sintaxis:
CREATE [ OR REPLACE ] RULEnameAS ONeventTOtable[ WHEREcondition] DO [ ALSO | INSTEAD ] { NOTHING |command| (command;command... ) }
En lo que sigue, reglas de actualización se refiere a las reglas que se definen
para INSERT, UPDATE o DELETE.
El sistema de reglas aplica las reglas de actualización cuando la relación
de resultado y el tipo de comando de un árbol de consulta coinciden con el
objeto y el evento indicados en el comando CREATE RULE.
Para las reglas de actualización, el sistema de reglas crea una lista de árboles de consulta.
Al principio, la lista de árboles de consulta está vacía.
Puede haber cero (palabra clave NOTHING), una o múltiples acciones.
Para simplificar, veremos una regla con una sola acción. Esta regla
puede tener una condición o no, y puede ser INSTEAD o
ALSO (el valor predeterminado).
¿Qué es una condición (qualification) de regla? Es una restricción que indica
cuándo se deben realizar las acciones de la regla y cuándo no. Esta
condición solo puede hacer referencia a las pseudorelaciones NEW y/o OLD,
que básicamente representan la relación que se dio como objeto (pero con un
significado especial).
Por lo tanto, tenemos tres casos que producen los siguientes árboles de consulta para una regla de una sola acción.
ALSO o
INSTEADel árbol de consulta de la acción de la regla con la condición del árbol de consulta original añadida
ALSOel árbol de consulta de la acción de la regla con la condición de la regla y la condición del árbol de consulta original añadidas
INSTEADel árbol de consulta de la acción de la regla con la condición de la regla y la condición del árbol de consulta original; y el árbol de consulta original con la condición de la regla negada añadida
Finalmente, si la regla es ALSO, el árbol de consulta original sin cambios se
añade a la lista. Dado que solo las reglas INSTEAD condicionales ya añaden el
árbol de consulta original, terminamos con uno o dos árboles de consulta de salida
para una regla con una sola acción.
Para las reglas ON INSERT, la consulta original (si no ha sido suprimida por INSTEAD)
se ejecuta antes que cualquier acción añadida por las reglas. Esto permite que las acciones
vean la(s) fila(s) insertada(s). Pero para las reglas ON UPDATE y ON
DELETE, la consulta original se ejecuta después de las acciones añadidas por las reglas.
Esto asegura que las acciones puedan ver las filas que se van a actualizar o eliminar;
de lo contrario, las acciones podrían no hacer nada porque no encuentran filas que
coincidan con sus condiciones (qualifications).
Los árboles de consulta generados a partir de las acciones de las reglas se envían de nuevo al sistema de reescritura, y tal vez se apliquen más reglas dando como resultado árboles de consulta adicionales o menos. Por lo tanto, las acciones de una regla deben tener un tipo de comando diferente o una relación de resultado diferente a aquella sobre la que está la regla misma, de lo contrario este proceso recursivo de expansión recursiva terminará en un bucle infinito. (La expansión recursiva de una regla se detectará y se informará como un error).
Los árboles de consulta que se encuentran en las acciones del catálogo del sistema
pg_rewrite son solo plantillas. Dado que pueden hacer referencia a las entradas de la tabla de rangos para
NEW y OLD, se deben realizar algunas sustituciones antes de que se puedan
utilizar. Para cualquier referencia a NEW, se busca una entrada correspondiente en la lista de objetivos de la consulta original. Si se encuentra, la expresión de esa entrada reemplaza a la referencia. De lo contrario, NEW significa lo mismo que OLD (para un UPDATE) o se reemplaza por un valor nulo (para un INSERT). Cualquier referencia a OLD se reemplaza por una referencia a la entrada de la tabla de rangos que es la relación de resultado.
Una vez que el sistema ha terminado de aplicar las reglas de actualización, aplica las reglas de vistas a los árboles de consulta producidos. Las vistas no pueden insertar nuevas acciones de actualización, por lo que no es necesario aplicar reglas de actualización a la salida de la reescritura de vistas.
Supongamos que queremos hacer un seguimiento de los cambios en la columna sl_avail en la relación shoelace_data. Así que configuramos una tabla de registro y una regla que escribe condicionalmente una entrada de registro cuando se realiza un UPDATE en 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
);
Ahora alguien hace:
UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7';
y miramos la tabla de registro:
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)
Eso es lo que esperábamos. Lo que ocurrió en segundo plano es lo siguiente. El analizador (parser) creó el árbol de consulta:
UPDATE shoelace_data SET sl_avail = 6 FROM shoelace_data shoelace_data WHERE shoelace_data.sl_name = 'sl7';
Hay una regla log_shoelace que es ON UPDATE con la expresión de condición de regla:
NEW.sl_avail <> OLD.sl_avail
y la acción:
INSERT INTO shoelace_log VALUES (
new.sl_name, new.sl_avail,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old;
(Esto parece un poco extraño ya que normalmente no puedes escribir INSERT ... VALUES ... FROM. La cláusula FROM aquí es solo para indicar que hay entradas en la tabla de rangos en el árbol de consulta para new y old. Estas son necesarias para que puedan ser referenciadas por variables en el árbol de consulta del comando INSERT).
La regla es una regla ALSO condicional, por lo que el sistema de reglas tiene que devolver dos árboles de consulta: la acción de la regla modificada y el árbol de consulta original. En el paso 1, la tabla de rangos de la consulta original se incorpora al árbol de consulta de la acción de la regla. Esto da como resultado:
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;
En el paso 2, se le añade la condición de la regla, por lo que el conjunto de resultados se restringe a las filas donde cambia 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;
(Esto parece aún más extraño, ya que INSERT ... VALUES tampoco tiene una cláusula WHERE, pero el planificador y el ejecutor no tendrán ninguna dificultad con ello. Necesitan soportar esta misma funcionalidad de todos modos para INSERT ... SELECT).
En el paso 3, se añade la condición del árbol de consulta original, restringiendo aún más el conjunto de resultados a solo las filas que habrían sido tocadas por la consulta original:
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';
El paso 4 reemplaza las referencias a NEW por las entradas de la lista de objetivos del árbol de consulta original o por las referencias a variables correspondientes de la relación de resultado:
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';
El paso 5 cambia las referencias a OLD en referencias a la relación de resultado:
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';
Eso es todo. Dado que la regla es ALSO, también emitimos el árbol de consulta original. En resumen, la salida del sistema de reglas es una lista de dos árboles de consulta que corresponden a estas sentencias:
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';
Estas se ejecutan en este orden, y eso es exactamente lo que se pretendía que hiciera la regla.
Las sustituciones y las condiciones añadidas aseguran que, si la consulta original fuera, por ejemplo:
UPDATE shoelace_data SET sl_color = 'green' WHERE sl_name = 'sl7';
no se escribiría ninguna entrada de registro. En ese caso, el árbol de consulta original no contiene una entrada en la lista de objetivos para sl_avail, por lo que NEW.sl_avail será reemplazado por shoelace_data.sl_avail. Por lo tanto, el comando adicional generado por la regla es:
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';
y esa condición nunca será verdadera.
También funcionará si la consulta original modifica múltiples filas. Así que si alguien ejecutara el comando:
UPDATE shoelace_data SET sl_avail = 0 WHERE sl_color = 'black';
en realidad se actualizan cuatro filas (sl1, sl2, sl3 y sl4). Pero sl3 ya tiene sl_avail = 0. En este caso, la condición de la consulta original es diferente y eso resulta en el árbol de consulta adicional:
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';
generado por la regla. Este árbol de consulta seguramente insertará tres nuevas entradas de registro. Y eso es absolutamente correcto.
Aquí podemos ver por qué es importante que el árbol de consulta original se ejecute al final. Si el UPDATE se hubiera ejecutado primero, todas las filas ya habrían sido establecidas a cero, por lo que el INSERT de registro no encontraría ninguna fila donde 0 <> shoelace_data.sl_avail.
Una forma sencilla de proteger las relaciones de vista de la mencionada posibilidad de que alguien intente ejecutar INSERT, UPDATE o DELETE en ellas es dejar que esos árboles de consulta se descarten. Así que podríamos crear las reglas:
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;
Si alguien intenta realizar alguna de estas operaciones en la relación de vista shoe, el sistema de reglas aplicará estas reglas. Dado que las reglas no tienen acciones y son INSTEAD, la lista resultante de árboles de consulta estará vacía y toda la consulta se convertirá en nada porque no queda nada que optimizar o ejecutar después de que el sistema de reglas haya terminado con ella.
Una forma más sofisticada de usar el sistema de reglas es crear reglas que reescriban el árbol de consulta en uno que realice la operación correcta en las tablas reales. Para hacer eso en la vista shoelace, creamos las siguientes reglas:
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;
Si deseas admitir consultas RETURNING en la vista, debes hacer que las reglas incluyan cláusulas RETURNING que calculen las filas de la vista. Esto suele ser bastante trivial para vistas sobre una sola tabla, pero es un poco tedioso para vistas de unión (joins) como shoelace. Un ejemplo para el caso de inserción es:
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);
Ten en cuenta que esta única regla admite tanto consultas INSERT como INSERT RETURNING en la vista — la cláusula RETURNING simplemente se ignora para INSERT.
Ten en cuenta que en la cláusula RETURNING de una regla, OLD y NEW se refieren a las pseudorelaciones añadidas como entradas adicionales de la tabla de rangos a la consulta reescrita, en lugar de a las filas antiguas/nuevas en la relación de resultado. Así, por ejemplo, en una regla que admite consultas UPDATE en esta vista, si la cláusula RETURNING contuviera old.sl_name, siempre se devolvería el nombre antiguo, independientemente de si la cláusula RETURNING en la consulta sobre la vista especificara OLD o NEW, lo cual podría ser confuso. Para evitar esta confusión y admitir la devolución de valores antiguos y nuevos en las consultas sobre la vista, la cláusula RETURNING en la definición de la regla debe hacer referencia a entradas de la relación de resultado como shoelace_data.sl_name, sin especificar OLD o NEW.
Ahora supón que, de vez en cuando, llega un paquete de cordones a la tienda y, junto con él, una gran lista de piezas. Pero no quieres actualizar manualmente la vista shoelace cada vez. En su lugar, configuramos dos pequeñas tablas: una donde puedes insertar los artículos de la lista de piezas, y otra con un truco especial. Los comandos de creación de estas son:
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;
Ahora puedes llenar la tabla shoelace_arrive con los datos de la lista de piezas:
SELECT * FROM shoelace_arrive; arr_name | arr_quant ----------+----------- sl3 | 10 sl6 | 20 sl8 | 20 (3 rows)
Echa un vistazo rápido a los datos actuales:
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)
Ahora traslada los cordones llegados:
INSERT INTO shoelace_ok SELECT * FROM shoelace_arrive;
y comprueba los resultados:
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)
Es un largo camino desde el único INSERT ... SELECT hasta estos resultados. Y la descripción de la transformación del árbol de consulta será la última de este capítulo. Primero, está la salida del analizador (parser):
INSERT INTO shoelace_ok SELECT shoelace_arrive.arr_name, shoelace_arrive.arr_quant FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok;
Ahora se aplica la primera regla shoelace_ok_ins y convierte esto en:
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;
y desecha el INSERT original en shoelace_ok. Esta consulta reescrita se pasa de nuevo al sistema de reglas, y la segunda regla aplicada, shoelace_upd, produce:
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;
De nuevo es una regla INSTEAD y el árbol de consulta anterior se desecha. Ten en cuenta que esta consulta todavía utiliza la vista shoelace. Pero el sistema de reglas no ha terminado con este paso, por lo que continúa y le aplica la regla _RETURN, y obtenemos:
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;
Finalmente, se aplica la regla log_shoelace, produciendo el árbol de consulta adicional:
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;
Después de eso, el sistema de reglas se queda sin reglas y devuelve los árboles de consulta generados.
Así que terminamos con dos árboles de consulta finales que son equivalentes a las sentencias SQL:
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;
El resultado es que los datos procedentes de una relación insertados en otra, cambiados a actualizaciones en una tercera, cambiados a actualizar una cuarta más el registro de esa actualización final en una quinta, se reducen a dos consultas.
Hay un pequeño detalle que es un poco feo. Mirando las dos consultas, resulta que la relación shoelace_data aparece dos veces en la tabla de rangos, cuando definitivamente podría reducirse a una. El planificador no lo maneja y, por lo tanto, el plan de ejecución para la salida del sistema de reglas del INSERT será:
Nested Loop
-> Merge Join
-> Seq Scan
-> Sort
-> Seq Scan on s
-> Seq Scan
-> Sort
-> Seq Scan on shoelace_arrive
-> Seq Scan on shoelace_data
mientras que omitir la entrada adicional de la tabla de rangos daría como resultado un:
Merge Join
-> Seq Scan
-> Sort
-> Seq Scan on s
-> Seq Scan
-> Sort
-> Seq Scan on shoelace_arrive
que produce exactamente las mismas entradas en la tabla de registro. Por lo tanto, el sistema de reglas provocó un escaneo adicional en la tabla shoelace_data que es absolutamente innecesario. Y el mismo escaneo redundante se realiza una vez más en el UPDATE. Pero fue un trabajo realmente difícil hacer que todo eso fuera posible.
Ahora hacemos una demostración final del sistema de reglas de PostgreSQL y su poder. Supongamos que añades algunos cordones con colores extraordinarios a tu base de datos:
INSERT INTO shoelace VALUES ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
INSERT INTO shoelace VALUES ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);
Nos gustaría hacer una vista para comprobar qué entradas de shoelace no coinciden con ningún zapato en color. La vista para esto es:
CREATE VIEW shoelace_mismatch AS
SELECT * FROM shoelace WHERE NOT EXISTS
(SELECT shoename FROM shoe WHERE slcolor = sl_color);
Su salida es:
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
Ahora queremos configurarlo para que los cordones que no coincidan y que no estén en stock se eliminen de la base de datos. Para hacérselo un poco más difícil a PostgreSQL, no los eliminamos directamente. En su lugar, creamos una vista más:
CREATE VIEW shoelace_can_delete AS
SELECT * FROM shoelace_mismatch WHERE sl_avail = 0;
y lo hacemos de esta manera:
DELETE FROM shoelace WHERE EXISTS
(SELECT * FROM shoelace_can_delete
WHERE sl_name = shoelace.sl_name);
Los resultados son:
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)
Un DELETE en una vista, con una condición de subconsulta que en total utiliza 4 vistas anidadas/unidas, donde una de ellas tiene una condición de subconsulta que contiene una vista y donde se utilizan columnas de vista calculadas, se reescribe en un único árbol de consulta que elimina los datos solicitados de una tabla real.
Probablemente solo haya unas pocas situaciones en el mundo real donde tal construcción sea necesaria. Pero te hace sentir cómodo saber que funciona.