Tema Título Subtema Versión
04 El modelo físco: SQL PARTE IV v 1.0
04 Vistas y Transacciones

TEMA 4: El modelo físico: SQL

DDL: Vistas

Introducción

Supongamos que tenemos la tabla:

  • ALUMNOS = codigo + nombre + edad + grupo + domicilio

Ya vimos que a partir del resultado de una consulta SELECT podíamos crear una tabla:

   CREATE TABLE alumnos_menores AS
      SELECT codigo, nombre, grupo, edad
         FROM alumnos
         WHERE edad <18;

Esa instrucción creará una nueva tabla con las columnas especificadas en la SELECT (codigo, nombre, grupo y edad) y con las filas que cumplan la WHERE (menores de edad). Ahora bien: los cambios que se hagan en la tabla alumnos no implicarán cambios en la tabla alumnos_menores ni viceversa:

  • Si insertamos un nuevo alumno de 15 años en la tabla alumnos, ese alumno no estará en la tabla alumnos_menores (ni viceversa).
  • Si borramos un alumno de alumnos_menores, no se borrará de alumnos (ni viceversa).
  • Si en la tabla de alumnos modificamos la edad alumno que antes era menor de edad y hacemos que ahora tenga 18 años, el alumno no desaparecerá de la tabla alumnos_menores

Queremos solucionar esto. Es decir: queremos crear una tabla especial a partir de otra, de forma que los cambios de una se reflejen en la otra. Realmente, lo que haremos no será copiar los datos de una tabla a otra, sino que crearemos una ventana a partir de la que ver los datos que queremos de la tabla. Esto se logra con las vistas:

CREATE VIEW alu_men AS
   SELECTcódigo, nombre, grupo, edad
   FROM alumnos
   WHERE edad < 18;

Así, con la "ventana” (vista) llamada alu_men, podremos ver el código, nombre, grupo y edad de los alumnos de la tabla alumnos que sean menores de 18 años. Por tanto, si insertamos un alumno nuevo de 1DAM en la tabla alumnos, también lo veremos desde la vista alu_men.

Estas dos consultas ahora son equivalentes:

Con vista

SELECT *
    FROM alu_men
    WHERE grupo = "1DAM";

Sin la vista

SELECT nombre, edad
    FROM alumnos
    WHERE edad < 18 AND grupo = "1DAM";

¿Qué es una vista?

Una vista es una tabla virtual, ya que no tiene existencia propia aunque exteriormente lo parezca. Es decir, no existe físicamente (no ocupa espacio en disco duro), sino que es como una ventana a partir de la que sólo podemos ver una parte (filas y columnas) de las tablas sobre las que está definida.

Creación de una vista

Sintaxis

CREATE VIEW nombre_vista [ (lista_nombre_col) ]
AS sentencia_select
    [ WITH CHECK OPTION ]

Nombre de las columnas que tendrá la vista. Es opcional.

Tipo de la select de la vista:

La select sobre la que se define la vista puede ser de cualquier tipo: puede tener columnas calculadas (expresiones), puede ser multi-tabla, puede tener uniones, group by, subconsultas....

También podemos crear vistas a partir de selects sobre otras vistas.

Operaciones que se pueden realizar sobre la vista:

En principio, una vista es como una tabla. Por tanto, podremos hacer sobre ella: select, insert, update, delete.

Modificación de vistas

hay que tener en cuenta que las sentencias sobre la vista "se traducen" a sentencias sobre la tabla (o tablas) sobre las que está definida. Por tanto, siempre podremos hacer select sobre ellas pero no siempre un insert/delete/update sobre la vista se podrá traducir a un insert/delete/update sobre la tabla (o tablas) correspondiente. Por ejemplo, si una vista está definida con un group by, si intentamos realizar un update sobre la vista, no habrá traducción para hacer update sobre la tabla correspondiente. Por ejemplo, supongamos la siguiente vista:

   CREATE VIEW cantidades AS
      SELECT grupo, count(*) AS cantidad
         FROM alumnos
            GROUP BY grupo;
