Integridad Referencial

  • Upload
    gaston

  • View
    2.588

  • Download
    4

Embed Size (px)

DESCRIPTION

Artículo sobre integridad referencial para bases de datos relacionales. Ejemplos y explicaciones completas con código SQL incluido. Basado en MySQL.

Citation preview

Integridad referencial @ Club Desarrolladores

Integridad referencialPublicado por Gastn el 24/04/2009 en MySQL - Nivel Intermedio

ResmenArtculo destinado a introducir los principios de la integridad referencial para bases de datos y en particular su uso en MySQL, ms precisamente en tablas INNOdb. El artculo se basa en dos ejemplos detallados (realmente detallados) para su fcil entendimiento; ejemplos basados en casos reales (en dos mdulos de este mismo sitio). Si bien el artculo es plenamente prctico, intento incluir algunos conceptos tericos para alivianar un poco su lectura y llegar mejor a buen puerto.

Tabla de contenidosIntegridad Referencial para una Base de Datos Consistente Relacin uno a muchos Inconsistencias Integridad Referencial Relacin muchos a muchos Inconsistencias Integridad Referencial

Integridad Referencial para una Base de Datos ConsistenteLa situaciones que vamos a estudiar son muy sencillas y comunes. Se tienen entidades que ofrecen algn tipo de relacin lgica entre sus cardinalidades, y se desea automatizar la consistente. actualizacin de las mismas manteniendo la base de datos

Relacin uno a muchosEn este caso estudiaremos el tpico caso de una ocurrencia de entidad A pertenece a una ocurrencia de la entidad B. Se trata de una relacin de uno a muchos o N a N, o maestro/detalle. Por ejemplo, en este mismo mdulo de artculos se da sta situacin: este artculo pertenece a la categora MySQL y la categora MySQL puede tener muchos artculos en su haber. En este tipo de relaciones, luego de realizar el mdelo conceptual, obtenemos el modelo lgico el cual resulta en la siguiente definicin de tablas (solo se mostrarn las claves primarias y foraneas, obviando cualquier otro tipo de columnas):

Pgina 1/8

Integridad referencial @ Club Desarrolladores CREATE TABLE categorias ( id_categoria integer not null primary key, categoria varchar(30) not null ); CREATE TABLE articulos ( id_articulo integer not null primary key, id_categoria integer null, articulo varchar(60) not null, texto text not null );

En la definicin de la tabla articulos decimos que un artculo no debe necesariamente pertenecer a una categora (puede ser null su referencia a categoria). Veamos un poco de teora. Unaclave primaria se define como una o ms columnas que identifican unvocamente una fila en nuestra misma tabla. En nuestro ejemplo tenemos id_categora en la tabla categorias e id_articulo en la tabla articulos. Como se puede ver en el cdigo stas se definen con las palabras claves primary key. Una clave foranea se define como una o ms columnas que identifican unvocamente una fila en otra tabla. Es decir, una clave foranea referencia a una clave primaria de una tabla foranea. En el ejemplo anterior sera id_categoria de la tabla articulos, que hara referencia a id_categoria de la tabla categorias. Como se puede ver, el motor de base de datos no sabe an de esta referencia (ni de cmo tratarla), a eso lo veremos a continuacin. Pero antes notemos un detalle muy importante, en la definicin 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 cdigo podremos reparar tal situacin y convertir nuestras tablas a INNOdb:ALTER TABLE categorias TYPE=INNODB ALTER TABLE articulos TYPE=INNODB

InconsistenciasUna inconsistencia es una situacin que se presenta cuando una clave foranea referencia a una clave primaria inexistente en la tabla referenciada. Lo explicar con un ejemplo. Supongamos que tenemos dos categoras y tres artculos:

INSERT INTO categorias (id_categoria, categoria) VALUES (1, 'MySQL'), (2, 'Microsoft SQL Server'); INSERT (1, 1, (2, 1, (3, 2, INTO articulos (id_articulo, id_categoria, articulo, texto) VALUES 'Tipos de tablas en MySQL', 'Contenido del artculo...'), 'Procedimientos almacenados', 'Contenido del artculo...'), 'Tablas autoreferenciadas', 'Contenido del artculo...');

