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