¿Podremos hacer la siguiente sentencia sobre la vista?

   UPDATE cantidades
      SET cantidad = 10
         WHERE grupo = "1ASIX"

La respuesta es no. En ese caso se dice que la vista no es actualizable.

Vistas actualizables

Una vista es actualizable si la select sobre la que está definida no tiene “cosas raras”: no está creada con UNION ni DISTINCT, ni GROUP BY, ni subconsultas, ni multi-tabla...

Cláusula WITH CHECK OPTION

Si en la definición de una vista actualizable se incluye la cláusula WITH CHECK OPTION, esta no permitirá inserts o updates que no cumplan las condiciones de la vista. Por ejemplo, si la vista alu_men la hubiese creado con esta cláusula,

las siguientes instrucciones darían error, ya que al finalizar los registros dejarian de pertencer a la vista:

INSERT INTO alu_men VALUES (100, Pep, 1DAM,20); -- es mayor de edad
UPDATE alu_men SET edad = 18 WHERE edad = 17; -- queremos hacerlos mayores de edad

Destrucción de una vista (sintaxis):

DROP VIEW nombre_vista [ RESTRICT | CASCADE ];
  • RESTRICT: no deja borrar la vista si existen otras vistas definidas sobre ella.
  • CASCADE: si hay otras vistas definidas sobre ella, también las borra.

MySQL acepta el RESTRICT y CASCADE, pero pasa de ellos. Es decir: deja borrar una vista V1 aunque exista alguna otra vista V2 definida a partir de ella. Si hicimos una select sobre V2 daría error. En otros SGBD, como PostgreSQL, sí funciona.

Utilidades de las vistas

  • Permitir tener en una tabla información derivada de otras tablas, de forma que las modificaciones en esas tablas primitivas también se reflejen en la “tabla derivada” (vista).

  • Facilitar la construcción de selectos complejos. Así podremos realizar selectos sobre otros “selects” ya hechas.

  • Permitir dar permisos sobre partes de una tabla (ya veremos el tema de permisos).

Ejercicios de Vistas

Crear una vista

Ejercicios, creación de vistas

  1. Crea la vista jug_sue con el dorsal, nombre y lugar de los jugadores del equipo de código sue (no existe en la tabla de jugadores, pero da igual). No le pones la cláusula del check option.
  2. Comprueba el contenido de la vista jug_sue. Habrá que hacer un SELECT sobre la vista. Verás que no tiene nada, puesto que no hay jugadores de ese equipo.

Comprobación del funcionamiento de la vista

Ejercicios sobre vistas

  1. Inserta el equipo tav (Tavernes CF) y el sue (Sueca United) en la tabla de equipos.
  2. Inserta en la tabla de jugadores a dos nuevos delanteros: Pep, del Tavernes CF y Pau, del Sueca United.
  3. Comprueba el contenido de la vista jug_sue. Verás que ahora la vista “sí tiene” a un jugador (Pau). Realmente “no lo tiene”, pero a través de la vista estamos mirando a los jugadores de la tabla de jugadores que son del Sueca United.
  4. Haz que el jugador del Tavernes CF ahora fiche por el Sueca United (si es necesario, cambiarle también el dorsal). Habrá que hacer un UPDATE.
  5. Vuelve a comprobar el contenido de la vista jug_sue. Ahora estaremos viendo a dos jugadores (Pau y Pep).
  6. Borra a Pep de la tabla jugadores. Hay que hacer DELETE sobre la tabla.
  7. Borra a Pau a través de la vista jug_sue. Hay que hacer DELETE sobre la vista.
  8. Comprueba el contenido de la vista jug_sue. Ya no estará ninguno de los dos. Comprueba que tampoco están en la tabla jugadores.
  9. Trata de insertar a Pau a través de la vista jug_sue. Te dará error porque en el INSERT no le ponemos el código del equipo y, por tanto, cuando se intente insertar en la tabla de jugadores no admitirá un NULL en el campo del equipo. Pero si no fuera por eso, sí se permite insertar a través de una vista.

