Reglas sobre INSERT, UPDATE y DELETE

Diferencias con las reglas de las vistas.

Las reglas que se definien para ON INSERT, UPDATE y DELETE son totalmente diferentes de las que se han descrito en la secci�n anterior para las vistas. Primero, su comando CREATE RULE permite m�s:

Segundo, no modifican el �rbol de traducci�n en el sitio. En lugar de ello, crean cero o varios �rboles de traducci�n nuevos y pueden desechar el original.

C�mo trabajan estas reglas

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:

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.

Una primera regla paso a paso.

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.

Cooperaci�n con las vistas

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.

NotaNota
 

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.

NotaLa verdad es
 

Haciendo esto encontr� otro bug mientras escrib�a este documento. Pero tras fijarlo comprob� un poco avergonzado que trabajaba correctamente.