Documente Academic
Documente Profesional
Documente Cultură
Integridad referencial
Publicado por Gastón el 24/04/2009 en MySQL - Nivel Intermedio
Resúmen
Artículo destinado a introducir los principios de la integridad referencial para bases de datos y en particular su uso
en MySQL, más precisamente en tablas INNOdb.
El artículo se basa en dos ejemplos detallados (realmente detallados) para su fácil entendimiento; ejemplos
basados en casos reales (en dos módulos de este mismo sitio).
Si bien el artículo es plenamente práctico, intento incluir algunos conceptos teóricos para alivianar un poco su
lectura y llegar mejor a buen puerto.
Tabla de contenidos
En este caso estudiaremos el típico caso de una ocurrencia de entidad A pertenece a una ocurrencia de la entidad
B. Se trata de una relación de uno a muchos o N a N, o maestro/detalle.
Por ejemplo, en este mismo módulo de artículos se da ésta situación: este artículo pertenece a la categoría MySQL y
la categoría MySQL puede tener muchos artículos en su haber.
En este tipo de relaciones, luego de realizar el módelo conceptual, obtenemos el modelo lógico el cual resulta en la
siguiente definición de tablas (solo se mostrarán las claves primarias y foraneas, obviando cualquier otro tipo de
columnas):
Página 1/8
Integridad referencial @ Club Desarrolladores
En la definición de la tabla articulos decimos que un artículo no debe necesariamente pertenecer a una categoría
(puede ser null su referencia a categoria).
Veamos un poco de teoría. Unaclave primaria se define como una o más columnas que identifican unívocamente
una fila en nuestra misma tabla. En nuestro ejemplo tenemos id_categoría en la tabla categorias e id_articulo en la
tabla articulos. Como se puede ver en el código éstas se definen con las palabras claves primary key.
Una clave foranea se define como una o más columnas que identifican unívocamente una fila en otra tabla. Es
decir, una clave foranea referencia a una clave primaria de una tabla foranea. En el ejemplo anterior sería
id_categoria de la tabla articulos, que haría referencia a id_categoria de la tabla categorias.
Como se puede ver, el motor de base de datos no sabe aún de esta referencia (ni de cómo tratarla), a eso lo
veremos a continuación. Pero antes notemos un detalle muy importante, en la definición de tablas anterior no
especificamos que tipo de tabla son, y MySQL soporta integridad referencial en tablas INNOdb, no así en tablas
MyISAM. Con el siguiente código podremos reparar tal situación y convertir nuestras tablas a INNOdb:
Inconsistencias
Una inconsistencia es una situación que se presenta cuando una clave foranea referencia a una clave primaria
inexistente en la tabla referenciada. Lo explicaré con un ejemplo.
Nota: Los número encerrados entre paréntesis indican las claves primarias (y foraneas en el caso de la primer
columna) de las tablas en cuestión.
Página 2/8
Integridad referencial @ Club Desarrolladores
Y ahora por algún motivo eliminamos de la tabla categorias la categoría de MySQL (1).
¿Qué sucede con los artículos que correspondían a esta categoría (1) y (2)?...
¡Quedan huérfanos! apuntarían a claves incorrectas y nuestra base de datos se volvería inconsistente.
Cuando digo que apuntarían a claves incorrectas se pueden ver dos posibles situaciones:
1 - Se elimina (1) MySQL de la tabla categorias, los articulos que apuntaban a (1) MySQL seguirían apuntando a
este, pero ¡éste ya no existe!
2 - Se elimina (1) MySQL de la tabla categorias, y se agrega una nueva categoría a esta tabla (1) 'PostgreSQL'
(esto es totalmente válido, se cumple la restricción de clave primaria en categorias). Ahora tenemos que:
Noten la primera ocurrencia de relación: (1) (1) 'Tipos de tablas en MySQL' -------> (1) 'PostgreSQL'... creo que
habla por si misma.
Integridad Referencial
Estas situaciones son muy comunes y, aunque pueden resolverse mediante el lenguaje de programación cliente, es
mejor dejar a la propia base de datos mantener la consistencia. Esto se debe a que mantener la consistencia
desde un lenguaje como PHP o C++ por ejemplo representaría tener que realizar una consulta extra luego de cada
UPDATE o DELETE en la tabla referenciada, lo que implicaría una perdida de legibilidad y de performance.
Por ejemplo si la intención es eliminar la categoría de MySQL, deberemos también actualizar la tabla articulos para
que la consistencia se mantenga entre ambas tablas, por lo que deberemos actualizar aquellas filas cuya columna
id_categoria sea igual a 1.
Y si la intención es modificar el id_categoria de 'MySQL' a 3 también deberíamos hacer lo propio en aquellas filas
de tabla articulos que referencian a MySQL.
Si por algún motivo olvidamos esto nuestra base de datos quedará inconsistente, o lo que es lo mismo,
incoherente tal como se explicó anteriormente.
Ahora por fin, relacionaremos las tablas. He aquí la sintaxis de la sentencia ALTER TABLE que creará la restricción
de clave ajena (o foránea) y por ende la integridad referencial para estas dos tablas:
Página 3/8
Integridad referencial @ Club Desarrolladores
Esto se puede leer asi: Alterar la tabla articulos agregando una restricción a la clave foranea id_categoria (de la
tabla articulos) que referencia a id_categoria (de la tabla categorias), cuando se elimine alguna categoría (y por
consiguiente su id_categoria en la tabla categorias) setear NULL a id_categoria (de la tabla articulos) y cuando se
modifique alguna categoria (se modifique id_categoria de la tabla categorias) aplicar esa modificación a todos los
articulos (a todos los id_categoria de la tabla articulos).
automáticamente se le dará el valor NULL a todas las tuplas de articulos cuyo id_categoria sea igual a 1.
Quedaria asi:
automáticamente se le dará el valor 3 a todas las tuplas de articulos cuyo id_categoria sea igual a 1.
¡CASCADE tanto en DELETE como en UPDATE! A eso lo veremos a continuación cuando expliquemos integridad
referencial en una relación de muchos a muchos (dé vuelta la página!).
Ahora que entendimos como funciona la integridad referencial en relaciones de uno a muchos, vamos a ver un
caso que también se ve mucho en la hora de desarrollar sistemas.
Siguiendo con el ejemplo anterior tomemos como caso de estudio el módulo de foros del club.
En este módulo tenemos temas y categorias (aunque yo trate de llamarla grupos para que suene novedoso). Un
tema puede pertenecer a muchas categorias y una categoría puede tener muchos temas.
Es una relación muchos a muchos o N a N que puede representarse de la siguiente manera:
Página 4/8
Integridad referencial @ Club Desarrolladores
Esta vez explicitamos que el tipo de tabla que usaremos será INNOdb.
Y también creamos la tabla que servirá de relación entre las anteriores:
Puede verse que esta última tabla tiene una clave primaria compuesta por dos columnas, e individualmente cada
una de estas columnas será clave foránea de las tablas anteriores según corresponda como veremos a
continuación luego de explicar los casos de inconsistencias que podriamos resultar.
Inconsistencias
(1) 'MySQL' ---> (1)(1) ---> (1) '¿Tipos de tabla MySQL, que son?'
(1) 'MySQL' ---> (1)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(3) ---> (3) 'Como lograr el login?!'
Empecemos a armar un poco de lio. Eliminemos la categoría (1) MySQL y veamos que sucede.
Página 5/8
Integridad referencial @ Club Desarrolladores
(NULL) ---> (1)(1) ---> (1) '¿Tipos de tabla MySQL, que son?'
(NULL) ---> (1)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(3) ---> (3) 'Como lograr el login?!'
La tabla de relaciones no se actualiza y sigue indicando que tanto los temas (1) como (2) pertenecen a la categoría
(1) la cual no existe!
Olvidemos la maldad anterior, y en lugar de eliminar la categoría MySQL eliminaremos el tema (3):
(1) 'MySQL' ---> (1)(1) ---> (1) '¿Tipos de tabla MySQL, que son?'
(1) 'MySQL' ---> (1)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(3) ---> (NULL)
Ahora tenemos otro problema, aunque muy similar. La categoría 2 cuenta con dos temas en su haber, uno es el
tema (2) y el otro el tema (3)... claro, (3) no existe!
Lo mismo sucedería si modificáramos en lugar de eliminar. Imaginemos que modificamos la categoria (1) MySQL
a (3) y que modificamos el tema (1) a (4), esto es lo que obtendriamos:
(3) 'MySQL' ---> (1)(1) ---> (4) '¿Tipos de tabla MySQL, que son?'
(3) 'MySQL' ---> (1)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(3) ---> (3) 'Como lograr el login?!'
¿Se dan cuenta no? La clave está en ver que la tabla que relaciona (categorias_temas) no se actualiza
automáticamente y por lo tanto saltan todas las incoherencias posibles.
Cualquiera diría que ésta es una base de datos inconsistente.
Integridad Referencial
Cortemos por lo sano, apliquemos integridad referencial a estas tablas alterando la que contiene las claves
foráneas, es decir:
Como puede verse hay que alterar la tabla dos veces, porque tenemos dos claves foráneas a las cuales restringir.
Alterar la tabla categorias_temas agregando una restricción a la clave foranea id_categoria (de la tabla
categorias_temas) que referencia a id_categoria (de la tabla categorias), cuando se elimine alguna categoría (y por
consiguiente su id_categoria en la tabla categorias) eliminar todas las tuplas de categorias_temas que tengan
id_categoria igual al id_categoria de la categoría eliminada y cuando se modifique alguna categoria (se modifique
id_categoria de la tabla categorias) aplicar esa modificación a todas las tuplas de categorias_temas (a todos los
id_categoria de la tabla categorias_temas).
Página 6/8
Integridad referencial @ Club Desarrolladores
Luego...
Alterar la tabla categorias_temas agregando una restricción a la clave foranea id_tema (de la tabla
categorias_temas) que referencia a id_tema (de la tabla temas), cuando se elimine algun tema (y por consiguiente
su id_tema en la tabla temas) eliminar todas las tuplas de categorias_temas que tengan id_tema igual al id_tema
del tema eliminado y cuando se modifique algun tema (se modifique id_tema de la tabla temas) aplicar esa
modificación a todas las tuplas de categorias_temas (a todos los id_tema de la tabla categorias_temas).
Como ejemplo, porque no voy a dejarlo con dudas, vamos a tomar el caso del que hablamos hace poco.
Teniendo esto cargado:
(1) 'MySQL' ---> (1)(1) ---> (1) '¿Tipos de tabla MySQL, que son?'
(1) 'MySQL' ---> (1)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(3) ---> (3) 'Como lograr el login?!'
Imaginemos que modificamos la categoria (1) MySQL a (3) y que modificamos el tema (1) a (4), esto es lo que
obtendriamos:
(3) 'MySQL' ---> (3)(4) ---> (4) '¿Tipos de tabla MySQL, que son?'
(3) 'MySQL' ---> (3)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta'
(2) 'Microsoft SQL Server' ---> (2)(3) ---> (3) 'Como lograr el login?!'
Ahora disponemos de una base de datos consistente. Todo es lógico, no perdemos relaciones, las mantenemos
coherentes en todo momento y lo mejor es que esto se realiza en la base de datos, con sus ventajas antes
mencionadas y de una manera muy fácil de declarar... ¿Será por eso que SQL se trata de un paradigma
declarativo?
Falta un solo detalle por comentar. En MySQL existe tres opciones más además de SET NULL y CASCADE a
saber:
NO ACTION y RESTRICT: Funcionan igual, no permiten actualización alguna (UPDATE o DELETE) rechazando el
cambio que intentó propagarse.
SET DEFAULT: A la fecha no está implementado, debería funcionar de forma similar a SET NULL solo que
asignaría el valor declarado por defecto al crear las columas de las tablas foráneas.
Y esto es todo por ahora. Espero que le den un buen uso a este artículo que acabo de escribir, ahora mismo
escribo la descripción (sería como el prólogo, dicen los que saben que se escriben al final del libro) y lo publico para
que puedan leerlo.
No se olviden de votarlo y comentar, si bien no lo hago para recibir feedback es motivador saber que la gente lee
lo que uno escribe con tanto esfuerzo.
Sobre el autor
Página 7/8
Integridad referencial @ Club Desarrolladores
Página 8/8