Administrar la integridad de los datos a través del código de la aplicación le añade complejidad innecesaria al código, además de tener sus riesgos. A pesar de ser cuidadosos, bajo algunas condiciones podemos llegar quebrar la integridad referencial.
Un ejemplo clásico es el de registros "huérfanos". Esto ocurre cuando eliminas de una tabla "padre" un registro y por alguna razón los "hijos" no son eliminados. Esto también puede ocurrir si actualizas el valor de un campo de la tabla "padre" y no actualizas los registros "hijos".
Estos problemas de integridad referencial son tratados por las bases de datos con CONSTRAINTs (restricciones), más específicamente FOREIGN KEY CONTRAINTs.
Con MySQL 5, utilizando el motor de almacenamiento InnoDB no debes preocuparte por controlar la integridad referencial de los datos, pues la base de datos lo hará por ti (así es como debe ser).
Así, si tienes que realizar un sitio con cuentas de usuarios y órdenes de compras, tienes dos tablas como las siguientes:
CREATE TABLE ordenes (
id_orden BIGINT(22) NOT NULL AUTO_INCREMENT COMMENT 'Primary key',
id_usuario BIGINT(22) NOT NULL DEFAULT '0' COMMENT 'Foreign Key',
fecha TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
...
PRIMARY KEY (id_orden),
KEY `key_id_usuario` (`id_usuario`)
) ENGINE=INNODB;
Se ve claramente que tienes una relación de uno a mucho entre la tabla usuarios (como tabla "padre") y la tabla ordenes (como tabla "hijo"). En este punto es cuando puedes agregar datos "huérfanos" (insertar en la tabla ordenes a usuarios que no existen) y no recibir ningún tipo de error ni advertencia. Para solucionar esto, debes agregar una FOREIGN KEY CONTRAINT:
La constraint previene el ingreso de datos "huérfanos":
Lo mismo, si agregas a un registro "padre" algunos "hijos", mira lo que ocurre en la tabla "hijo":
INSERT INTO ordenes (id_orden,id_usuario) VALUES (1,1);
INSERT INTO ordenes (id_orden,id_usuario) VALUES (2,1);
INSERT INTO ordenes (id_orden,id_usuario) VALUES (3,1);
DELETE FROM usuarios WHERE id_usuario = 1;
-- (1 row(s)affected)
SELECT * FROM ordenes;
-- (0 row(s)returned)
12 Respuestas para "Constraints & Cascading en MySQL"
Gracias por la info. Sin duda es una buena razón para decantarse por InnoDB en lugar de MyISAM en algunos proyectos.
Saludos.
Por cierto sabes si desde PHP hay alguna forma de obtener la lista de CONSTRAINTS que se han definido en una tabla?
Gracias.
Desde PHP puedes realizar una consulta (mysql_query) sobre MySQL para consultar cuales son las CONSTRAINTS definidas.
MySQL almacena la “metadata” de las constraints de las tablas en el la tabla TABLE_CONSTRAINTS del esquema information_schema.
SELECT constraint_catalog, constraint_schema, constraint_name, table_schema, table_name, constraint_type FROM information_schema.TABLE_CONSTRAINTS;
Muchas gracias Luciano, es justo lo que andaba buscando.
Saludos.
Hey no entiendo porque no me funciona hice todo como se explica, no me ocurrio ningun error en el proceso y sin embargo me permite introducir datos huerfanos :S
Si puedes, contáctame y enviame el script sql que vas haciendo así reproduzco el error y te puedo decir que sucede.
Saludos.
Hola como estas lo que pasa es que tengo problemas con una tabla, ocurre lo siguiente hay una tabla que tiene tos los datos de una persona nombre, apellidos, sexo, entre otras cosas, lo que sucede es que esta tabla tiene un campo que va referenciado de otra tabla que se llama ocupación, pero ese campo no necesito llenarlo enseguida con los otros datos si no mas adelante y resulta que no me deja seguir me lanza un error que dice #1452 – Cannot add or update a child row: a foreign key constraint fails, la verdad ese campo no es obligatorio solo se llena si la persona lo quiere y no me deja insertar si ese campo no se llena. necesito que me ayudes por fa, te lo agradezco de antemano.
@CATALINA: el campo que va referenciado con la otra tabla, si no llenas ese campo enseguida, deberías dejarlo por defecto un valor NULL. De esta forma, al momento de realizar el INSERT no tendrás violación de Foreign Key. Mira este ejemplo:
CREATE TABLE ocupaciones (
id_ocupacion smallint(6) NOT NULL,
descripcion char(30) default NULL,
PRIMARY KEY (id_ocupacion)
) ENGINE=InnoDB;
CREATE TABLE empleados (
id_empleado int(11) NOT NULL,
Apellidos char(40) default NULL,
Nombre char(40) default NULL,
id_ocupacion smallint(6) default NULL,
PRIMARY KEY (id_empleado),
KEY FK_empleados (id_ocupacion),
CONSTRAINT FK_empleados FOREIGN KEY (id_ocupacion) REFERENCES ocupaciones (id_ocupacion)
) ENGINE=InnoDB;
INSERT INTO ocupaciones (id_ocupacion, descripcion ) VALUES (1, 'Programador Jr' ), (2, 'Programador Sr' ), (3, 'Programador SSr' );
-- el siguiente INSERT no llena el campo id_ocupacion, el mismo queda por default en NULL (no se viola ninguna foreign key)
INSERT INTO empleados (id_empleado, Apellidos, Nombre ) VALUES (1, 'De la Fuente', 'Carlos');
select * from empleados;
-- el siguiente INSERT, en cambio, si dará error (como debe ser) pues intento hacer referencia a un id_ocupación que no existe en la tabla referenciada
INSERT INTO empleados (id_empleado, Apellidos, Nombre, id_ocupacion ) VALUES (2, 'Perez', 'Juan' , 6 );
-- Cannot add or update a child row: a foreign key constraint fails (`test/empleados`, CONSTRAINT `FK_empleados` FOREIGN KEY (`id_ocupacion`) REFERENCES `ocupaciones` (`id_ocupacion`) ON DELETE SET NULL ON UPDATE SET NULL)
Muy buena informacion, mas sin embargo, si me proporciona una buena relacion entre entidades (talas)???
disculpa la pregunta, pero porque colocas ENGINE=InnoDB; al final de crear una tabla? es obligatorio? para que sirve? gracias…
@Jesus, el ENGINE=InnoDB es para especificarle a MySQL el motor de almacenamiento que se utilizará para las tablas. Para la definición de foreign key contraints hay que usar InnoDB.
Si se omite el ENGINE la tabla se creará con el engine por default, al mismo lo puedes ver en la una de las variables de entorno del motor (ejecuta show variables desde un shell de MySQL)
oye key significa lo mismo que foreign key??
por que tu asi lo manejas, o es otro tipo de llave?
Comentarios: