SQL Server でカスケードを使用する場合とその理由は何ですか?
-
09-06-2019 - |
質問
SQL Server で外部キーを設定する場合、どのような状況で削除または更新時に外部キーをカスケードする必要がありますか?またその背後にある理由は何ですか?
これはおそらく他のデータベースにも当てはまります。
私が最も求めているのは、各シナリオの具体的な例、できればそれらをうまく使用した人からの例です。
解決
これまでに見てきたことの要約:
- カスケードをまったく好まない人もいます。
カスケード削除
- カスケード削除は、関係のセマンティクスに排他的な関係が含まれる可能性がある場合に意味があるかもしれません。 「の一部です" 説明。たとえば、OrderLine レコードはその親注文の一部であり、OrderLine が複数の注文間で共有されることはありません。Order が消滅した場合、OrderLine も消滅するはずであり、Order のないラインは問題になります。
- カスケード削除の標準的な例は SomeObject と SomeObjectItems です。この場合、対応するメイン レコードなしでアイテム レコードが存在することは意味がありません。
- あなたがすべき ない 履歴を保存する場合、または削除されたビット列のみを 1/true に設定する「ソフト/論理削除」を使用する場合は、カスケード削除を使用します。
カスケードアップデート
- カスケード更新は、複数のテーブルにわたってサロゲート キー (ID/自動インクリメント列) ではなく実際のキーを使用する場合に意味を持ちます。
- カスケード更新の標準的な例は、変更可能なユーザー名のような変更可能な外部キーがある場合です。
- あなたがすべき ない ID/自動インクリメント列であるキーを使用してカスケード更新を使用します。
- カスケード更新は、一意の制約と組み合わせて使用するのが最適です。
カスケードを使用する場合
- 操作のカスケードを許可する前に、ユーザーから非常に強力な確認を返したい場合がありますが、それはアプリケーションによって異なります。
- 外部キーを間違って設定すると、カスケードによって問題が発生する可能性があります。しかし、それを正しく行えば大丈夫なはずです。
- カスケードを完全に理解する前に使用するのは賢明ではありません。ただし、これは便利な機能なので、時間をかけて理解する価値があります。
他のヒント
外部キーは、データベースの参照整合性を確保するための最良の方法です。魔法であるためにカスケードを回避することは、コンパイラの背後にある魔法を信頼しないために、すべてをアセンブリで記述するようなものです。
問題があるのは、外部キーを逆方向に作成するなど、外部キーの間違った使用です。
Juan Manuel の例は標準的な例です。コードを使用すると、データベースに偽の DocumentItem が残され、攻撃される可能性が高くなります。
カスケード更新は、たとえば、変更される可能性のあるものによってデータへの参照がある場合、たとえば、ユーザー テーブルの主キーが名前と姓の組み合わせである場合に便利です。次に、その組み合わせの変更を参照先のどこにでも反映させたいとします。
@Aidan、あなたが言及したその明確さには高いコストがかかり、データベースに偽のデータが残る可能性があります。 小さくない. 。私にとって、その不安を助長するのは、通常、DB に慣れていないことと、DB を操作する前にどの FK が配置されているかを見つけることができないことです。それか、エンティティが概念的に関連していない場合、または履歴を保存する必要がある場合にカスケードを使用する、カスケードの継続的な誤用のどちらかです。
私はカスケード削除を決して使用しません。
データベースから何かを削除したい場合は、何を削除したいのかをデータベースに明示的に伝えます。
もちろん、これらはデータベースで使用できる関数であり、それらを使用しても問題ない場合もあります。たとえば、「order」テーブルと「orderItem」テーブルがある場合、テーブルを削除するときに項目をクリアしたい場合があります。注文。
「魔法」が起こるよりも、コード (またはストアド プロシージャ) で実行することで得られる明瞭さが気に入っています。
同じ理由で、私もトリガーのファンではありません。
注意すべき点は、「注文」を削除すると、カスケード削除によって 50 個の「orderItem」が削除された場合でも、「1 行が影響を受けました」というレポートが返されることです。
私はカスケード削除を頻繁に使用します。
データベースを操作する人が不要なデータを残さない可能性があることを知るのは、安心です。依存関係が増大した場合は、Management Studio でダイアグラム内の制約を変更するだけでよく、sp やデータアクセスを微調整する必要はありません。
そうは言っても、カスケード削除には 1 つ問題があり、それは循環参照です。これにより、データベースの一部にカスケード削除が行われないことがよくあります。
私は多くのデータベース作業を行っていますが、カスケード削除が役立つとはほとんど感じません。私がそれらを効果的に使用したのは、夜間のジョブによって更新されるレポート データベース内での 1 回だけです。前回のインポート以降に変更されたトップレベルのレコードを削除して、変更されたデータが正しくインポートされていることを確認し、変更されたレコードとそれに関連するものを再インポートします。これにより、データベースの下から上に向かって複雑な削除を大量に記述する必要がなくなりました。
カスケード削除はデータを削除するだけであり、トリガーにはあらゆる種類の厄介なものが含まれる可能性があるため、トリガーほど悪いものとは考えていません。
一般に、私は実際の削除を完全に避け、論理的な削除を使用します。代わりに、true に設定される isDeleted というビット列があります)。
一例としては、エンティティ間に依存関係がある場合です。つまり:Document -> DocumentItems (Document を削除すると、DocumentItems は存在する理由がなくなります)
参照している PK レコードが削除された場合に FK を持つレコードを削除したい場合は、カスケード削除を使用します。言い換えれば、参照レコードがなければレコードは意味を持ちません。
カスケード削除は、無効な参照が null 例外を引き起こすのではなく、デフォルトで確実に削除されるようにするのに便利だと思います。
ON カスケード削除:
君が望む時に 削除する子テーブルの行数 もし 対応する行が削除される 親テーブル内。
もし カスケード削除時 使用されていない場合はエラーが発生します 参照整合性.
ON 更新カスケード:
君が望む時に 主キーの変更 に更新される予定 外部キー
(コード内で実行するのではなく) カスケード削除を行う理由の 1 つは、パフォーマンスを向上させるためです。
ケース 1:カスケード削除の場合
DELETE FROM table WHERE SomeDate < 7 years ago;
ケース 2:カスケード削除を行わない場合
FOR EACH R IN (SELECT FROM table WHERE SomeDate < 7 years ago) LOOP
DELETE FROM ChildTable WHERE tableId = R.tableId;
DELETE FROM table WHERE tableId = R.tableid;
/* More child tables here */
NEXT
次に、カスケード削除を使用して追加の子テーブルを追加すると、ケース 1 のコードは動作し続けます。
関係のセマンティクスが「一部」であるカスケードのみを挿入します。そうしないと、次の操作を行うと、誰かがデータベースの半分を削除してしまいます。
DELETE FROM CURRENCY WHERE CurrencyCode = 'USD'
純粋に過去の悪い経験を理由に、「削除時カスケード」 (およびその他) の使用を禁止している DBA や「企業ポリシー」について聞いたことがあります。あるケースでは、ある男が 3 つのトリガーを作成し、最終的には相互に呼び出しを行うことになりました。回復までに 3 日間かかり、トリガーの全面禁止が課せられました。すべては 1 人のイジットの行為のせいでした。
もちろん、子データを保存する必要がある場合など、「削除時カスケード」の代わりにトリガーが必要になる場合もあります。ただし、他の場合には、削除時カスケード メソッドを使用することが完全に有効です。「削除時カスケード」の主な利点は、すべての子をキャプチャすることです。カスタムで作成されたトリガー/ストア プロシージャは、正しくコーディングされていないと機能しない可能性があります。
開発者は、開発の内容と仕様に記載されている内容に基づいて決定を下すことが許可されるべきだと私は信じています。悪い経験に基づいてカーペットを禁止することが基準となるべきではありません。「決して使用しない」という思考プロセスは、よく言っても厳格です。毎回判断が必要であり、ビジネスモデルの変化に応じて変更も行われます。
これが開発というものではないでしょうか?
SQL サーバーで明示的に要求していない削除や更新は避けるようにしています。
カスケードまたはトリガーの使用のいずれかによって。彼らは、バグを追跡しようとするとき、またはパフォーマンスの問題を診断するときに、いつかお尻を噛む傾向があります。
私がこれらを使用するのは、あまり労力をかけずに一貫性を保証する場合です。同じ効果を得るには、ストアド プロシージャを使用する必要があります。
ここにいる他の皆さんと同じように、私も、カスケード削除は実際にはほんの少ししか役に立たないことに気づきました (他のテーブル内の参照データを削除するのは、実際には大した作業ではありません。テーブルがたくさんある場合は、スクリプトでこれを自動化するだけです) が、本当に面倒です。誰かが誤ってカスケード削除した重要なデータを復元するのが困難な場合。
私が使用する唯一のケースは、テーブル table 内のデータが高度に制御されており (アクセス許可が制限されているなど)、検証済みの制御されたプロセス (ソフトウェア更新など) を通じてのみ更新または削除される場合です。
R の一部のタプルで見つかった外部キー値を削除する S の削除または更新は、次の 3 つの方法のいずれかで処理できます。
- 拒絶
- 伝搬
- 無効化。
伝播はカスケードと呼ばれます。
次の 2 つのケースがあります。
‣ S のタプルが削除された場合、それを参照していた R タプルを削除します。
‣ S のタプルが更新された場合、それを参照する R タプルの値を更新します。
さまざまなバージョンのさまざまなモジュールを含むシステムで作業している場合、カスケード削除されたアイテムが PK ホルダーの一部であるか、PK ホルダーによって所有されている場合、これは非常に役立ちます。そうしないと、すべてのモジュールで、PK 所有者を削除する前に依存項目をクリーンアップするための即時パッチが必要になります。そうしないと、外部キー リレーションが完全に省略され、クリーンアップが正しく実行されないとシステムに大量のゴミが残る可能性があります。
カスケード削除がかなり長い間推奨されなかった後、既存の 2 つのテーブル間の新しい交差テーブル (削除する交差のみ) に対してカスケード削除を導入しました。データが失われた場合もそれほど問題はありません。
ただし、列挙型のリスト テーブルではこれは問題です。誰かがテーブル「colors」からエントリ 13 - 黄色を削除すると、データベース内のすべての黄色の項目が削除されます。また、これらはすべて削除、すべて挿入の方法で更新される場合があり、その結果、参照整合性が完全に省略されてしまいます。もちろんそれは間違っていますが、真の参照整合性の導入には予期せぬ副作用が生じるリスクがある中で、長年実行されてきた複雑なソフトウェアをどのように変更するのでしょうか?
もう 1 つの問題は、主キーが削除された後でも元の外部キー値を保持する必要がある場合です。元の FK に対して廃棄列と ON DELETE SET NULL オプションを作成できますが、これも冗長 (PK 削除後を除く) キー値を維持するためのトリガーまたは特定のコードが必要です。
カスケード削除は、物理データベースに論理スーパータイプおよびサブタイプ エンティティを実装する場合に非常に便利です。
スーパータイプ/サブタイプを物理的に実装するために個別のスーパータイプとサブタイプのテーブルが使用される場合 (すべてのサブタイプ属性を 1 つの物理スーパータイプ テーブルにロールアップするのとは対照的に)、これらのテーブル間の関係が 1 つあり、問題はこれらのテーブル間で主キーの 100% 同期を維持する方法になります。
カスケード削除は、次の場合に非常に便利なツールです。
1) スーパータイプ レコードを削除すると、対応する単一のサブタイプ レコードも削除されることを確認してください。
2) サブタイプ レコードを削除すると、スーパータイプ レコードも削除されるようにしてください。これは、対応するスーパータイプ レコードを削除する「代わりの」削除トリガーをサブタイプ テーブルに実装することで実現されます。これにより、サブタイプ レコードがカスケード削除されます。
この方法でカスケード削除を使用すると、スーパータイプ レコードを先に削除するかサブタイプ レコードを先に削除するかに関係なく、孤立したスーパータイプ レコードやサブタイプ レコードが存在しないことが保証されます。