3

私はこれが可能であると確信していますが、私の人生ではそれを理解することはできません.

私が作成したのは、ユーザーと誰によって行われた変更を保持するためのユーザー履歴 MSSQL テーブルです。このテーブルには、他のテーブル (ユーザー) を参照する 2 つの外部キーが含まれています。

私が必要とするのは、(User) テーブルへの変更が、この新しいテーブルの対応するエントリをカスケードして更新することです。

新しいテーブル (User_History) のフィールドは次のとおりです (各ユーザーは 2 つのフィールドで識別されます)。

Affected_User_House_Id  - int
Affected_User_Id - int
Modified_By_User_House_Id - int
Modified_By_User_Id – int
Modification_Date - datetime
ModificationMade - ntext

「ModificationMade」以外の各フィールドは主キーです。フィールド「Modification_Date」の精度は 1 秒までです。私が抱えている問題は、上記のカスケードを作成することです。次の T-SQL コードを実行してみました。

ALTER TABLE [User_History] WITH CHECK
ADD CONSTRAINT [FK_User_History_User] FOREIGN KEY([Affected_User_House_Id], [Affected_User_Id])
REFERENCES [User] ([User_House_Id], [User_ID])
ON UPDATE CASCADE
GO

ALTER TABLE [User_History] CHECK CONSTRAINT [FK_User_History_User]
GO

ALTER TABLE [User_History]  WITH CHECK
ADD CONSTRAINT [FK_User_History_User_ModifiedBy] FOREIGN KEY([Modified_By_User_House_Id], [Modified_By_User_Id])
REFERENCES [User] ([User_House_Id], [User_ID])
ON UPDATE CASCADE
GO

ALTER TABLE [User_History] CHECK CONSTRAINT [FK_User_History_User_ModifiedBy]
GO

この T-SQL により、次のエラーが発生しました。

*'User' table saved successfully
'User_History' table
- Unable to create relationship 'FK_User_History_User_ModifiedBy'.  
Introducing FOREIGN KEY constraint 'FK_User_History_User_ModifiedBy' on table 'User_History' may     cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or     modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.*

2 番目の「ON UPDATE CASCADE」を削除すると、コードは機能しますが、フィールド「Modified_By_User_House_Id」および「Modified_By_User_Id」の値は、User テーブルで参照されている値と一致するように更新されません。

どうすればこの目標を達成できるか途方に暮れています。

4

2 に答える 2

2

指定できるカスケードは 1 つだけです。2 つのトリガーで複数のカスケードをシミュレートする試みを次に示します。

create table TabA (
    ID1 int not null,
    ID2 int not null,
    _RowID int IDENTITY(1,1) not null,
    constraint PK_TabA PRIMARY KEY (ID1,ID2),
    constraint UQ_TabA__RowID UNIQUE (_RowID)
)
go
create table TabB (
    ID1a int not null,
    ID2a int not null,
    ID1b int not null,
    ID2b int not null,
    constraint PK_TabB PRIMARY KEY (ID1a,ID2a,ID1b,ID2b)
)

それらはあなたのテーブルよりも単純ですが、うまくいけば十分に近いものです. には不変の識別子が必要ですが、変更をカスケードすることが全体のポイントであるため、TabA明らかにs はそうではありません。IDだから私は追加しまし_RowIDた。

少なくとも実際の外部キーを実装し、その上でカスケード動作をシミュレートするだけでよいでしょうが、いくつかの単純な反射は、FK が壊れるポイントが常にあることを示します。したがって、それをシミュレートします。

create trigger FK_TabB_TabA on TabB
after insert,update
as
    set nocount on
    if exists (
        select
            *
        from
            inserted i
                left join
            TabA a
                on
                    i.ID1a = a.ID1 and
                    i.ID2a = a.ID2
                left join
            TabA b
                on
                    i.ID1b = b.ID1 and
                    i.ID2b = b.ID2
        where
            a._RowID is null or
            b._RowID is null)
    begin
        declare @Error varchar(max)
        set @Error = 'The INSERT statement conflicted with the Foreign Key constraint "FK_TabB_TabA". The conflict occurred in database "'+DB_NAME()+'", table "dbo.TabB".'
        RAISERROR(@Error,16,0)
        rollback
    end

そして、カスケード更新:

create trigger FK_TabB_TabA_Cascade on TabA
after update
as
    set nocount on

    ;with Updates as (
        select
            d.ID1 as OldID1,
            d.ID2 as OldID2,
            i.ID1 as NewID1,
            i.ID2 as NewID2
        from
            inserted i
                inner join
            deleted d
                on
                    i._RowID = d._RowID
    )
    update b
    set
        ID1a = COALESCE(u1.NewID1,ID1a),
        ID2a = COALESCE(u1.NewID2,ID2a),
        ID1b = COALESCE(u2.NewID1,ID1b),
        ID2b = COALESCE(u2.NewID2,ID2b)
    from
        TabB b
            left join
        Updates u1
            on
                b.ID1a = u1.OldID1 and
                b.ID2a = u1.OldID2
            left join
        Updates u2
            on
                b.ID1b = u2.OldID1 and
                b.ID2b = u2.OldID2
    where
        u1.OldID1 is not null or
        u2.OldID1 is not null
go

簡単な挿入:

insert into TabA (ID1,ID2)
values (1,1),(1,2),(2,1),(2,2)
go
insert into TabB (ID1a,ID2a,ID1b,ID2b)
values (1,1,2,2)

次に、次のエラーが発生します。組み込みの FK 違反とはまったく異なりますが、十分に近いものです。

insert into TabB (ID1a,ID2a,ID1b,ID2b)
values (1,1,2,3)
--Msg 50000, Level 16, State 0, Procedure FK_TabB_TabA, Line 28
--The INSERT statement conflicted with the Foreign Key constraint "FK_TabB_TabA". The conflict occurred in database "Flange", table "dbo.TabB".
--Msg 3609, Level 16, State 1, Line 1
--The transaction ended in the trigger. The batch has been aborted.

これは、実行できるようにしたかった更新です。

update TabA set ID2 = ID2 + 1

そして、FK テーブルをクエリします。

select * from TabB

結果:

ID1a        ID2a        ID1b        ID2b
----------- ----------- ----------- -----------
1           2           2           3

そのため、更新がカスケードされました。


実際の FK を使用できない理由:

カスケード更新が必要です。つまり、ID 値TabAが現在存在しない新しい値に変更されることを意味します (注意 - 2n 行が ID 値を交換する状況を除外しています)。それ以外の場合、この更新によって主キー制約が壊れます。 .

そのため、新しいキー値はまだ存在しないことがわかっています。INSTEAD OF(親の前に子テーブルを更新するために) トリガーを使用してカスケード更新を試みる場合、更新しようとする新しい値はTabBまだ存在しません。あるいは、トリガーを使用してカスケード更新を実行しようとするとAFTER、手遅れになります。FK 制約により、既に更新が妨げられています。

新しい行を「重複」として挿入し、子を更新してから古い行を削除するトリガーを実装できると思います。INSTEAD OFそのような状況では、本当の FK を持つことができると思います。しかし、すべての状況でそのトリガーを正しく記述しようとはしません (たとえば、3 つの行が更新されている場合。2 つの行が ID 値を交換し、もう 1 つが新しい ID を作成します)。

于 2012-09-10T14:24:34.463 に答える
1

このナレッジ ベースの記事によると、このエラー メッセージは、「DELETE ステートメントまたは UPDATE ステートメントのいずれかによって開始されたすべてのカスケード参照アクションのリストに、テーブルが複数回表示されない」場合に発生します。

同じテーブルからのパスが 2 つあるため、回避策として、親テーブルに新しいキーを作成し、子テーブルに単一の外部キーを作成することが考えられます ( [Affected_User_House_Id], [Affected_User_Id], [Modified_By_User_House_Id], [Modified_By_User_Id])。ただし、これにより多くのオーバーヘッドが発生する可能性があります。最後の手段として、トリガーを使用してリレーショナル整合性を強制できます。

于 2012-09-10T13:20:34.640 に答える