Eliminar una vista

Ejercicios sobre eliminación vistas

  1. Elimina la vista anterior (jug_sue). No borres sus registros, sino destrúyela. Habrá que hacer un DROP de la vista. Esto no afecta a la tabla sobre la que estaba definida.

Crear una vista con el CHECK OPTION

Ejercicios sobre vistas con CHECK OPTION

  1. Crea la vista equipetes con el código, nombre y presupuesto de todos los equipos que tengan un presupuesto inferior a 30 millones de euros. Hazlo con el CHECK OPTION.
  2. Inserta a través de la vista equipetes estos equipos, en 2 INSERT:
    • Equipo gan, “CF Gandia”, con 0 millones de euros de presupuesto.
    • Equipo and, “Andorra CF”, con 31 millones de euros de presupuesto. Debe dar error por no cumplir la condición del CHECK OPTION.
  3. Borra a través de la vista equipetes los equipos de más de 40 millones de presupuesto. No dará error, pero no borrará ningún equipo debido al CHECK OPTION.
  4. A través de la vista equipetes, haz que el nuevo presupuesto del CF Gandia sea ahora de 31 millones. Dará error por no cumplir el CHECK OPTION.

Crear una vista no actualizable

Ejercicios sobre vistas no actualizables

  1. Crea la vista equipos_numerosos con el código del equipo, el nombre corto, el nombre de la ciudad y la cantidad de jugadores de cada uno. Pero sólo con aquellos equipos que tengan a más de 30 jugadores en plantilla. Los nombres de las columnas serán: código, nombre, ciudad, plantilla.
  2. En la vista equipos_numerosos, modifica el nombre del equipo del Betis (código bet): ahora se llamará “Betis”. Dará un error diciendo que la vista no es actualizable. No lo es porque, como tiene GROUP BY (y es multi-tabla), no se puede “traducir” ese UPDATE sobre la vista a un UPDATE sobre tablas. Lo mismo ocurriría si intentamos hacer INSERT o DELETE sobre esa vista.

Crear una vista a partir de otra (ejercicio solucionado)

Ejercicio 1 sobre vistas a partir de otras vistas

Crea la vista resultados_equipos con estos campos:

    - `equipo`: código del equipo
    - `pgc`: cantidad de Partidos Ganados en Casa
    - `pec`: cantidad de Partidos Empatados en Casa
    - `ppc`: cantidad de Partidos Perdidos en Casa
    - `pgf`: cantidad de Partidos Ganados Fuera
    - `pef`: cantidad de Partidos Empatados Fuera
    - `ppf`: cantidad de Partidos Perdidos Fuera

Este tipo de consulta se realiza con subconsultas dentro de la propia cláusula select. No te preocupes si no te sale. Estas consultas sólo se han visto en algún ejercicio. La solución sería esta:

```sql
create view resultados_equipos as
    select código en equipo,
        (select count(*) from partidos where equipc=e.codi and golsc>golsf) as pgc,
        (select count(*) from partidos where equipc=e.codi and golsc=golsf) as pec,
        (select count(*) from partidos where equipc=e.codi and golsc<golsf) as ppc,
        (select count(*) from partidos where equipf=e.codi and golsf>golsc) as pgf,
        (select count(*) from partidos where equipf=e.codi and golsf=golsc) as pef,
        (select count(*) from partidos where equipf=e.codi and golsf<golsc) as ppf
from equipos e;
```

Ejercicio 2 sobre vistas a partir de otras vistas

Crea la vista classif con los siguientes campos. Créala a partir de la vista resultados_equipos:

| Campo | Descripción |
| -- | -- |
| equipo    |  Código del equipo
| pjc   |  Partidos Jugados en Casa | 
| pgc   |  Partidos Ganadosen Casa |
| pec   |  Partidos Empatados en Casa |
| ppc   |  Partidos Perdidos en Casa |
| puntosc |  Puntos en Casa |
| pjf   |  Partidos Jugados Fuera |
| pgf   |  Partidos Ganados Fuera |
| pef   |  Partidos Empatados Fuera |
| ppf   |  Partidos Perdidos Fuera |
| puntosf |  Puntos Fuera |
| pjt   |  Partidos Jugados en Total |
| pgt   |  Partidos Ganados en Total |
| pedo |  Partidos Empatados en Total |
| ppt   |  Partidos Perdidos en Total |
| puntost |  Puntos en Total |

La solución sería:

```sql
create view classif as
    select equipo, (pgc + pec + ppc) as pjc,
        pgc, pec, ppc, (3 * pgc + pec) as puntosc,
        (pgf + pef + ppf) as pjf,
        pgf, pef, ppf, (3 * pgf + pef) as puntosf,
        (pgc + pec + ppc + pgf + pef + ppf) as pjt,
        (pgc+ pgf) as pgt,
        (pec + pef) as pedo,
        (ppc + ppf) as ppt,
        (3 * pgc + pec + 3 * pgf + pef) as puntost 
    from resultados_equipos;
```

Ejercicio3 sobre vistas a partir de otras vistas

Crea la vista classif2 con los campos de classif más otros campos calculados: goles marcados (en casa, fuera y en total), recibidos (en casa, fuera y en total) y los que quieras.

```sql
create view classif2 as
    select *,   
        (select sum(golsco) -- Goles Marcados en Casa
                from partidos
                where partidos.equipc = classif.equipo) as gmc,
        (select sum(golsf)  -- Goles Marcados Fuera
                from partidos
                where partidos.equipf = classif.equip) as gmf,

        (select sum(golsco) -- Goles Marcados en Total
                from partidos
                where partits.equipc = classif.equipo)
        + (select sum(golsf)
                from partidos
                where partidos.equipf = classif.equipo) as gmt,
        (select sum(golsf)  -- Goles Recibidos en Casa
                from partidos
                where partidos.equipc = classif.equipo) as grc,
        (select sum(golsco) -- Goles Recibidos Fuera
                from partidos
                where partidos.equipf = classif.equip) as grf,
        (select sum(golsf)  -- Goles Recibidos en Total
                from partidos
                where partits.equipc = classif.equipo)
        + (select sum(golsco)
                from partidos
                where partidos.equipf = classif.equip) as grt
    from classif
```

Ejemplos de Consultas sobre las vistas.

Las vistas que hemos realizado nos servirán para hacer más fáciles consultas como las siguientes:

  1. Muestra la tabla clasificatoria (vista classif) ordenada por los puntos totales descendentes. También debe salir (en la primera columna) el nombre largo del equipo.

    select equipos.nombrelargo, clasif.*
    from classif, equipos
    where classif.equip = equipos.código
    order by puntt desc;
    
  2. Equipos que han logrado más del doble de puntos en casa que fuera de casa. Muestra también los puntos totales en casa, los puntos totales fuera y los puntos totales. Decreciente por los puntos en casa.

    select equipo, puntosc, puntosf, puntost
    from classif
    where puntosc > 2 * puntsf
    order by puntosc desc;
    
  3. Muestra los puntos que tienen los equipos de la ciudad con más habitantes y de la ciudad con menos habitantes. Muestra también el nombre de la ciudad, el nombre corto del equipo y los habitantes de la ciudad.

    select ciudades.nombre en la ciudad, equipos.nombre en el equipo,
    ciudades.habitantes, classif.puntst
    from ciudades, equipos, clasif
    where classif.equip = equipos.código and equipos.ciudad = ciudades.código
        and habitantes in (select max(habitantes)
                            from ciudades, equipos
                            where ciudads.código = equipos.ciudad
                            union
                            select min(habitantes)
                            from ciudades, equipos
                            where ciudads.código = equipos.ciudad)
    order by 1, 2;
    

TCL: Transacciones

Definición

