válida update tipos tabla restricciones referencial referencia integridad hace foreign foranea externa ejemplo datos clave sql sql-server constraints

sql - update - La restricción de clave externa puede causar ciclos o múltiples rutas en cascada?



restricciones sql (9)

Este es un error al escribir las políticas de activación de la base de datos. Un desencadenante es un código y puede agregar algunas inteligencias o condiciones a una relación de Cascada, como la eliminación de cascada. Es posible que necesites especializar las opciones de tablas relacionadas como Desconectar CascadeOnDelete :

protected override void OnModelCreating( DbModelBuilder modelBuilder ) { modelBuilder.Entity<TableName>().HasMany(i => i.Member).WithRequired().WillCascadeOnDelete(false); }

O apaga esta característica por completo:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

Tengo un problema cuando trato de agregar restricciones a mis tablas. Me sale el error:

La introducción de la restricción FOREIGN KEY ''FK74988DB24B3C886'' en la tabla ''Empleado'' puede provocar ciclos o múltiples rutas en cascada. Especifique ON DELETE NO ACTION o ON UPDATE NO ACTION, o modifique otras restricciones FOREIGN KEY.

Mi restricción es entre una tabla de Code y una tabla de employee . La tabla de Code contiene Id , Name , FriendlyName , Type y un Value . El employee tiene una serie de campos que hacen referencia a los códigos, de modo que puede haber una referencia para cada tipo de código.

Necesito que los campos se configuren como nulos si se elimina el código al que se hace referencia.

¿Alguna idea de cómo puedo hacer esto?


Esto se debe a que Emplyee podría tener Colección de otra entidad, digamos Calificaciones y Calificación podría tener otras Universidades de recopilación, por ej.

public class Employee{ public virtual ICollection<Qualification> Qualifications {get;set;}

}

public class Qualification{ public Employee Employee {get;set;} public virtual ICollection<University> Universities {get;set;}

}

public class University{ public Qualification Qualification {get;set;}

}

En DataContext podría ser como a continuación

protected override void OnModelCreating(DbModelBuilder modelBuilder){ modelBuilder.Entity<Qualification>().HasRequired(x=> x.Employee).WithMany(e => e.Qualifications); modelBuilder.Entity<University>.HasRequired(x => x.Qualification).WithMany(e => e.Universities);

}

en este caso hay una cadena de Empleado a Calificación y de Calificación a Universidades. Entonces me estaba lanzando la misma excepción.

Me funcionó cuando cambié

modelBuilder.Entity<Qualification>().**HasRequired**(x=> x.Employee).WithMany(e => e.Qualifications);

A

modelBuilder.Entity<Qualification>().**HasOptional**(x=> x.Employee).WithMany(e => e.Qualifications);



Me gustaría señalar que (funcionalmente) hay una GRAN diferencia entre ciclos y / o múltiples rutas en el SCHEMA y los DATOS. Si bien los ciclos y quizás las multitrayectorias en los DATOS ciertamente podrían complicar el procesamiento y causar problemas de rendimiento (costo de manejo "adecuado"), el costo de estas características en el esquema debería ser cercano a cero.

Dado que la mayoría de los ciclos aparentes en los RDB se producen en estructuras jerárquicas (organigrama, parte, subparte, etc.) es desafortunado que SQL Server asuma lo peor; es decir, ciclo de esquema == ciclo de datos. De hecho, si usa restricciones de RI, ¡no puede construir un ciclo en los datos!

Sospecho que el problema de multitrayecto es similar; es decir, múltiples rutas en el esquema no implican necesariamente múltiples rutas en los datos, pero tengo menos experiencia con el problema de multitrayecto.

Por supuesto, si SQL Server permitió ciclos, aún estaría sujeto a una profundidad de 32, pero eso probablemente sea adecuado para la mayoría de los casos. (Lástima que no es una configuración de base de datos, sin embargo!)

Los activadores "En lugar de Eliminar" tampoco funcionan. La segunda vez que se visita una tabla, se ignora el desencadenador. Entonces, si realmente quieres simular una cascada, tendrás que usar procedimientos almacenados en presencia de ciclos. Sin embargo, el disparador en lugar de borrar funcionaría para casos de trayectos múltiples.

Celko sugiere una "mejor" forma de representar las jerarquías que no introduce ciclos, pero hay compensaciones.


Mi solución a este problema que se encontró al utilizar ASP.NET Core 2.0 y EF Core 2.0 fue realizar lo siguiente en orden:

  1. Ejecute el comando update-database en la consola de gestión de paquetes (PMC) para crear la base de datos (esto da como resultado la "introducción de la restricción FOREIGN KEY ... puede causar ciclos o múltiples rutas en cascada". Error)

  2. Ejecute el comando script-migration -Idempotent en PMC para crear un script que se pueda ejecutar independientemente de las tablas / restricciones existentes

  3. Tome la secuencia de comandos resultante y encuentre ON DELETE CASCADE y reemplace con ON DELETE NO ACTION

  4. Ejecute el SQL modificado contra la base de datos

Ahora, sus migraciones deberían estar actualizadas y las eliminaciones en cascada no deberían ocurrir.

Lástima que no pude encontrar ninguna forma de hacerlo en Entity Framework Core 2.0.

¡Buena suerte!


Por los sonidos que tiene, tiene una acción OnDelete / OnUpdate en una de sus Foreign Keys existentes, que modificará su tabla de códigos.

Entonces, al crear esta clave externa, estarías creando un problema cíclico,

Por ejemplo, la actualización de empleados, hace que los códigos cambien por una acción de actualización en marcha, hace que los empleados se modifiquen mediante una acción de actualización activada ... etc ...

Si publica sus Definiciones de tabla para ambas tablas, y sus definiciones de Clave externa / restricción, deberíamos poder decirle dónde está el problema ...


SQL Server hace un recuento simple de rutas en cascada y, en lugar de tratar de determinar si existen ciclos en realidad, asume lo peor y se niega a crear las acciones referenciales (CASCADE): puede y aún debe crear las restricciones sin las acciones referenciales. Si no puede modificar su diseño (o hacerlo podría poner en peligro las cosas), debería considerar el uso de desencadenantes como último recurso.

FWIW resolver rutas en cascada es un problema complejo. Otros productos de SQL simplemente ignorarán el problema y le permitirán crear ciclos, en cuyo caso será una carrera para ver cuál sobrescribirá el último valor, probablemente por ignorancia del diseñador (por ejemplo, ACE / Jet lo hace). Entiendo que algunos productos SQL intentarán resolver casos simples. El hecho permanece, SQL Server ni siquiera lo intenta, lo juega de forma ultra segura al no permitir más de una ruta y al menos se lo dice.


Trigger es la solución para este problema:

IF OBJECT_ID(''dbo.fktest2'', ''U'') IS NOT NULL drop table fktest2 IF OBJECT_ID(''dbo.fktest1'', ''U'') IS NOT NULL drop table fktest1 IF EXISTS (SELECT name FROM sysobjects WHERE name = ''fkTest1Trigger'' AND type = ''TR'') DROP TRIGGER dbo.fkTest1Trigger go create table fktest1 (id int primary key, anQId int identity) go create table fktest2 (id1 int, id2 int, anQId int identity, FOREIGN KEY (id1) REFERENCES fktest1 (id) ON DELETE CASCADE ON UPDATE CASCADE/*, FOREIGN KEY (id2) REFERENCES fktest1 (id) this causes compile error so we have to use triggers ON DELETE CASCADE ON UPDATE CASCADE*/ ) go CREATE TRIGGER fkTest1Trigger ON fkTest1 AFTER INSERT, UPDATE, DELETE AS if @@ROWCOUNT = 0 return set nocount on -- This code is replacement for foreign key cascade (auto update of field in destination table when its referenced primary key in source table changes. -- Compiler complains only when you use multiple cascased. It throws this compile error: -- Rrigger Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, -- or modify other FOREIGN KEY constraints. IF ((UPDATE (id) and exists(select 1 from fktest1 A join deleted B on B.anqid = A.anqid where B.id <> A.id))) begin update fktest2 set id2 = i.id from deleted d join fktest2 on d.id = fktest2.id2 join inserted i on i.anqid = d.anqid end if exists (select 1 from deleted) DELETE one FROM fktest2 one LEFT JOIN fktest1 two ON two.id = one.id2 where two.id is null -- drop all from dest table which are not in source table GO insert into fktest1 (id) values (1) insert into fktest1 (id) values (2) insert into fktest1 (id) values (3) insert into fktest2 (id1, id2) values (1,1) insert into fktest2 (id1, id2) values (2,2) insert into fktest2 (id1, id2) values (1,3) select * from fktest1 select * from fktest2 update fktest1 set id=11 where id=1 update fktest1 set id=22 where id=2 update fktest1 set id=33 where id=3 delete from fktest1 where id > 22 select * from fktest1 select * from fktest2


Una situación típica con múltiples rutas de castración será esta: una tabla maestra con dos detalles, digamos "Maestro" y "Detalle1" y "Detalle2". Ambos detalles son eliminación en cascada. Hasta ahora no hay problemas. Pero, ¿qué pasa si ambos detalles tienen una relación de uno a muchos con alguna otra tabla (digamos "SomeOtherTable")? SomeOtherTable tiene una columna Detail1ID Y una columna Detail2ID.

Master { ID, masterfields } Detail1 { ID, MasterID, detail1fields } Detail2 { ID, MasterID, detail2fields } SomeOtherTable {ID, Detail1ID, Detail2ID, someothertablefields }

En otras palabras: algunos de los registros en SomeOtherTable están vinculados con Detail1-records y algunos de los registros en SomeOtherTable están vinculados con Detail2 records. Incluso si se garantiza que los registros SomeOtherTable nunca pertenecen a ambos detalles, ahora es imposible hacer que los registros de SomeOhterTable se eliminen en cascada para ambos detalles, porque hay múltiples rutas en cascada desde Master a SomeOtherTable (una a través del Detail1 y otra vía Detail2). Ahora es posible que ya haya entendido esto. Aquí hay una posible solución:

Master { ID, masterfields } DetailMain { ID, MasterID } Detail1 { DetailMainID, detail1fields } Detail2 { DetailMainID, detail2fields } SomeOtherTable {ID, DetailMainID, someothertablefields }

Todos los campos ID son campos clave y autoincremento. La clave está en los campos DetailMainId de las tablas de detalles. Estos campos son clave y referencial referencial. Ahora es posible eliminar todo en cascada eliminando solo registros maestros. La desventaja es que para cada registro de detalle1 Y para cada registro de detalle2, también debe haber un registro detallado de detalle (que en realidad se crea primero para obtener el ID correcto y único).