Como se puede ver tenemos estas referencias:(1) (1) 'Tipos de tablas en MySQL' -------> (1) 'MySQL' (2) (1) 'Procedimientos almacenados' -------> (1) 'MySQL' (3) (2) 'Tablas autoreferenciadas' -------> (2) 'Microsoft SQL Server'

Nota: Los nmero encerrados entre parntesis indican las claves primarias (y foraneas en el caso de la primer columna) de las tablas en cuestin.

Pgina 2/8

Integridad referencial @ Club Desarrolladores

Y ahora por algn motivo eliminamos de la tabla categorias la categora de MySQL (1). Qu sucede con los artculos que correspondan a esta categora (1) y (2)?... Quedan hurfanos! apuntaran a claves incorrectas y nuestra base de datos se volvera inconsistente. Cuando digo que apuntaran 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 seguiran apuntando a este, pero ste ya no existe!(1) (1) 'Tipos de tablas en MySQL' -------> (???) (2) (1) 'Procedimientos almacenados' -------> (???) (3) (2) 'Tablas autoreferenciadas' -------> (2) 'Microsoft SQL Server'

2 - Se elimina (1) MySQL de la tabla categorias, y se agrega una nueva categora a esta tabla (1) 'PostgreSQL' (esto es totalmente vlido, se cumple la restriccin de clave primaria en categorias). Ahora tenemos que:(1) (1) 'Tipos de tablas en MySQL' -------> (1) 'PostgreSQL' (2) (1) 'Procedimientos almacenados' -------> (1) 'PostgreSQL' (3) (2) 'Tablas autoreferenciadas' -------> (2) 'Microsoft SQL Server'

Noten la primera ocurrencia de relacin: (1) (1) 'Tipos de tablas en MySQL' -------> (1) 'PostgreSQL'... creo que habla por si misma.

Integridad ReferencialEstas situaciones son muy comunes y, aunque pueden resolverse mediante el lenguaje de programacin 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 representara tener que realizar una consulta extra luego de cada UPDATE o DELETE en la tabla referenciada, lo que implicara una perdida de legibilidad y de performance. Por ejemplo si la intencin es eliminar la categora de MySQL, deberemos tambin 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.

DELETE FROM categorias WHERE id_categoria = 1; UPDATE articulos SET id_categoria = NULL WHERE id_categoria = 1;

Y si la intencin es modificar el id_categoria de 'MySQL' a 3 tambin deberamos hacer lo propio en aquellas filas de tabla articulos que referencian a MySQL.UPDATE categorias SET id_categoria = 3 WHERE id_categoria = 1; UPDATE articulos SET id_categoria = 3 WHERE id_categoria = 1;

Si por algn 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 restriccin de clave ajena (o fornea) y por ende la integridad referencial para estas dos tablas:ALTER TABLE articulos ADD FOREIGN KEY(id_categoria) REFERENCES categorias(id_categoria) ON DELETE SET NULL ON UPDATE CASCADE

Pgina 3/8

Integridad referencial @ Club Desarrolladores