Las transacciones son un concepto fundamental de todos los sistemas de base de datos. Una transacción es un conjunto de sentencias INSERT, UPDATE y/o DELETE que constituyen una operación única. Es decir: podremos hacer que se ejecuten todas las sentencias de la transacción o ninguna.

Las sentencias entre BEGIN y COMMIT se llaman bloque de transacción o transacción.

7.2. Sintaxis

   BEGIN;
   (SENTENCIA 1);
   (SENTENCIA 2);
   ...
   (SENTENCIA N);
   COMMIT;
   -- Se confirma la transacción
   BEGIN;
   (SENTENCIA 1);
   (SENTENCIA 2);
   ...
   (SENTENCIA N);
   ROLLBACK;
   -- Se anula la transacción

Las sentencias que queremos que formen parte de una transacción deben empezar con el mando BEGIN (o START TRANSACTION) y acabar con COMMIT (para que se hagan efectivas las sentencias) o ROLLBACK (para anular las sentencias de la transacción ):

Utilidades

  1. Si ocurre algún error durante la transacción, podemos anular la transacción y así ninguno de los pasos afectará a la base de datos (se deshacen las acciones realizadas).

  2. Los estados intermedios en una transacción sí que es visible en esa transacción pero no en otras transacciones concurrentes. Es decir, otras sesiones no ven ningún cambio de esa transacción hasta que no se ha realizado el COMMIT.

  3. Problema del huevo y la gallina (inserción en dos tablas interdependientes).

  4. Podemos deshacer las acciones realizadas mientras no confirmamos la transacción.

  5. Podemos deshacer parte de las acciones mientras no confirmamos la transacción (SAVEPOINT)

Ejemplo utilidad 1: prevención de errores inesperados**

Pensamos en una base de datos de un banco que contiene saldos de cuentas de los clientes. Queremos registrar un pago de 100€ desde la cuenta de Alicia hasta la cuenta de Pep:

UPDATE cuentas
   SET saldo = saldo - 100
   WHERE nombre = 'Alicia';

UPDATE cuentas
   SET saldo = saldo + 100
   WHERE nombre = 'Pep';

Este tipo de operaciones suele ser más complicado e implicar a más sentencias (introducir la transferencia en un histórico de movimientos, etc). Pero lo que importa es que existe más de una sentencia de actualización para realizar esta transferencia bancaria.

Los clientes de nuestro banco querrán que se les asegure que: o bien estas 2 actualizaciones se realizan, o que ninguna de ellas se hace. No quisieran que el saldo se decrementara de Alicia y no se incrementara en Pep.

Necesitamos una garantía de que si existe algún error en alguna de esas operaciones anteriores, que ninguno de los pasos ejecutados surta efecto. Agrupar las actualizaciones en una transacción nos da esta garantía:

BEGIN;
UPDATE cuentas SET saldo = saldo  100 WHERE nombre = 'Alicia';
UPDATE cuentas SET saldo = saldo + 100 WHERE nombre = 'Pep';
COMMIT;

Si después del 1º UPDATE se perdiera la conexión con la BD, no se haría el 2º UPDATE pero tampoco el COMMIT. Esto hace que los efectos del 1º UPDATE no se hagan efectivos realmente en la base de datos.

Ejemplo utilidad 2: cambios intermedios transparentes a otras sesiones

Otra propiedad importante de bases de datos transaccionales está relacionada con la idea de actualizaciones atómicas: cuando las transacciones múltiples están corriendo concurrentemente (es decir: simultáneamente, al mismo tiempo), cada una no debe ver los cambios incompletos realizados por otros.

Por ejemplo, si una transacción está ocupada sumando todos los saldos de las cuentas, no sumará los saldos hasta que no haya terminado la transacción que está realizando el traspaso de una cuenta a otra.

SESIÓN 1 SESIÓN 2

BEGIN;

UPDATE cuentas

SET saldo = saldo - 100

WHERE nombre = 'Alicia';

UPDATE cuentas

