2009-05-12 5 views
137

テーブルに制約を追加しようとすると問題が発生します。私はエラーを取得する:外部キー制約により、サイクルまたは複数のカスケードパスが発生する可能性がありますか?

Introducing FOREIGN KEY constraint 'FK74988DB24B3C886' on table 'Employee' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.

私の制約がCodeテーブルとemployeeテーブルの間にあります。 CodeテーブルはIdNameFriendlyNameTypeValueが含まれています。 employeeにはコードを参照するいくつかのフィールドがあり、各コードタイプの参照ができるようになっています。

参照されているコードが削除された場合は、フィールドをnullに設定する必要があります。

どうすればいいですか?あなたのコードテーブルを変更します、既存の外部キーの一つ、上のOnDelete /にonUpdate作用を有することの音によって

+0

解決策の1つは[ここ]です(http://stackoverflow.com/a/5828969/148271) – IsmailS

答えて

146

SQL Serverは、むしろ任意のサイクルが実際に存在するかどうかを仕事にしようとしてより簡単なカスケードパスのカウントとを、ない、それは最悪の事態を想定して作成することを拒否します参照アクション(CASCADE):参照アクションを使用せずに制約を作成することはできます。デザインを変更できない場合(またはそのようにすると物が妥協する場合)、最後の手段としてトリガーを使用することを検討する必要があります。

カスケードパスを解決するFWIWは複雑な問題です。他のSQL製品は単に問題を無視してサイクルを作成することができます。その場合は、最後に値を上書きするかどうかを判断するレースになります(たとえば、ACE/Jetではこれを実行します)。私は、単純なケースを解決しようとするSQL製品を理解しています。事実、SQL Serverは複数のパスを許可しないことで非常に安全にプレイしようとしますが、少なくともそれはあなたに伝えます。

+0

私は最後に同じ結託に来て、トリガーを使用して解決した。 DBMSとの間にこの違いがあることを知ってうれしく思います。実際に私が固執しているのは設計上の決定です。ありがとうたくさん:) –

+1

私はまだ理解できない1つのことは、この "問題"トリガーを使用して解決することができる場合、どのようにトリガーは、サイクルや複数のカスケードパス... "原因になりませんか? – armen

+3

@armen:トリガーは、システムが暗黙的に把握できなかったロジックを明示的に提供するため、例えば参照アクションを削除するパスが複数ある場合、トリガーコードはどのテーブルを削除し、どの順序で行うかを定義します。 – onedaywhen

2

だから、この外部キーを作成することによって、あなたは巡回問題を作成することだろう、例えば

従業員を更新し、更新アクション時にコードを変更し、更新アクション時に従業員を変更させます...

両方のテーブルにテーブル定義を投稿すると、外部キー/制約私たちは、問題がどこにあるあなたを伝えることができるはずです定義...

+1

これはかなり長いので、ここに投稿することはできませんが、私はあなたにそれらを送ることができる何らかの方法があるかどうか知りませんか? 悪い試して説明してください: 存在する唯一の制約は、すべて単純なINT Idキーでコードを参照するフィールドを持つ3つのテーブルからです。問題は、従業員がコードテーブルを参照するいくつかのフィールドを持っていることと、それらをすべて私がSET NULLにカスケードしたいと思うようです。 私が必要とするのは、コードが削除されると、それらへの参照をどこにでも設定する必要があるということです。 –

+0

とにかくそれらを投稿してください...私はここに誰も気にしないだろうと思うと、コードウィンドウがスクロールブロックで適切にそれらをフォーマットします:) –

11

私は、(機能的に)SCHEMAとDATAのサイクルや複数のパスの間に大きな違いがあることを指摘します。 DATAのサイクルやおそらくマルチパスは処理を複雑にし、パフォーマンス上の問題(「適切に処理する」コスト)を引き起こす可能性がありますが、スキーマ内のこれらの特性のコストはゼロに近くなければなりません。

のRDBに最も明らかサイクルは階層構造に(組織図、一部、サブパート、等)を発生するので、SQL Serverが最悪想定して残念です。すなわち、スキーマサイクル==データサイクル。実際に、RI制約を使用している場合は、実際にはデータのサイクルを構築できません。

私は、マルチパスの問題は似ていると思われます。すなわち、スキーマ内の複数の経路は、必ずしもデータ内に複数の経路を含むとは限らないが、マルチパス問題の経験は少ない。

もちろん、SQL Server を実行した場合、それでも32の深さになる可能性がありますが、ほとんどの場合これで十分でしょう。 (あまりにも悪いのはデータベース設定ではない!)

"削除の代わりに"トリガーも機能しません。 2回目にテーブルにアクセスすると、トリガは無視されます。したがって、実際にカスケードをシミュレートしたい場合は、サイクルの存在下でストアドプロシージャを使用する必要があります。しかし、Delete-of-Delete-Triggerはマルチパスの場合には有効です。

Celkoは、サイクルを導入しない階層を表現する「より良い」方法を提案しますが、トレードオフがあります。

+0

"RI制約を使用している場合は、実際にデータを作成することはできません!" - いい視点ね! – onedaywhen

73

複数のカスケードパスを使用する典型的な状況は、次のようになります。 2つの詳細があるマスターテーブル。「マスター」と「詳細1」と「詳細2」とします。両方の詳細はカスケード削除です。これまでのところ問題はありません。しかし、両方の詳細が他のテーブルとの関係が1対多(「SomeOtherTable」など)の場合はどうでしょうか。 SomeOtherTableにはDetail1ID列とDetail2ID列があります。言い換えれば

Master { ID, masterfields } 

Detail1 { ID, MasterID, detail1fields } 

Detail2 { ID, MasterID, detail2fields } 

SomeOtherTable {ID, Detail1ID, Detail2ID, someothertablefields } 

:SomeOtherTable内のレコードのいくつかは詳細1レコードにリンクされているとSomeOtherTable内のレコードのいくつかは、詳細2レコードにリンクされています。 SomeOtherTableレコードが両方の詳細に属していることが保証されていても、MasterからSomeOtherTableへの複数のカスケードパスがあるため(Detail1経由で、Detail2経由で)、SomeOhterTableのレコードを両方の詳細に対してカスケード削除することは不可能です。 これはすでに理解しているかもしれません。可能な解決策は次のとおりです。

Master { ID, masterfields } 

DetailMain { ID, MasterID } 

Detail1 { DetailMainID, detail1fields } 

Detail2 { DetailMainID, detail2fields } 

SomeOtherTable {ID, DetailMainID, someothertablefields } 

すべてのIDフィールドは、キーフィールドと自動インクリメントです。要点は、詳細テーブルのDetailMainIdフィールドにあります。これらのフィールドは、キーと参照両方の制約です。マスターレコードを削除するだけでカスケード削除が可能になりました。欠点は、detail1レコードごとに、またdetail2レコードごとに、DetailMainレコード(実際には一意かつ正しいIDを得るために実際に作成されたレコード)が必要であることです。

+0

あなたのご意見は私が直面している問題を理解するのを助けてくれました。ありがとうございました!私はパスのいずれかのカスケード削除をオフにし、他のいくつかの他の方法(ストアドプロシージャ、トリガ、コードなど)の削除を処理することをお勧めします。しかし、私は同じ問題の可能な異なるアプリケーションのためのあなたの解決策(1つの経路でグループ化)を念頭に置いてください... – freewill

+0

クルクスという言葉の使用のために1つ(また説明するために) – masterwok

+0

これはトリガーを書くよりも優れていますか?カスケードを機能させるためにテーブルを追加するのは奇妙に思えます。 – dumbledad

1

これはEmplyeeが他のエンティティのコレクションを持っている可能性があるためです。資格と資格には他のコレクションがあるかもしれません。 それは

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); 
以下のようであってもよいのDataContextに

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;} 

}

}

この場合、従業員から資格、および資格から大学への連鎖があります。だから私に同じ例外を投げていた。

私はこれは型データベース・トリガー政策の誤りである

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

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

を変えたとき、それは私のために働きました。 トリガーはコードであり、カスケード削除などのカスケードリレーションシップにいくつかのインテリジェンスまたは条件を追加できます。

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

または完全にこの機能をオフにする:あなたはCascadeOnDeleteオフのように、この周りに関連するテーブルのオプションを特化する必要があるかもしれません

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 
0

トリガーは、この問題の解決策である。この問題の

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 
0

私のソリューションは、Core 2.0は、順番に次のことを実行することでしたASP.NETコア2.0とEFを使用して発生しました:

  1. パッケージ管理コンソール(PMC)でupdate-databaseコマンドを実行してデータベースを作成します(これにより、「FOREIGN KEY結果のスクリプトを取るに関係なく、既存のテーブル/制約の

  2. 実行できるスクリプトを作成するには、PMCでtraint ...サイクルまたは複数のカスケードパスを引き起こす可能性があります。」というエラー)

  3. 実行script-migration -Idempotentコマンドとあなたの移行、

今、データベースに対して変更されたSQLを実行しON DELETE CASCADEを見つけて、ON DELETE NO ACTION

  • と交換寿最新のものであり、連鎖的な削除は起こらないはずです。

    あまりにも、Entity Framework Core 2.0でこれを行う方法が見つかりませんでした。

    幸運を祈る!

  • 関連する問題