Esto se puede leer asi: Alterar la tabla articulos agregando una restriccin a la clave foranea id_categoria (de la tabla articulos) que referencia a id_categoria (de la tabla categorias), cuando se elimine alguna categora (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 modificacin a todos los articulos (a todos los id_categoria de la tabla articulos). Lealo nuevamente si tiene dudas, igualmente aqui hay un ejemplo esclarecedor. Por ejemplo si hacemos:DELETE FROM categorias WHERE id_categoria = 1;

automticamente se le dar el valor NULL a todas las tuplas de articulos cuyo id_categoria sea igual a 1. Quedaria asi:(1) (NULL) 'Tipos de tablas en MySQL' -------> NULL (2) (NULL) 'Procedimientos almacenados' -------> NULL (3) (2) 'Tablas autoreferenciadas' -------> (2) 'Microsoft SQL Server'

Y si en lugar de la eliminacin anterior hacemos una actualizacin como sta:UPDATE categorias SET id_categoria = 3 WHERE id_categoria = 1;

automticamente se le dar el valor 3 a todas las tuplas de articulos cuyo id_categoria sea igual a 1. Nos quedaria asi:(1) (3) 'Tipos de tablas en MySQL' -------> (3) 'MySQL' (2) (3) 'Procedimientos almacenados' -------> (3) 'MySQL' (3) (2) 'Tablas autoreferenciadas' -------> (2) 'Microsoft SQL Server'

Fcil no? una solucin elegante y eficiente. Y que sucede si hubiera definido la restriccin de la siguiente manera:ALTER TABLE articulos ADD FOREIGN KEY(id_categoria) REFERENCES categorias(id_categoria) ON DELETE CASCADE ON UPDATE CASCADE

CASCADE tanto en DELETE como en UPDATE! A eso lo veremos a continuacin cuando expliquemos integridad referencial en una relacin de muchos a muchos (d vuelta la pgina!).

Relacin muchos a muchosAhora que entendimos como funciona la integridad referencial en relaciones de uno a muchos, vamos a ver un caso que tambin se ve mucho en la hora de desarrollar sistemas. Siguiendo con el ejemplo anterior tomemos como caso de estudio el mdulo de foros del club. En este mdulo tenemos temas y categorias (aunque yo trate de llamarla grupos para que suene novedoso). Un tema puede pertenecer a muchas categorias y una categora puede tener muchos temas. Es una relacin muchos a muchos o N a N que puede representarse de la siguiente manera:

Pgina 4/8

Integridad referencial @ Club Desarrolladores CREATE TABLE categorias ( id_categoria integer not null primary key, categoria varchar(30) not null ) ENGINE = INNODB; CREATE TABLE temas ( id_tema integer not null primary key, tema varchar(60) not null, texto text not null ) ENGINE = INNODB;

Esta vez explicitamos que el tipo de tabla que usaremos ser INNOdb. Y tambin creamos la tabla que servir de relacin entre las anteriores:CREATE TABLE categorias_temas ( id_categoria integer not null, id_tema integer not null, primary key(id_categoria, id_tema) ) ENGINE = INNODB;

Puede verse que esta ltima tabla tiene una clave primaria compuesta por dos columnas, e individualmente cada una de estas columnas ser clave fornea de las tablas anteriores segn corresponda como veremos a continuacin luego de explicar los casos de inconsistencias que podriamos resultar.

InconsistenciasSupongamos que tenemos estos datos cargados:

INSERT INTO categorias (id_categoria, categoria) VALUES (1, 'MySQL'), (2, 'Microsoft SQL Server'); INSERT INTO temas (id_tema, tema, texto) VALUES (1, 'Tipos de tabla MySQL, que son?', 'Contenido del tema...'), (2, 'No me sale esta consulta', 'Contenido del tema...'), (3, 'Como lograr el login?!', 'Contenido del tema...'); INSERT INTO categorias_temas (id_categoria, id_tema) VALUES (1, 1), (1, 2), (2, 2), (2, 3);

En forma tabular veriamos algo asi:(1) (1) (2) (2) 'MySQL' ---> (1)(1) ---> (1) 'Tipos de tabla MySQL, que son?' 'MySQL' ---> (1)(2) ---> (2) 'No me sale esta consulta' 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta' 'Microsoft SQL Server' ---> (2)(3) ---> (3) 'Como lograr el login?!'

Empecemos a armar un poco de lio. Eliminemos la categora (1) MySQL y veamos que sucede.

Pgina 5/8

Integridad referencial @ Club Desarrolladores (NULL) ---> (1)(1) (NULL) ---> (1)(2) (2) 'Microsoft SQL (2) 'Microsoft SQL ---> (1) 'Tipos de tabla MySQL, que son?' ---> (2) 'No me sale esta consulta' Server' ---> (2)(2) ---> (2) 'No me sale esta consulta' 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 categora (1) la cual no existe! Olvidemos la maldad anterior, y en lugar de eliminar la categora MySQL eliminaremos el tema (3):(1) (1) (2) (2) 'MySQL' ---> (1)(1) ---> (1) 'Tipos de tabla MySQL, que son?' 'MySQL' ---> (1)(2) ---> (2) 'No me sale esta consulta' 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta' 'Microsoft SQL Server' ---> (2)(3) ---> (NULL)

Ahora tenemos otro problema, aunque muy similar. La categora 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 sucedera si modificramos 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) (3) (2) (2) 'MySQL' ---> (1)(1) ---> (4) 'Tipos de tabla MySQL, que son?' 'MySQL' ---> (1)(2) ---> (2) 'No me sale esta consulta' 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta' '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 automticamente y por lo tanto saltan todas las incoherencias posibles. Cualquiera dira que sta es una base de datos inconsistente.

