Banco: vistas, disparadores, UPSERT y transacciones
Ocho apartados sobre las cuentas de un banco pequeño que modifican la base de datos: una vista, un disparador que actualiza saldos, otro que ignora operaciones imposibles con RAISE(IGNORE), intereses con UPDATE, comisiones con INSERT … SELECT, un DELETE, un UPSERT y un traspaso en una transacción.
- CREATE VIEW
- CREATE TRIGGER (AFTER y BEFORE)
- NEW y RAISE(IGNORE)
- UPDATE con ROUND
- INSERT … SELECT
- DELETE con subconsulta
- UPSERT (ON CONFLICT)
- BEGIN y COMMIT
Enunciado
Un banco pequeño guarda sus clientes, sus cuentas (corrientes y de ahorro) y los movimientos de cada cuenta: ingresos en positivo y cargos en negativo. La tabla resumen guarda los ingresos y gastos de cada cuenta por mes, y se rellena con un proceso nocturno.
Todos los apartados de este ejercicio cambian algo: crean objetos (vistas y disparadores) o modifican datos. Como en una base de datos real no se puede ver el resultado de un UPDATE directamente, cada apartado dice qué consulta se usará para comprobar el estado final de la base de datos. Si tu sentencia da un error, el apartado no se corrige.
En SQLite los disparadores no pueden usar variables ni bloques como en MySQL o PL/SQL, pero tienen lo esencial: NEW y OLD para la fila afectada, WHEN para la condición y RAISE para detener o ignorar la operación.
Qué hay que hacer
- Escribe la sentencia o sentencias de cada apartado (puedes poner varias separadas por
;). Cada apartado indica la consulta con la que se comprueba. - Cada apartado se corrige por separado sobre una copia nueva de la base de datos, con exactamente los datos de las tablas de arriba: lo que hagas en uno no afecta a los demás.
- Importan las filas, el número y el orden de las columnas y, cuando el apartado pide un orden, el orden de las filas; los nombres de las columnas no importan. Redondea con
ROUNDcuando se pidan decimales.
Las tablas y sus datos
Base de datos del ejercicio · 4 tablas
- clientes
- cuentas
- movimientos
- resumen
Subrayada, la clave primaria; con flecha, la columna a la que apunta cada clave ajena.
| cliente_id | nombre | dni |
|---|---|---|
| 1 | Ana Gil | 12345678Z |
| 2 | Luis Mora | 23456789D |
| 3 | Eva Ruiz | 34567890V |
| 4 | Pedro Sanz | 45678901G |
| cuenta_id | cliente_id | numero | tipo | saldo | abierta | cerrada |
|---|---|---|---|---|---|---|
| 1 | 1 | C-001 | corriente | 1250.4 | 2019-05-10 | NULL |
| 2 | 1 | A-001 | ahorro | 8300 | 2020-01-15 | NULL |
| 3 | 2 | C-002 | corriente | 340.75 | 2021-03-01 | NULL |
| 4 | 3 | C-003 | corriente | 2210 | 2018-11-20 | NULL |
| 5 | 3 | A-002 | ahorro | 950 | 2022-06-30 | NULL |
| 6 | 4 | C-004 | corriente | 0 | 2015-02-01 | 2020-12-31 |
| movimiento_id | cuenta_id | fecha | concepto | importe |
|---|---|---|---|---|
| 1 | 1 | 2026-09-01 | Nómina | 1800 |
| 2 | 1 | 2026-09-03 | Alquiler | -750 |
| 3 | 1 | 2026-09-15 | Supermercado | -120.35 |
| 4 | 3 | 2026-09-01 | Nómina | 1200 |
| 5 | 3 | 2026-09-05 | Hipoteca | -610 |
| 6 | 3 | 2026-09-20 | Luz | -48.2 |
| 7 | 4 | 2026-09-02 | Transferencia recibida | 300 |
| 8 | 2 | 2026-09-30 | Intereses | 10.37 |
| 9 | 6 | 2019-06-01 | Recibo | -25 |
| 10 | 6 | 2020-11-15 | Recibo | -30 |
| 11 | 1 | 2026-10-01 | Nómina | 1800 |
| 12 | 3 | 2026-10-02 | Supermercado | -95.6 |
| cuenta_id | mes | ingresos | gastos |
|---|---|---|---|
| 1 | 2026-08 | 1800 | 900 |
| 1 | 2026-09 | 0 | 0 |
Ver el script SQL que crea la base de datos
1CREATE TABLE clientes (
2 cliente_id INTEGER PRIMARY KEY,
3 nombre TEXT NOT NULL,
4 dni TEXT NOT NULL UNIQUE
5);
6CREATE TABLE cuentas (
7 cuenta_id INTEGER PRIMARY KEY,
8 cliente_id INTEGER NOT NULL REFERENCES clientes(cliente_id),
9 numero TEXT NOT NULL UNIQUE,
10 tipo TEXT NOT NULL CHECK (tipo IN ('corriente', 'ahorro')),
11 saldo REAL NOT NULL,
12 abierta DATE NOT NULL,
13 cerrada DATE
14);
15CREATE TABLE movimientos (
16 movimiento_id INTEGER PRIMARY KEY,
17 cuenta_id INTEGER NOT NULL REFERENCES cuentas(cuenta_id),
18 fecha DATE NOT NULL,
19 concepto TEXT NOT NULL,
20 importe REAL NOT NULL
21);
22CREATE TABLE resumen (
23 cuenta_id INTEGER NOT NULL REFERENCES cuentas(cuenta_id),
24 mes TEXT NOT NULL,
25 ingresos REAL NOT NULL,
26 gastos REAL NOT NULL,
27 PRIMARY KEY (cuenta_id, mes)
28);
29
30INSERT INTO clientes (cliente_id, nombre, dni) VALUES
31 (1, 'Ana Gil', '12345678Z'),
32 (2, 'Luis Mora', '23456789D'),
33 (3, 'Eva Ruiz', '34567890V'),
34 (4, 'Pedro Sanz', '45678901G');
35
36INSERT INTO cuentas (cuenta_id, cliente_id, numero, tipo, saldo, abierta, cerrada) VALUES
37 (1, 1, 'C-001', 'corriente', 1250.4, '2019-05-10', NULL),
38 (2, 1, 'A-001', 'ahorro', 8300, '2020-01-15', NULL),
39 (3, 2, 'C-002', 'corriente', 340.75, '2021-03-01', NULL),
40 (4, 3, 'C-003', 'corriente', 2210, '2018-11-20', NULL),
41 (5, 3, 'A-002', 'ahorro', 950, '2022-06-30', NULL),
42 (6, 4, 'C-004', 'corriente', 0, '2015-02-01', '2020-12-31');
43
44INSERT INTO movimientos (movimiento_id, cuenta_id, fecha, concepto, importe) VALUES
45 (1, 1, '2026-09-01', 'Nómina', 1800),
46 (2, 1, '2026-09-03', 'Alquiler', -750),
47 (3, 1, '2026-09-15', 'Supermercado', -120.35),
48 (4, 3, '2026-09-01', 'Nómina', 1200),
49 (5, 3, '2026-09-05', 'Hipoteca', -610),
50 (6, 3, '2026-09-20', 'Luz', -48.2),
51 (7, 4, '2026-09-02', 'Transferencia recibida', 300),
52 (8, 2, '2026-09-30', 'Intereses', 10.37),
53 (9, 6, '2019-06-01', 'Recibo', -25),
54 (10, 6, '2020-11-15', 'Recibo', -30),
55 (11, 1, '2026-10-01', 'Nómina', 1800),
56 (12, 3, '2026-10-02', 'Supermercado', -95.6);
57
58INSERT INTO resumen (cuenta_id, mes, ingresos, gastos) VALUES
59 (1, '2026-08', 1800, 900),
60 (1, '2026-09', 0, 0);Guía paso a paso
Intenta resolverlo por tu cuenta y abre un paso solo cuando te atasques: cada uno te acerca a la solución sin dártela entera.
1. Cada apartado parte de cero
Cada apartado se ejecuta sobre una copia nueva de la base de datos: el disparador del apartado 2 no existe en el apartado 8. Si un apartado necesita algo, se crea en ese mismo apartado.
2. Un disparador por dentro
Se dispara BEFORE o AFTER una operación sobre una tabla, opcionalmente solo WHEN se cumple una condición. Dentro, NEW es la fila que entra (y OLD la que sale, en UPDATE y DELETE).
CREATE TRIGGER trg_saldo AFTER INSERT ON movimientos
BEGIN
UPDATE cuentas SET saldo = ROUND(saldo + NEW.importe, 2)
WHERE cuenta_id = NEW.cuenta_id;
END;3. Insertar a partir de una consulta
INSERT INTO t (cols) SELECT … crea una fila por cada fila del SELECT, y el SELECT puede mezclar columnas con valores fijos. Es la forma de hacer operaciones masivas sin un bucle.
4. Insertar o actualizar (UPSERT)
ON CONFLICT (clave) DO UPDATE SET … evita tener que comprobar antes si la fila existe. En el SET, excluded.columna es el valor que se intentaba insertar.
5. Comprobar antes de modificar
Antes de un UPDATE o un DELETE, ejecuta un SELECT con el mismo WHERE. Y para ver qué ha hecho tu sentencia, ejecuta después la consulta de comprobación del apartado.
Resuélvelo aquí
Cada apartado se corrige por separado contra la base de datos de arriba: escribe la consulta y pulsa «Comprobar».
Crea la vista v_saldos con el nombre del cliente, el número de cuenta, el tipo y el saldo de las cuentas abiertas (las que no tienen fecha de cierre). Se comprueba con SELECT * FROM v_saldos ORDER BY 2.
Crea el disparador trg_saldo que, después de insertar un movimiento, sume su importe al saldo de su cuenta (redondeando el saldo a dos decimales). Después inserta estos dos movimientos: cuenta 3, 2026-10-03, 'Gimnasio', -39.90; y cuenta 5, 2026-10-03, 'Ingreso', 200. Se comprueba con SELECT cuenta_id, saldo FROM cuentas ORDER BY cuenta_id.
Las cuentas de ahorro no pueden quedarse en negativo. Crea el disparador trg_ahorro que, antes de insertar un movimiento en una cuenta de ahorro, ignore la inserción (sin dar error) si el saldo actual más el importe sería menor que 0. Después inserta: cuenta 2, 2026-10-04, 'Retirada', -9000; cuenta 5, 2026-10-04, 'Retirada', -500; y cuenta 1, 2026-10-04, 'Recibo', -2000. Se comprueba con SELECT cuenta_id, concepto, importe FROM movimientos WHERE fecha = '2026-10-04' ORDER BY cuenta_id.
Aplica los intereses: las cuentas de ahorro abiertas con un saldo de más de 1.000 € ganan un 1,5 %, con el saldo redondeado al céntimo. Se comprueba con SELECT cuenta_id, saldo FROM cuentas ORDER BY cuenta_id.
Cobra la comisión de mantenimiento: inserta un movimiento de -2 € con fecha 2026-10-31 y concepto 'Comisión de mantenimiento' en cada cuenta corriente abierta con un saldo de menos de 1.500 € (no hace falta actualizar el saldo). Se comprueba con SELECT cuenta_id, fecha, concepto, importe FROM movimientos WHERE fecha = '2026-10-31' ORDER BY cuenta_id.