Nota: Si este artículo te parece demasiado largo, pídele a una IA que te lo resuma.
Luego, si quieres, léelo y verás lo que te perdiste.
Desde el estándar SQL-92 existen las “Assertions”, pero ningún motor de base de datos las implementaba hasta ahora.
Podríamos definir una SQL Assertion como una constraint avanzada que permite una comprobación cruzada entre múltiples tablas o entre filas de la misma tabla. Pongo ejemplos más adelante.
En la práctica, este tipo de condiciones sólo podían implementarse como triggers y, además de la complejidad de la lógica de implementación, resultaba muy difícil garantizar su integridad y persistencia. Por no decir que chequear estas condiciones en cada DML podría impactar considerablemente el rendimiento.
Cada vez que daba un curso de SQL y aparecía el tema, siempre indicaba que “eso se explicaba en PL/SQL”, mencionando los triggers.
Y entonces, cuando llegaba el momento de explicar los triggers y para qué eran útiles, los definía como “una capa de PL/SQL pegada a una tabla, sensible a insert/update/delete, capaz de registrar auditorías o trazas complejas, o de mantener restricciones de integridad avanzada entre diferentes tablas”.
Me refería a estas SQL Assertions y, en más de 25 años, no he podido impartir esta lección porque ningún motor de base de datos las implementaba.
Hasta ahora.
Las SQL Assertions son, por fin, una nueva funcionalidad de Oracle26ai. Cuidado, sólo en la versión 23.26.1 (es decir, con el parche RU de enero de 2026 aplicado).
Así que vamos con todo.
Casos prácticos
Vamos a pensar en un primer escenario para el dataset de vuelos en el que podamos aplicar estas SQL Assertions. Si quieres generar el modelo de tablas, lo tienes en el repositorio de GitHub de Café Database aquí.
https://github.com/CafeDatabase/vuelos-dataset
Tenemos VUELOS con capacidades distintas. Estas pueden ser de 50, 60, 100 o 120 plazas. Algo así como si tuviéramos cuatro tipos de aviones.
En el modelo cargado tenemos, para cada vuelo, tantas plazas en la tabla PLAZAS como la capacidad total del avión de ese vuelo. Un vuelo en un avión con capacidad para 50 plazas tiene 50 filas en la tabla PLAZAS. Los vuelos con 100 plazas tienen 100 filas en la tabla PLAZAS.
Podríamos estar interesados en crear una restricción que valide que el número total de filas en la tabla PLAZAS asignadas a cada vuelo no exceda dicha capacidad.
Y necesitamos que esa restricción se cumpla siempre. Tanto si se hace un cambio en la capacidad del avión, como si añadimos nuevas plazas a un determinado vuelo.
PLAZAS han de ser iguales o menores que la CAPACIDAD (columna) del VUELO.
Actualmente, el dataset de vuelos se carga poblando tantas plazas como la capacidad de su vuelo. En este contexto, debería ser posible que un vuelo de 60 plazas tuviera 58 o 59 a la venta, pero no 61.
CREATE ASSERTION capacidad_consistente_con_plazas
CHECK (
NOT EXISTS (
SELECT 'plaza fuera de capacidad'
FROM VUELOS v, PLAZAS p
WHERE p.VUE_ID_VUELO = v.ID_VUELO
AND p.PLA_ID > v.CAPACIDAD_AVION
)
);
Lo interesante de las SQL Assertions es que, para cada UPDATE que pueda alterar esta regla, la Assertion validará que dicha condición se cumpla. Si cambiamos la capacidad de un vuelo (es decir, si cambiamos el avión) por uno de capacidad mayor, no habrá problema, pero si definimos un avión con menos plazas que las que hay insertadas para ese vuelo, tendremos un error en esa misma sentencia.
SQL> CREATE ASSERTION capacidad_consistente_con_plazas
2 CHECK (
3 NOT EXISTS (
4 SELECT 'plaza fuera de capacidad'
5 FROM VUELOS v, PLAZAS p
6 WHERE p.VUE_ID_VUELO = v.ID_VUELO
7 AND p.PLA_ID > v.CAPACIDAD_AVION
8 )
9 );
Assertion created.
SQL> select id_vuelo, capacidad_avion from vuelos fetch first 1 rows only;
ID_VUELO CAPACIDAD_AVION
---------- ---------------
94848 100
SQL> select count(*) from plazas where vue_id_vuelo=94848;
COUNT(*)
----------
100
SQL> update vuelos set capacidad_avion=60 where id_vuelo=94848;
update vuelos set capacidad_avion=60 where id_vuelo=94848
*
ERROR at line 1:
ORA-08601: SQL assertion (VUELOS.CAPACIDAD_CONSISTENTE_CON_PLAZAS) violated.
Help: https://docs.oracle.com/error-help/db/ora-08601/
Pero ¿qué ocurre si queremos permitir que la Assertion se valide a nivel de transacción? Es decir, permitir que el vuelo cambie a una capacidad menor y, antes de hacer el COMMIT, borrar las plazas adicionales.
En este caso, al igual que con las constraints, tenemos la cláusula DEFERRABLE INITIALLY DEFERRED.
CREATE ASSERTION capacidad_consistente_con_plazas
CHECK (
NOT EXISTS (
SELECT 'plaza fuera de capacidad'
FROM VUELOS v, PLAZAS p
WHERE p.VUE_ID_VUELO = v.ID_VUELO
AND p.PLA_ID > v.CAPACIDAD_AVION
)
) DEFERRABLE INITIALLY DEFERRED;
Así es posible actualizar un vuelo a una capacidad menor, pero debemos asegurarnos antes de realizar el commit de que hemos eliminado el excedente de plazas.
Vamos a verlo.
SQL> drop assertion capacidad_consistente_con_plazas;
Assertion dropped.
SQL> CREATE ASSERTION capacidad_consistente_con_plazas
2 CHECK (
3 NOT EXISTS (
4 SELECT 'plaza fuera de capacidad'
5 FROM VUELOS v, PLAZAS p
6 WHERE p.VUE_ID_VUELO = v.ID_VUELO
7 AND p.PLA_ID > v.CAPACIDAD_AVION
8 )
9 ) DEFERRABLE INITIALLY DEFERRED;
Assertion created.
SQL> update vuelos set capacidad_avion=60 where id_vuelo=94848;
1 row updated.
SQL> -- He podido modificar la capacidad, pero la SQL Assertion no se valida hasta el commit
SQL> delete from plazas where pla_id>60 and vue_id_vuelo=94848;
40 rows deleted.
SQL> commit;
Commit complete.
SQL> -- Voy a reducir la capacidad a 50 plazas y dejar 51 plazas en PLAZAS
SQL> update vuelos set capacidad_avion=50 where id_vuelo=94848;
1 row updated.
SQL> delete from plazas where pla_id>51 and vue_id_vuelo=94848;
9 rows deleted.
SQL> commit;
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-08601: SQL assertion (VUELOS.CAPACIDAD_CONSISTENTE_CON_PLAZAS) violated.
Help: https://docs.oracle.com/error-help/db/ora-02091/
SQL> -- toda la transacción conflictiva se rechaza y se mantiene la capacidad a 60 plazas
SQL> select id_vuelo, capacidad_avion from vuelos where id_vuelo=94848;
ID_VUELO CAPACIDAD_AVION
---------- ---------------
94848 60
SQL> select count(*) from plazas where vue_id_vuelo=94848;
COUNT(*)
----------
60
Involucrando un control adicional sobre RESERVAS.
Vamos a complicar el ejemplo impidiendo que un vuelo pueda reducir su capacidad (por ejemplo, de 120 a 100 plazas) si hay más plazas reservadas de las que dispone el nuevo avión.
De este modo, el UPDATE con el cambio de plazas a un avión más pequeño solo se permitiría si no se ha vendido un número de plazas superior a la nueva capacidad.
Por lo tanto, voy a crear una segunda SQL Assertion para verificar las reservas. Si hay más reservas que la capacidad actual del avión, ese UPDATE en la tabla VUELOS deberá fallar.
CREATE ASSERTION capacidad_no_inferior_a_plazas_reservadas
CHECK (
NOT EXISTS (
SELECT 'plaza fuera de capacidad'
FROM VUELOS v, RESERVAS r
WHERE r.VUE_ID_VUELO = v.ID_VUELO
AND r.PLA_ID_PLAZA > v.CAPACIDAD_AVION
)
);
Aunque aquí estoy haciendo una pequeña trampa.
Las plazas de un avión, en nuestro modelo de datos, son un dato ordinal; es decir, se numera del 1 al número de plazas del avión. Las reservas, si se hacen secuencialmente, también llenarían el avión de forma ordinal: la primera reserva ocupa la plaza 1 del vuelo, la segunda, la plaza 2, y así sucesivamente.
Si los clientes eligen la plaza porque prefieren estar al final del avión, por ejemplo, las últimas plazas estarían ocupadas, pero quizás el avión esté medio vacío.
Por no decir que, si hay cancelaciones, esas plazas deberían volver a estar desocupadas y sí debería permitir actualizar a un avión más pequeño.
CREATE ASSERTION capacidad_no_inferior_a_plazas_reservadas
CHECK (
3 NOT EXISTS (
4 SELECT 'plaza fuera de capacidad'
5 FROM VUELOS v, RESERVAS r
6 WHERE r.VUE_ID_VUELO = v.ID_VUELO
7 AND r.PLA_ID_PLAZA > v.CAPACIDAD_AVION
8 )
9 );
Assertion created.
SQL> select vue_id_vuelo, count(*) from reservas group by vue_id_vuelo having count(*)>20;
VUE_ID_VUELO COUNT(*)
------------ ----------
1006 21
Elapsed: 00:00:00.08
SQL> select * from reservas where vue_id_vuelo=1006;
ID_RESERVA IMPORTE CLI_NIF TRS AGE_ID_AGENCIA PLA_ID_PLAZA VUE_ID_VUELO
---------- ---------- ---------- --- -------------- ------------ ------------
Mg`TVfJJBL 103.19 90857407-N TEL 53 2 1006
DPKEOEUWO[ 101.2 87095872-R INT 40 6 1006
bdfOPJdiEP 113.17 16602711-O VEN 14 7 1006
MZBFJITNM` 189.4 2657873-D INT 133 11 1006
LHWShhcOU^ 193.73 82355352-I INT 40 13 1006
ZGWaRhK^P^ 147.67 8071986-E VEN 136 20 1006
JPFITFcGYP 101.74 21409917-K VEN 54 29 1006
iAbZBPZOXI 177.72 31934765-J TEL 7 38 1006
YPUeJZePeU 109.58 20615714-I VEN 12 39 1006
BeJVLaM\JG 181.4 15451433-W VEN 136 42 1006
WV`IhKYZWh 134.4 57515235-U INT 121 98 1006
XTLg`QYMYE 192.39 56560002-I VEN 19 108 1006
4CD6D1B19F 118.3 12996373-S TEL 95 1 1006
gQREZPSESX 177.7 56920247-J TEL 86 22 1006
BDh`]WAJXS 197.07 24307672-R VEN 55 32 1006
UTLh`aWLCZ 120.66 77586025-R INT 144 49 1006
dYSZKdPYUe 186.31 92136826-K INT 25 50 1006
WKHU`dETNd 134.73 30179377-V VEN 5 63 1006
ddZJ]hUGIU 174.29 9167243-T TEL 85 78 1006
UJX]DHA\ae 148.58 67159700-G TEL 66 95 1006
^BDLCVRKRX 120.22 94489790-X TEL 43 96 1006
21 rows selected.
Elapsed: 00:00:00.04
SQL> update vuelos set capacidad_avion=100 where id_vuelo=1006;
update vuelos set capacidad_avion=100 where id_vuelo=1006
*
ERROR at line 1:
ORA-08601: SQL assertion (VUELOS.CAPACIDAD_NO_INFERIOR_A_PLAZAS_RESERVADAS) violated.
Help: https://docs.oracle.com/error-help/db/ora-08601/
Elapsed: 00:00:00.14
En este caso, entonces, no podríamos reducir la ocupación del vuelo 1006 de 120 a 100 plazas porque hay una persona ocupando la plaza 108.
La SQL Assertion que hemos creado, comparando el número de la plaza con la capacidad, es una medida un poco “regulera”. Lo óptimo en este caso sería contar las plazas reservadas para permitir, o no, reducir la capacidad del vuelo a un avión más pequeño.
El código de la SQL Assertion podría ser el siguiente:
CREATE ASSERTION capacidad_no_inferior_a_reservas
CHECK (
NOT EXISTS (
SELECT 1
FROM VUELOS v
WHERE v.CAPACIDAD_AVION < (
SELECT COUNT(*)
FROM RESERVAS r
WHERE r.VUE_ID_VUELO = v.ID_VUELO
)
)
);
Y digo “podría” porque esta sintaxis no está permitida.
Las SQL Assertions vienen con un montón de limitaciones. Sólo podemos usarlas en sentencias SQL que no contengan funciones de grupo; no soportan ANSI SQL; no resuelven accesos a través de sinónimos; no se permiten sobre vistas materializadas ni información de contexto (SYSDATE, USERENV, CURRENT_SCHEMA, etc.).
Han de ser estrictamente determinísticas.
Por si quieres echarle un ojo a las restricciones de las SQL Assertions, aquí tienes la documentación oficial con toda la lista de restricciones:
https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-assertion.html
Y por si piensas que, mediante funciones analíticas, quizás puedas salvar la restricción de uso de funciones de grupo, has de saber que también forman parte de la lista de funcionalidades no prohibidas, aunque el error “Las tablas pertenecientes a SYS no están permitidas” no sea muy ilustrativo.
SQL> SELECT 'plaza fuera de capacidad'
2 FROM VUELOS v, RESERVAS r
3 WHERE r.VUE_ID_VUELO = v.ID_VUELO
4 QUALIFY ROW_NUMBER() OVER (
5 PARTITION BY r.VUE_ID_VUELO order by r.PLA_ID_PLAZA
6 ) > v.CAPACIDAD_AVION;
no rows selected
SQL> CREATE ASSERTION capacidad_no_inferior_a_reservas
2 CHECK (
3 NOT EXISTS (
4 SELECT 'plaza fuera de capacidad'
5 FROM VUELOS v, RESERVAS r
6 WHERE r.VUE_ID_VUELO = v.ID_VUELO
7 QUALIFY ROW_NUMBER() OVER (
8 PARTITION BY r.VUE_ID_VUELO order by r.PLA_ID_PLAZA
9 ) > v.CAPACIDAD_AVION
10 )
11 )
FROM VUELOS v, RESERVAS r
*
ERROR at line 5:
ORA-08689: CREATE ASSERTION failed
ORA-08697: SYS owned tables are not supported.
Help: https://docs.oracle.com/error-help/db/ora-08689/
Otro ejemplo: comprobar que un cliente no toma dos vuelos el mismo día.
Vamos a explorar otro ejemplo: validar que un cliente no tenga reservas en vuelos distintos para un mismo día. En este caso, los datos de fecha del vuelo están en la tabla VUELOS, y para ello necesitamos hacer el cruce con vuelos y reservas dos veces.
Recuerda que no podemos usar ni funciones de grupo ni analíticas.
CREATE ASSERTION no_dos_vuelos_mismo_dia
CHECK (
NOT EXISTS (
SELECT 1
FROM reservas r1,
reservas r2,
vuelos v1,
vuelos v2
WHERE r1.cli_nif = r2.cli_nif
AND r1.rowid < r2.rowid
AND r1.vue_id_vuelo = v1.id_vuelo
AND r2.vue_id_vuelo = v2.id_vuelo
AND TRUNC(v1.fecha_vuelo) = TRUNC(v2.fecha_vuelo)
)
) ENABLE NOVALIDATE;
Aquí nos encontramos con más restricciones en la validación de la constraint. Dado que en la tabla RESERVAS el valor de CLI_NIF puede ser NULL, la SQL Assertion nos fallará con un error que nos advierte de que la comprobación de la assertion ha de ser de tipo FAST, y que con las condiciones habituales, no es posible garantizar.
Tendremos dos opciones para que funcione: o añadimos las cláusulas WHERE CLI_NIF IS NOT NULL al código de la SQL Assertion, o modificaremos la tabla RESERVAS para impedir que CLI_NIF sea NULL
SQL> CREATE ASSERTION no_dos_vuelos_mismo_dia
2 CHECK (
3 NOT EXISTS (
4 SELECT 1
5 FROM reservas r1,
6 reservas r2,
7 vuelos v1,
8 vuelos v2
9 WHERE r1.cli_nif = r2.cli_nif
10 AND r1.rowid < r2.rowid
11 AND r1.vue_id_vuelo = v1.id_vuelo
12 AND r2.vue_id_vuelo = v2.id_vuelo
13 AND TRUNC(v1.fecha_vuelo) = TRUNC(v2.fecha_vuelo)
14 )
15 ) ENABLE NOVALIDATE
FROM reservas r1,
* ERROR at line 5: ORA-08689: CREATE ASSERTION failed ORA-08673: Equijoin "R1"."CLI_NIF"="R2"."CLI_NIF" found
does not meet the criteria to do a FAST validation. Help: https://docs.oracle.com/error-help/db/ora-08689/
SQL> alter table reservas modify cli_nif not null;
Table altered.
SQL> CREATE ASSERTION no_dos_vuelos_mismo_dia
2 CHECK (
3 NOT EXISTS (
4 SELECT 1
5 FROM reservas r1,
6 reservas r2,
7 vuelos v1,
8 vuelos v2
9 WHERE r1.cli_nif = r2.cli_nif
10 AND r1.rowid < r2.rowid
11 AND r1.vue_id_vuelo = v1.id_vuelo
12 AND r2.vue_id_vuelo = v2.id_vuelo
13 AND TRUNC(v1.fecha_vuelo) = TRUNC(v2.fecha_vuelo)
14 )
15 ) ENABLE NOVALIDATE
Assertion created.
Elapsed: 00:00:00.22
SQL> insert into reservas values ('Y\QZ]ZJPPV',197.65,'10108372-X','INT',38,9,20108);
insert into reservas values ('Y\QZ]ZJPPV',197.65,'10108372-X','INT',38,9,20108)
*
ERROR at line 1:
ORA-08601: SQL assertion (VUELOS.NO_DOS_VUELOS_MISMO_DIA) violated.
Help: https://docs.oracle.com/error-help/db/ora-08601/
Una precaución aquí: hay que tener cuidado con el rendimiento.
La SQL Assertion anterior se ha creado con la cláusula ENABLE NOVALIDATE de forma instantánea. Aunque las comprobaciones no parezcan afectar el rendimiento al ejecutar el código SQL que la valida, la creación de estas SQL Assertions puede llevar un tiempo considerable debido a la validación de todos los casos existentes.
Podemos ver que la query se ejecuta en menos de un segundo, pero la activación con validación de la Assertion tarda más de 1 hora.
SQL> SELECT 'cliente con dos vuelos el mismo dia' 2 FROM reservas r1, 3 reservas r2, 4 vuelos v1, 5 vuelos v2 6 WHERE r1.cli_nif = r2.cli_nif 7 AND r1.rowid < r2.rowid 8 AND r1.vue_id_vuelo = v1.id_vuelo 9 AND r2.vue_id_vuelo = v2.id_vuelo 10 AND TRUNC(v1.fecha_vuelo) = TRUNC(v2.fecha_vuelo) no rows selected Elapsed: 00:00:00.60 SQL> alter assertion NO_DOS_VUELOS_MISMO_DIA enable validate; Elapsed: 01:43:05.21
¿Qué hay por debajo?
Vamos a echarle un ojo a las tablas que se crean automáticamente con la creación de las constraints.
SQL> select table_name, blocks*(8*1024) bytes from user_tables
TABLE_NAME BYTES
------------------------------------------------------- ----------
CLIENTES 778240
COMPANIAS 40960
AGENCIAS 40960
TIPOS_VUELO 40960
EMPRESAS_VIAJES 40960
SEXOS 40960
PLAZAS 82714624
AEROPUERTOS 40960
ESTADOSLABORALES 40960
PAISES 40960
RESERVAS 11288576
VUELOS 4063232
ESTADOSCIVILES 40960
CATERING 40960
TIPOS_RESERVAS 40960
EDADES 40960
CIUDADES 40960
ORA$SA$TE_VUELOS
ORA$SA$TE_PLAZAS
ORA$SA$VC_CAPACIDAD_CONSISTENTE_CON_PLAZAS
ORA$SA$TE_RESERVAS
ORA$SA$VC_CAPACIDAD_NO_INFERIOR_A_PLAZAS_RESERVADAS
ORA$SA$VC_NO_DOS_VUELOS_MISMO_DIA
23 rows selected.
SQL> select count(*) from ORA$SA$TE_VUELOS;
select count(*) from ORA$SA$TE_VUELOS
*
ERROR at line 1:
ORA-08709: Reads from SQL assertion auxiliary tables are restricted.
Help: https://docs.oracle.com/error-help/db/ora-08709/
Para consultar al diccionario, tenemos a nuestra disposición las vistas USER_ASSERTIONS, USER_ASSERTION_DEPENDENCIES y USER_ASSERTION_LOCK_MATRIX
SQL> desc USER_ASSERTIONS Name Null? Type ----------------------------------------- -------- ---------------------------- ASSERTION_NAME VARCHAR2(128) OBJECT_ID NOT NULL NUMBER STATUS VARCHAR2(8) DEFERRABLE VARCHAR2(14) DEFERRED VARCHAR2(9) VALIDATED VARCHAR2(13) INVALID VARCHAR2(7) DEFINITION_SQL CLOB SQL> desc USER_ASSERTION_DEPENDENCIES Name Null? Type ----------------------------------------- -------- ---------------------------- ASSERTION_NAME VARCHAR2(128) REFERENCED_OWNER VARCHAR2(128) REFERENCED_NAME VARCHAR2(128) REFERENCED_TYPE VARCHAR2(23) REFERENCED_ID NOT NULL NUMBER VALIDATION_TYPE VARCHAR2(8) VALIDATION_EVENT VARCHAR2(24) SQL> desc USER_ASSERTION_LOCK_MATRIX Name Null? Type ----------------------------------------- -------- ---------------------------- ASSERTION_NAME VARCHAR2(128) TABLE_ID NUMBER TABLE_OWNER VARCHAR2(128) TABLE_NAME VARCHAR2(128) ADJ_TABLE_ID NUMBER ADJ_TABLE_OWNER VARCHAR2(128) ADJ_TABLE_NAME VARCHAR2(128) LOCK_SCOPE VARCHAR2(18) JOIN_COLUMNS VARCHAR2(4000) LOCK_MODE VARCHAR2(8) LOCK_TEXT VARCHAR2(4000) SQL> select assertion_name, object_id, status, validated 2 from user_assertions; ASSERTION_NAME OBJECT_ID STATUS VALIDATED -------------------------------------------------- ---------- -------- ------------- CAPACIDAD_CONSISTENTE_CON_PLAZAS 131257 ENABLED VALIDATED CAPACIDAD_NO_INFERIOR_A_PLAZAS_RESERVADAS 131262 ENABLED VALIDATED NO_DOS_VUELOS_MISMO_DIA 131306 ENABLED NOT VALIDATED SQL> select assertion_name, referenced_owner, referenced_name, validation_type, validation_event 2 from user_assertion_dependencies; ASSERTION_NAME REFERENCED_OWNER REFERENCED_NAME VALIDATI VALIDATION_EVENT --------------------------------------------------- -------------------- -------------------- -------- ------------------------ CAPACIDAD_CONSISTENTE_CON_PLAZAS VUELOS PLAZAS FAST ROWS INSERTED OR UPDATED CAPACIDAD_NO_INFERIOR_A_PLAZAS_RESERVADAS VUELOS RESERVAS FAST ROWS INSERTED OR UPDATED NO_DOS_VUELOS_MISMO_DIA VUELOS RESERVAS FAST ROWS INSERTED OR UPDATED NO_DOS_VUELOS_MISMO_DIA VUELOS RESERVAS FAST ROWS INSERTED OR UPDATED CAPACIDAD_CONSISTENTE_CON_PLAZAS VUELOS VUELOS FAST ROWS INSERTED OR UPDATED CAPACIDAD_NO_INFERIOR_A_PLAZAS_RESERVADAS VUELOS VUELOS FAST ROWS INSERTED OR UPDATED NO_DOS_VUELOS_MISMO_DIA VUELOS VUELOS FAST ROWS INSERTED OR UPDATED NO_DOS_VUELOS_MISMO_DIA VUELOS VUELOS FAST ROWS INSERTED OR UPDATED 8 rows selected. SQL> select assertion_name, table_name, adj_table_name, lock_scope, join_columns, lock_mode, lock_text 2 from user_assertion_lock_matrix 3 order by assertion_name; ASSERTION_NAME TABLE_NAME ADJ_TABLE_ LOCK_SCOPE JOIN_COLUMNS LOCK_MOD LOCK_TEXT --------------------------------------------------- ---------- ---------- ------------------ --------------- -------- --------------- CAPACIDAD_CONSISTENTE_CON_PLAZAS VUELOS PLAZAS JOIN_COLUMN_VALUES DETALLES S T#:0 C#:1:3 CAPACIDAD_CONSISTENTE_CON_PLAZAS PLAZAS PLAZAS JOIN_COLUMN_VALUES VUE_ID_VUELO SX T#:0 C#:1:3 CAPACIDAD_CONSISTENTE_CON_PLAZAS PLAZAS VUELOS JOIN_COLUMN_VALUES PLA_ID S T#:1 C#:1:1 CAPACIDAD_CONSISTENTE_CON_PLAZAS VUELOS VUELOS JOIN_COLUMN_VALUES ID_VUELO SX T#:1 C#:1:1 CAPACIDAD_NO_INFERIOR_A_PLAZAS_RESERVADAS RESERVAS RESERVAS JOIN_COLUMN_VALUES VUE_ID_VUELO SX T#:0 C#:1:7 CAPACIDAD_NO_INFERIOR_A_PLAZAS_RESERVADAS RESERVAS VUELOS JOIN_COLUMN_VALUES ID_RESERVA S T#:1 C#:1:1 CAPACIDAD_NO_INFERIOR_A_PLAZAS_RESERVADAS VUELOS VUELOS JOIN_COLUMN_VALUES ID_VUELO SX T#:1 C#:1:1 CAPACIDAD_NO_INFERIOR_A_PLAZAS_RESERVADAS VUELOS RESERVAS JOIN_COLUMN_VALUES CAT_ID_CATERING S T#:0 C#:1:7 NO_DOS_VUELOS_MISMO_DIA VUELOS RESERVAS JOIN_COLUMN_VALUES CAT_ID_CATERING S T#:0 C#:1:7 NO_DOS_VUELOS_MISMO_DIA VUELOS RESERVAS TABLE S T#:0 C#:0: NO_DOS_VUELOS_MISMO_DIA VUELOS VUELOS TABLE SX T#:3 C#:0: NO_DOS_VUELOS_MISMO_DIA VUELOS RESERVAS JOIN_COLUMN_VALUES CAT_ID_CATERING S T#:1 C#:1:7 NO_DOS_VUELOS_MISMO_DIA RESERVAS VUELOS JOIN_COLUMN_VALUES ID_RESERVA S T#:2 C#:1:1 NO_DOS_VUELOS_MISMO_DIA RESERVAS VUELOS TABLE S T#:2 C#:0: NO_DOS_VUELOS_MISMO_DIA RESERVAS VUELOS TABLE S T#:3 C#:0: NO_DOS_VUELOS_MISMO_DIA RESERVAS VUELOS JOIN_COLUMN_VALUES ID_RESERVA S T#:3 C#:1:1 NO_DOS_VUELOS_MISMO_DIA VUELOS VUELOS TABLE SX T#:2 C#:0: NO_DOS_VUELOS_MISMO_DIA VUELOS VUELOS JOIN_COLUMN_VALUES ID_VUELO SX T#:2 C#:1:1 NO_DOS_VUELOS_MISMO_DIA VUELOS VUELOS TABLE S T#:2 C#:0: NO_DOS_VUELOS_MISMO_DIA VUELOS VUELOS TABLE S T#:3 C#:0: NO_DOS_VUELOS_MISMO_DIA VUELOS VUELOS JOIN_COLUMN_VALUES ID_VUELO SX T#:3 C#:1:1 NO_DOS_VUELOS_MISMO_DIA RESERVAS RESERVAS JOIN_COLUMN_VALUES CLI_NIF SX T#:1 C#:1:3 NO_DOS_VUELOS_MISMO_DIA RESERVAS RESERVAS TABLE SX T#:1 C#:0: NO_DOS_VUELOS_MISMO_DIA RESERVAS RESERVAS JOIN_COLUMN_VALUES VUE_ID_VUELO SX T#:1 C#:1:7 NO_DOS_VUELOS_MISMO_DIA RESERVAS RESERVAS JOIN_COLUMN_VALUES CLI_NIF S T#:1 C#:1:3 NO_DOS_VUELOS_MISMO_DIA RESERVAS RESERVAS JOIN_COLUMN_VALUES CLI_NIF S T#:0 C#:1:3 NO_DOS_VUELOS_MISMO_DIA RESERVAS RESERVAS JOIN_COLUMN_VALUES CLI_NIF SX T#:0 C#:1:3 NO_DOS_VUELOS_MISMO_DIA RESERVAS RESERVAS JOIN_COLUMN_VALUES VUE_ID_VUELO SX T#:0 C#:1:7 NO_DOS_VUELOS_MISMO_DIA RESERVAS RESERVAS TABLE SX T#:0 C#:0: NO_DOS_VUELOS_MISMO_DIA VUELOS RESERVAS TABLE S T#:1 C#:0: 30 rows selected.
Disponible sólo en Oracle AI Database a partir de la release 23.26.1
Estamos ante una funcionalidad completamente nueva. Sólo disponible a partir del RU de enero de 2026 para la base de datos Oracle AI Database (26ai).
No solo eso: se trata de una funcionalidad nueva en el mundo de las bases de datos en general, pues hasta ahora sólo existía en el marco teórico.
El comportamiento y la sintaxis recuerdan mucho a la gestión de constraints, lo que permite combinaciones entre ENABLE/DISABLE y VALIDATE/NOVALIDATE. Desconozco si en el futuro la lista de restricciones de uso se irá reduciendo, ni si podremos contar con SQL Assertions con expresiones de grupo o funciones analíticas, pero la idea de acercarnos cada vez más al estándar y poder implementar desde el motor el mayor número posible de reglas de negocio sobre los datos me parece súper interesante.
Echo de menos una cláusula EXCEPTIONS INTO para gestionar excepciones en una tabla EXCEPTIONS, igual que ocurre con las constraints, y tampoco tenemos documentación sobre si el optimizador utilizará la lógica de las assertions para mejorar las estimaciones de cardinalidad y selectividad.
En cualquier caso, por fin vemos implementada una funcionalidad diseñada para mejorar la lógica de negocio que ha tardado décadas en ver la luz.
Al igual que este artículo.