2

数日から私は次のシナリオを考えています

1対多の親子関係を持つ2つのテーブルがあるとします。親行を削除すると、親に関連する子の行を削除する必要があります。簡単でしょ?

上記の操作を行うには、トランザクションスコープを作成する必要があります。これは次のように実行できます。(その疑似コードですが、私はodbc接続を使用してc#コードでこれを行っており、データベースはSQLサーバーです)

  1. トランザクションの開始(読み取りコミット)
  2. child.fk=p1であるすべての子を読み取ります
  3. foreach(child)child.pk=cxである子を削除します
  4. parent.pk=p1である親を削除します
  5. トランスをコミットする

また

  1. トランザクションの開始(読み取りコミット)
  2. child.fk=p1であるすべての子を削除します
  3. parent.pk=p1である親を削除します
  4. トランスをコミットする

今、私の心の中にいくつかの質問があります

  1. 数秒以内に他の何千もの操作(選択/更新/削除/挿入)が実行されるリアルタイムシステムのシナリオを特に考慮して、上記のどれを使用するのが良いですか。

  2. トランザクションが完了するまで、child.fk = p1の新しい子が追加されないようにしますか?

  3. 2番目の質問で「はい」の場合、それはどのように保証されますか?それはテーブルレベルのロックを取りますか、それとも何ですか。

  4. SQL Serverでサポートされているインデックスロックの種類はありますか?はいの場合、その機能と使用方法はありますか?

よろしくムバシャール

4

2 に答える 2

2

私はカスケード削除を使用したことはありません(また、正当な必要性もありませんでした)。また、これを強制するためにトリガーを使用したこともありません。これの主な理由は、通常、次のとおりです。

  • アプリでは削除も許可されていません。削除済みのマークが付けられているか、すべての時間で時間的に一貫しており、発効日や終了日などがあります。
  • 親が誤って削除された場合、それらに関連付けられているすべてのものが単に消えるわけではないことを知りたいので、削除をカスケードせずにRIを使用すると、依存関係のツリー全体を削除できなくなります。
  • アプリケーションとデータベースの設計で、エンティティの相互依存性についてより慎重になり、構造とプロセスの両方を適切にリファクタリングできるようにします。
  • エンティティに適切な削除手順を強制的に作成することで、すべてのステップの順序を選択し、デッドロックを回避できる可能性があります。また、クエリが調整されていることを確認できます。

私が見ることができるカスケード削除の唯一の利点は、それが宣言型であり、テーブルで定義されており、おそらく可能な限り最小のロックエスカレーションフットプリントを持つことです。上記の利点は、私の見解ではその使用を上回ります。

2番目の例のように、トランザクション(通常はストアドプロシージャ)で囲みます。

DELETE FROM child WHERE child.fk IN (set to delete);
DELETE FROM parent WHERE parent.pk IN (set to delete);

何らかの理由で子または親をすべて削除できない場合、つまり、削除で考慮されていない子または親への別のFK参照があった場合、トランザクション全体がデータベースを一貫した状態のままにして成功するか、変更をコミットできません。 。

データベースは、常に参照整合性を保証します。

USE SandBox
GO

IF EXISTS ( SELECT  *
            FROM    sys.objects
            WHERE   object_id = OBJECT_ID(N'Child')
                    AND type IN ( N'U' ) ) 
    DROP TABLE dbo.Child
GO

IF EXISTS ( SELECT  *
            FROM    sys.objects
            WHERE   object_id = OBJECT_ID(N'Parent')
                    AND type IN ( N'U' ) ) 
    DROP TABLE dbo.Parent
GO

CREATE TABLE Parent
    (
     PK INT NOT NULL
            IDENTITY
    ,Nm VARCHAR(15)
    ,PRIMARY KEY ( PK )
    )
GO

CREATE TABLE Child
    (
     PK INT NOT NULL
            IDENTITY
    ,FK INT NOT NULL
    ,Nm VARCHAR(15)
    ,PRIMARY KEY ( PK )
    ) 
GO

ALTER TABLE Child
        WITH CHECK
ADD CONSTRAINT FK_Child_Parent FOREIGN KEY ( FK ) REFERENCES Parent ( PK )
GO

DECLARE @LastParent AS INT

INSERT  INTO Parent ( Nm )
VALUES  ( 'Donald Duck' )
SET @LastParent = SCOPE_IDENTITY()

INSERT  INTO Child ( FK, Nm )
VALUES  ( @LastParent, 'Huey' )
INSERT  INTO Child ( FK, Nm )
VALUES  ( @LastParent, 'Dewey' )
INSERT  INTO Child ( FK, Nm )
VALUES  ( @LastParent, 'Louie' )

SELECT  *
FROM    Parent
SELECT  *
FROM    Child
GO

BEGIN TRANSACTION
DELETE  FROM Child
WHERE   FK = ( SELECT   PK
               FROM     Parent
               WHERE    Nm = 'Donald Duck'
             )
 -- Run just to here
 -- In another session do this:
 -- INSERT INTO Child (FK, Nm) VALUES ((SELECT PK FROM Parent WHERE Nm = 'Donald Duck'), 'Cuckoo')
 -- Then return here
DELETE  FROM Parent
WHERE   Nm = 'Donald Duck' -- Should fail
IF @@ERROR <> 0 
    ROLLBACK TRANSACTION
ELSE 
    COMMIT TRANSACTION

SELECT  *
FROM    Parent
SELECT  *
FROM    Child
于 2010-04-04T11:43:29.403 に答える
0

どちらのアプローチも間違っています。あなたは常にすべきです:

  • 最初に親を挿入し、次に子を挿入します
  • 最初に親を更新し、次に子を更新します
  • 最初に親を削除し、次に子を削除します
  • 最初に親を選択し、次に子を選択します

さらに良いことに、カスケード削除を使用して参照整合性を宣言し、それが子の削除を処理できるようにします。

問題の要点は、順序を選択して、親から子または子から親のいずれかに固執する必要があるということです。後者はほとんどの場合意味がないので、前者に固執する方がよいでしょう。

于 2010-04-04T07:22:44.907 に答える