Mantenga en mente la sintaxis
CREATE RULE rule_name AS ON event
TO object [WHERE rule_qualification]
DO [INSTEAD] [action | (actions) | NOTHING]; |
En lo que sigue, "las reglas de update" muestran reglas que están definidas
ON INSERT, UPDATE o DELETE.
Update toma las reglas aplicadas por el sistema de reglas cuando la
relación resultado y el tipo de comando de un árbol de traducción son
iguales al objeto y el acontecimiento dado en el comando CREATE RULE. Para
reglas de update, el sistema de reglas crea una lista de árboles de
traducción. Inicialmente la lista de árboles de traducción está vacía.
Puede haber cero (palabra clave NOTHING), una o múltiples acciones. Para
simplificar, veremos una regla con una acción. Esta regla puede tener una
cualificación o no y puede ser INSTEAD o no.
¿Qué es una cualificación de una regla? Es una restricción que se dice
cuándo las acciones de una regla se deberían realizar y cuándo no. Esta
cualficación sólo se puede referir a las pseudo-relaciones NEW y/o OLD, que
básicamente son la relación dada como objeto (pero con unas características
especiales).
De este modo tenemos cuatro casos que producen los siguientes árboles de
traducción para una regla de una acción:
Sin cualificación ni INSTEAD:
Sin cualificación pero con INSTEAD:
Se da cualificación y no se da INSTEAD:
Se da cualificación y se da INSTEAD:
El árbol de traducción de la acción de la regla a la que se han
añadido la cualificación de la regla y la cualificación de los
árboles de traducción originales.
El árbol de traducción original al que se le ha añadido la
cualificación de la regla negada.
Finalmente, si la regla no es INSTEAD, el árbol de traducción original sin
cambiar se añade a la lista. Puesto que sólo las reglas INSTEAD
cualificadas se añaden al árbol de traducción original, terminamos con un
máximo total de dos árboles de traducción para una regla con una acción.
Los árboles de traducción generados a partir de las acciones de las reglas
se colocan en el sistema de reescritura de nuevo, y puede ser que otras
reglas aplicadas resulten en más o menos árboles de traducción.
De este modo, los árboles de traducción de las acciones de las reglas
deberían tener bien otro tipo de comando, bien otra relación resultado. De
otro modo, este proceso recursivo terminaría en un bucle. Hay un límite de
recursiones compiladas actualmente de 10 iteraciones. Si tras 10
iteraciones aún sigue habiendo reglas de update para aplicar, el sistema de
reglas asumirá que se ha producido un bucle entre muchas definiciones de
reglas y aborta la transacción.
Los árboles de traducción encontrados en las acciones del catálogo de
sistema pg_rewrite son sólo plantillas. Una vez que
ellos pueden hacer referencia a las entradas de tabla de rango para NEW u
OLD, algunas sustituciones habrán sido hechas antes de ser utilizadas. Para
cualquier referencia a NEW, la lista objetivo de la query original se
revisa busando una entrada correspondiente. si se encuentra, esas entradas
de la expresión se sitúan en la referencia. De otro modo, NEW se mantiene
igual que OLD. Cualquier referencia a OLD se reemplaza por una referencia a
la entrada de la tabla de rango que es la relación resultado.
Queremos tracear los cambios en la columna sl_avail de la relación
shoelace_data. Para ello, crearemos una tabla de log,
y una regla que escriba las entradas cada vez que se realice una UPDATE
sobre shoelace_data.
CREATE TABLE shoelace_log (
sl_name char(10), -- shoelace changed
sl_avail integer, -- new available value
log_who name, -- who did it
log_when datetime -- 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,
getpgusername(),
'now'::text
); |
Un detalle interesante es la caracterización de 'now' en la reglas de la
acción INSERT para teclear texto. Sin ello, el traductor vería en el
momento del CREATE RULE, que el tipo objetivo en
shoelace_log es un dato de tipo fecha, e intenta hacer
una constante de él... con éxito. De ese modo, se almacenaría un valor
constante en la acción de la regla y todas las entradas del log tendrían la
hora de la instrucción CREATE RULE. No es eso exactamente lo que queremos.
La caracterización lleva al traductor a construir un "fecha-hora" que será
evaluada en el momento de la ejecución (datetime('now'::text)).
Ahora Al hace
al_bundy=> UPDATE shoelace_data SET sl_avail = 6
al_bundy-> WHERE sl_name = 'sl7'; |
y nosotros miramos en la tabla de log.
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) |
Que es justo lo que nosotros esperábamos. Veamos qué ha ocurrido en la
sombra. El traductor creó un árbol de traducción (esta vez la parte del
árbol de traducción original está resaltado porque la base de las operación
es es la acción de la regla para las reglas de update)
UPDATE shoelace_data SET sl_avail = 6
FROM shoelace_data shoelace_data
WHERE bpchareq(shoelace_data.sl_name, 'sl7'); |
Hay una regla para 'log_shoelace' que es ON UPDATE con la expresión de
cualificación de la regla:
int4ne(NEW.sl_avail, OLD.sl_avail) |
y una acción
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; |
No detallaremos la salida de la vista del sistema pg_rules. Especialmente
manipula la siutación de que aquí sólo se haga referencia a NEW y OLD en la
INSERT, y las salidas del formato de VALUES de INSERT. De hecho, no hay
diferencia entre una INSERT ... VALUES y una INSERT ... SELECT al nivel del
árbol de traducción. Ambos tienen tablas de rango, listas objetivo, pueden
tener cualificación, etc. El optimizador decide más tarde si crear un plan
de ejecución de tio resultado, barrido secuencial, barrido de índice, join
o cualquier otro para ese árbol de traducción. Si no hay referencias en
entradas de la tabla de rango previas al árbol de traducción, éste se
convierte en un plan de ejecución (la versión INSERT ... VALUES). La acción
de las reglas anterior puede ciertamente resultar en ambas variantes.
La regla es una regla no-INSTEAD cualificada, de modo que el sistema de
reglas deberá devolver dos árboles de traducción. La acción de la regla
modificada y el árbol de traducción original. En el primer paso, la tabla
de rango de la query original está incorporada al árbol de traducción de la
acción de las reglas. Esto da como resultado
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; |
En el segundo paso, se añade la cualificación de la regla, de modo que el
resultado se restringe a las filas en las que sl_avail cambie.
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); |
En el tercer paso, se añade la cualificación de los árboles de traducción
originales, restringiendo el juego de resultados más aún, a sólo las filas
tocadas por el árbol de traducción original.
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'); |
En el paso cuatro se sustituyen las referencias NEW por las entradas de la
lista objetivo del árbol de traducción original o con las referencias a
variables correspondientes de la relación resultado.
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'); |
El paso 5 reemplaza las referencias OLD por referencias en la relación
resultado.
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'); |
Y esto es. De modo que la máxima reducción de la salida del sistema de
reglas es una lista de dos árboles de traducción que son lo mismo que las
instrucciones:
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'; |
Estas con ejecutadas en este orden y eso es exactamente lo que la regla
define. Las sustituciones y las cualificaciones añadidas aseguran que si la
query original fuese una
UPDATE shoelace_data SET sl_color = 'green'
WHERE sl_name = 'sl7'; |
no se habría escrito ninguna entrada en la tabla de log, ya que esta vez el
árbol de traducción original no contiene una entrada de la lista objetivo
para sl_avail, de modo que NEW.sl_avail será reemplazada por
shoelace_data.sl_avail resultando en la query adicional
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'; |
cuya cualificación nunca será cierta. Una vez que no hay diferencias a
nivel de árbol de traducción entre una INSERT ... SELECT, y una INSERT ...
VALUES, trabajará también si la query original modificaba multiples
columnas. De modo que si Al hubiese pedido el comando
UPDATE shoelace_data SET sl_avail = 0
WHERE sl_color = 'black'; |
serán actualizadas cuatro filas (sl1, sl2, sl3 y sl4). Pero sl3 ya tiene
sl_avail = 0. Esta vez, la cualificación de los árboles de traducción
originales es diferente y como resultado tenemos el árbol de traducción
adicional
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'; |
Este árbol de traducción seguramente insertará tres nuevas entradas de la
tabla de log. Y eso es absolútamente correcto.
Es importante recordar que el árbol de traducción original se ejecuta el
último. El "agente de tráfico" de Postgres
incrementa el contador de comandos entre la ejecución de los dos árboles de
traducción, de modo que el segundo puede ver cambios realizados por el
primero. Si la UPDATE hubiera sido ejecutada primero, todas las filas
estarían ya a 0, de modo que la INSERT del logging no habría encontrado
ninguna fila para las que shoelace_data.sl_avail != 0: no habría dejado
ningún rastro.
Una forma sencilla de proteger las relaciones vista de la mencionada
posibilidad de que alguien pueda INSERT, UPDATE y DELETE datos invisibles
es permitir a sus árboles de traducción recorrerlas de nuevo.
Creamos 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 Al ahora intenta hacer cualquiera de estas operaciones en la relación
vista
shoe, el sistema de reglas aplicará las reglas.
Una vez que las reglas no tiene acciones y son INSTEAD, la lista resultante
de árboles de traducción estará vacía, y la query no devolverá nada, debido
a que no hay nada para ser optimizado o ejecutado tras la actuación del
sistema de reglas.
| Nota |
---|
| Este hecho debería irritar a las aplicaciones cliente, ya que no ocurre
absolútamente nada en la base de datos, y por ello, el servidor no devuelve
nada para la query. Ni siquiera un PGRES_EMPTY_QUERY o similar será
utilizable en libpq. En psql, no ocurre nada. Esto debería cambiar en el
futuro.
|
Una forma más sofisticada de utilizar el sistema de reglas es crear reglas
que reescriban el árbol de traducción en uno que haga la operación correcta
en las tablas reales. Para hacer esto en la vista
shoelace, crearemos 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; |
Ahora llega un paquete de cordones de zapatos a la tienda de Al, y el tiene
una gran lista de artículos. Al no es particularmente bueno haciendo
cálculos, y no lo queremos actualizando manualmente la vista shoelace. En
su lugar, creamos dos tablas pequeñas, una donde él pueda insertar los
datos de la lista de artículos, y otra con un truco especial.
Los comandos CREATE completos son:
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; |
ahora Al puede sentarse y hacer algo como:
al_bundy=> SELECT * FROM shoelace_arrive;
arr_name |arr_quant
----------+---------
sl3 | 10
sl6 | 20
sl8 | 20
(3 rows) |
Que es exactametne lo que había en la lista de artículos. Daremos una
rápida mirada en los datos actuales.
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) |
trasladamos los cordones recien llegados:
al_bundy=> INSERT INTO shoelace_ok SELECT * FROM shoelace_arrive; |
y comprobamos los resultados:
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) |
Esta es una larga vía desde la primera INSERT ... SELECT a estos
resultados. Y su descripción será la última en este documento (pero no el
último ejemplo :-). Primero estaba la salida de los traductores:
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_in' y se vuelve:
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); |
y lanza otra vez la INSERT original sobre
shoelace_ok.
Esta query reescrita se pasa al sistema de reglas de nuevo, y la aplicación
de la segunda regla 'shoelace_upd' produce
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); |
Otra vez es una regla INSTEAD, y el árbol de traducción anterior se
deshecha. Nótese que esta query aún utiliza la vista
shoelace. Pero el sistema de reglas no ha terminado
con esta vuelta, de modo que continúa y aplica la regla '_RETshoelace',
produciendo
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); |
De nuevo se ha aplicado una regla de update y por ello vuelve a girar la
rueda, y llegamos a la ronda de reescritura número 3. Esta vez, se aplica
la regla 'log_shoelace', que produce el árbol de traducción extra
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); |
Tras de lo cual, el sistema de reglas se desconecta y devuelve los árboles
de traducción generados. De esta forma, terminamos con dos árboles de
traducción finales que son iguales a las instrucciones de
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; |
El resultado es que los datos vienen de una relación, se insertan en otra,
cambian por actualizaciones una tercera, cambian por actualizaciones una
cuarta, más registran esa actualización final en una quinta: todo eso se
reduce a dos queries.
Hay un pequeño detalle un tanto desagradable. Mirando en las dos queries,
descrubrimos que la relación shoelace_data aparece dos
veces en la tabla de rango, lo que se debería reducir a una sóla.
El optimizador no manipula esto, y por ello el plan de ejecución para la
salida del sistema de reglas de la 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 omitiendo la entrada extra a la tabla de rango debería ser
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 relación de log. Es
decir, el sistema de reglás ha probocado un barrido extra de la relación
shoelace_data absolútamente innecesario. Y el mismo
barrido obsoleto se produce de nuevo en la UPDATE. Pero era un trabajo
realmente duro hacer que todo sea posible.
Una demostración final del sistema de reglas de
Postgres y de su poder. Hay una astuta rubia que
vende cordones de zapatos. Y lo que Al nunca hubiese imaginado, ella no es
sólo astuta, también es elegante, un poco demasiado elegante. Por ello,
ella se empeña de tiempo en tiempo en que Al pida cordones que son
absolútamente invendibles. Esta vez ha pedido 1000 pares de cordones
magenta, y aunque ahora no es posible adquirir otro color, como él se
comprometió a comprar algo, prepara su base de datos para cordones rosa.
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); |
Ahora quiere revisar los cordones que no casan con ningún par de zapatos.
El podría realizar una complicada query cada vez, o bien le podemos
preparar una vista al efecto:
CREATE VIEW shoelace_obsolete AS
SELECT * FROM shoelace WHERE NOT EXISTS
(SELECT shoename FROM shoe WHERE slcolor = sl_color); |
cuya salida es
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 |
Sobre los 1000 cordones magenta, deberíamos avisar a Al antes de que
podamos hacerlo de nuevo, pero ese es otro problema. La entrada rosa, la
borramos. Para hacerlo un poco más dificil para
Postgres, no la borramos directamente. En su
lugar, crearemos una nueva vista
CREATE VIEW shoelace_candelete AS
SELECT * FROM shoelace_obsolete WHERE sl_avail = 0; |
Y lo haremos de esta forma:
DELETE FROM shoelace WHERE EXISTS
(SELECT * FROM shoelace_candelete
WHERE sl_name = shoelace.sl_name); |
Voila:
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) |
Una DELETE en una vista, con una subselect como cualificación, que en total
utiliza 4 vistas anidadas/cruzadas, donde una de ellas mismas tiene una
subselect de cualificación conteniendo una vista y donde se utilizan
columnas calculadas queda reescrita en un único árbol de traducción que
borra los datos requeridos de una tabla real.
Pienso que hay muy pocas ocasiones en el mundo real en las que se una
construcción similar sea necesaria. Pero me tranquiliza un poco que esto
funcione.
| La verdad es |
---|
| Haciendo esto encontré otro bug mientras escribía este documento. Pero
tras fijarlo comprobé un poco avergonzado que trabajaba correctamente.
|