SET saldo = saldo + 100

WHERE nombre = 'Pep';

CONMITO;

SELECT SUM(saldo)

FROM cuentas

Si la sesión 2 se ejecuta en el momento en que termina el primer UPDATE, no verá los efectos de ese UPDATE. Si no hubiéramos puesto la sesión 1 dentro de una transacción, la sesión 2 se habría dejado 100 € para sumar, ya que hubiera sumado el saldo de Alicia sin los 100 y el saldo de Pep sin haberlo sumado todavía .

Ejemplo utilidad 3: problema del huevo y la gallina

Supongamos una base de datos de una academia de informática, en la que hay tantos alumnos como ordenadores. Cada alumno debe tener asignado un ordenador. Y cada ordenador debe tener asignado un alumno:

Diseño de tablas

  • ORDENADORES = codigo + carac + alumno
    • Caj. alumno -> ALUMNOS(exp)
  • ALUMNOS = exp + nombre + ordenador
    • Caj. ordenador -> ORDENADORES(codigo)

Nota: Este diseño tiene redundancia de información, pero nos sirve para el ejemplo.

Para introducir un ordenador deberá tener asignado un alumno, pero para introducir un alumno deberá tener asignado un ordenador.

Problema: el SGBD no nos dejará insertar nada (problema del huevo y la gallina).

Solución: en la definición de las claves ajenas, debemos poner la propiedad DEFERRABLE, que hará que las comprobaciones de existencia se realicen en el momento en que termine la transacción. Pero! MySQL no admite el DEFERRABLE. Sí que es admitido por SGBD como Oracle o PostgreSQL.

Create table ordenadores (
   Código integer primary key,
   Caraco char(50),
   Alumno integer not null references alumnos DEFERRABLE);

Create table alumnos (
   Exp integer primary key,
   Nombre char(40),
   Ordenador integer not null references ordenadores DEFERRABLE);

BEGIN;
   Insert into ordenadores(1, 'Pentium III', 1001);
   Insert into alumnos(1001, 'Pep', 1);
COMMIT;

Cuando insertamos el ordenador, no se comprueba que exista el alumno 1001, ya que al ser su clave ajena de tipo DEFERRABLE, lo comprobará en el momento del CONMIT. Lo mismo ocurre en la inserción del alumno. Cuando hagamos el COMMIT, se comprobarán las 2 cosas y sí se cumplirán.

Ejemplo utilidad 4: deshacer cambios voluntariamente**

Si durante la ejecución de la transacción decidimos que no queremos terminarla (quizás nos damos cuenta de que el saldo de Alicia se hacía negativo), podemos ejecutar el comando ROLLBACK en lugar de COMMIT. Esto hará que se anule el efecto de las actualizaciones de la transacción que estaban hechas:

   BEGIN;
   UPDATE cuentas
      SET saldo = saldo - 100
      WHERE nombre = 'Alicia';
-- Aquí nos damos cuenta de que la cuenta de Alicia es negativa.

-- Por tanto, queremos anular el update anterior, y haremos el ROLLBACK
   ROLLBACK;

Ejemplo utilidad 5: deshacer parcialmente la transacción en curso

Es posible controlar las acciones en una transacción de forma más detallada. Es decir, en lugar de descartar toda la transacción, sólo podemos descartar parte de la transacción.

Esto se hace con los SAVEPOINTS.

SAVEPOINTS

Los savepoints son “puntos de salvamento”, que permiten descartar selectivamente partes de la transacción (admitiendo al resto de la transacción).

Ejemplo:

Supongamos que traspasamos 100 € de la cuenta de Alicia a la cuenta de Pep, pero que, nada más haberlo hecho, nos damos cuenta de que queríamos traspasarlo a la cuenta de María y no a la de Pep. Si nos hubiéramos curado en salud utilizando transacciones y savepoints, podríamos deshacer sólo unas sentencias de la transacción y no toda:

BEGIN;
   UPDATE cuentas 
      SET saldo = saldo - 100.00
      WHERE nombre = 'Alicia';