Integridad ReferencialCortemos por lo sano, apliquemos integridad referencial a estas tablas alterando la que contiene las claves forneas, es decir:

ALTER TABLE categorias_temas ADD FOREIGN KEY(id_categoria) REFERENCES categorias(id_categoria) ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE categorias_temas ADD FOREIGN KEY(id_tema) REFERENCES temas(id_tema) ON DELETE CASCADE ON UPDATE CASCADE;

Como puede verse hay que alterar la tabla dos veces, porque tenemos dos claves forneas a las cuales restringir. Lo voy a traducir al castellano (o espaol... mejor al criollo): Alterar la tabla categorias_temas agregando una restriccin a la clave foranea id_categoria (de la tabla categorias_temas) que referencia a id_categoria (de la tabla categorias), cuando se elimine alguna categora (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 categora eliminada y cuando se modifique alguna categoria (se modifique id_categoria de la tabla categorias) aplicar esa modificacin a todas las tuplas de categorias_temas (a todos los id_categoria de la tabla categorias_temas).

Pgina 6/8

Integridad referencial @ Club Desarrolladores

Luego... Alterar la tabla categorias_temas agregando una restriccin 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 modificacin a todas las tuplas de categorias_temas (a todos los id_tema de la tabla categorias_temas). Difcil de leer? Imagnese escribirlo... Como ejemplo, porque no voy a dejarlo con dudas, vamos a tomar el caso del que hablamos hace poco. Teniendo esto cargado:(1) (1) (2) (2) 'MySQL' ---> (1)(1) ---> (1) 'Tipos de tabla MySQL, que son?' 'MySQL' ---> (1)(2) ---> (2) 'No me sale esta consulta' 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta' '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) (3) (2) (2)

'MySQL' ---> (3)(4) ---> (4) 'Tipos de tabla MySQL, que son?' 'MySQL' ---> (3)(2) ---> (2) 'No me sale esta consulta' 'Microsoft SQL Server' ---> (2)(2) ---> (2) 'No me sale esta consulta' 'Microsoft SQL Server' ---> (2)(3) ---> (3) 'Como lograr el login?!'

Ahora disponemos de una base de datos consistente. Todo es lgico, 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 fcil de declarar... Ser por eso que SQL se trata de un paradigma declarativo? Falta un solo detalle por comentar. En MySQL existe tres opciones ms adems de SET NULL y CASCADE a saber: NO ACTION y RESTRICT: Funcionan igual, no permiten actualizacin alguna (UPDATE o DELETE) rechazando el cambio que intent propagarse. SET DEFAULT: A la fecha no est implementado, debera funcionar de forma similar a SET NULL solo que asignara el valor declarado por defecto al crear las columas de las tablas forneas. Y esto es todo por ahora. Espero que le den un buen uso a este artculo que acabo de escribir, ahora mismo escribo la descripcin (sera como el prlogo, 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 autorGastn tiene 28 aos, vive en Argentina / Santa Fe / Santa Fe y su ocupacin es Desarrollador de aplicaciones web. Ha publicado 31 artculos en clubdesarrolladores con un promedio de valoracin de 7.80 puntos. Puedes visitar su sitio web en http://www.clubdesarrolladores.com

Pgina 7/8

Integridad referencial @ Club Desarrolladores

Descargado de Club Desarrolladores - Visitanos en http://www.clubdesarrolladores.com

Pgina 8/8