SAVEPOINT seguridad1;

   UPDATE cuentas 
      SET saldo = saldo + 100.00
      WHERE nombre = 'Pep';

-- ¡Ay, no! No debía haberle sumado a Pepe, sino a María.

ROLLBACK TO seguridad1;

UPDATE cuentas 
   SET saldo = saldo + 100.00
   WHERE nombre = 'María';

COMMIT;

Después de haber definido un savepoint con SAVEPOINT nombre_de_savepoint, podré retroceder, si es necesario, a ese punto mediante el comando ROLLBACK TO nombre_de_savepoint.

Las acciones realizadas en la transacción entre la definición del SAVEPOINT y el ROLLBACK TO correspondiente se descartan, pero se quedan los cambios anteriores a la definición del SAVEPOINT.

Podemos retroceder a un savepoint siempre que queramos:

BEGIN;

   Acción 1;
   Acción 2;
   SAVEPOINT seguro1;
   Acción 3;
   Acción 4;
   ROLLBACK TO seguro1;
   Acción 5;
   ROLLBACK TO seguro1;
   ...
COMMIT;

Ahora bien, si retrocediendo a un savepoint en concreto, se borrarán automáticamente todos los savepoints definidos después de ese savepoint. Esto lo hace el sistema para liberar recursos.

BEGIN;
   Acción 1;
   Acción 2;
   SAVEPOINT seguro1;
   Acción 3;
   Acción 4;
   SAVEPOINT seguro2;
   Acción 5;
   ROLLBACK TO seguro2
   Acción 6;
   ROLLBACK TO seguro1;**-- Aquí se borrará el SAVEPOINT seguro2
   ...
   COMMIT;

MySQL

Aunque no trabajamos con transacciones, MySQL trata cada sentencia SQL como si fuera una transacción de una sola sentencia:

Es decir, si por ejemplo se está haciendo un UPDATE muy costoso, mientras dura la actualización, cualquier otro intento de actualización de la tabla será paralizado hasta que termine el primer UPDATE.

Ejercicios sobre transacciones

Ejercicio 1

Comprueba que se pueden deshacer los cambios de una transacción no terminada:

1. Inicia una transacción
2. Haz varios update/delete en algunas tablas
3. Anula la transacción
4. Comprueba que los cambios que has realizado no han surtido efecto.

Ejercicio 2

Comprueba que mientras una transacción no termina, otra no puede ver los cambios realizados:

1. Inicia una transacción y realiza algún cambio (update/delete) en una tabla y comprueba que, de momento, los cambios se pueden ver.
2. Abre en otra sesión otra conexión a la misma bd:
3. Comprueba en esta 2ª sesión que los cambios realizados en la 1ª sesión no se pueden ver (ya que no está terminada la transacción).
4. Ve a la 1ª sesión y confirma la transacción.
5. Ve a la 2ª sesión y comprueba que ahora  están los cambios hechos.

Ejercicio 3

Comprueba que mientras una transacción no termina, si había hecho una modificación en alguna tabla, otra sesión no puede hacer ninguna modificación en esa tabla:

1. Inicia una transacción y realiza algún cambio (update/delete) en una tabla y comprueba que,
de momento, los cambios se pueden ver.
2. Abre en otra sesión otra conexión a la misma bd.
3. Intenta realizar un cambio (update/delete) en la misma tabla. 
Verás cómo el servidor deja en espera esa petición esperando a que termine 
la transacción de la primera sesión:

Comprueba que, cuando termine la transacción de la 1ª sesión (tanto si la confirmas como si la terminas), el update/delete de la 2ª sesión acabará de inmediato.

Comprueba el funcionamiento de los savepoints:

1. Inicia una transacción y realiza algún cambio en una tabla
2. Pone un savepoint
3. Haz algún otro cambio
4. Cancela la transacción sólo hasta el savepoint
5. Confirma la transacción y comprueba cuál de los dos cambios ha quedado.