この質問に適切に回答するには、最初に以下を決定する必要があります。このシステム/アプリケーションのコンテキストで「削除」とはどういう意味ですか?
答えるためにその質問を、あなたはまだ別の質問に答える必要があります。なぜレコードが削除されていますか?
ユーザーがデータを削除する必要がある理由はいくつかあります。通常、削除が必要になる理由は1 つだけ(テーブルごとに)あることがわかります。以下に例を示します。
- ディスク容量を再利用するには;
- 保持/プライバシーポリシーに従ってハード削除が必要です。
- 破損/絶望的に不正確なデータ。修復よりも削除および再生成が容易です。
- 行の大部分が削除されます。たとえば、ログテーブルがXレコード/日に制限されます。
ハード削除の非常に悪い理由もいくつかあります(これらの理由については後で詳しく説明します)。
- 軽微なエラーを修正します。これは通常、開発者の怠lazと敵対的なUIを強調しています。
- トランザクションを「無効」にする(たとえば、請求されるべきではない請求書)。
- あなたがいるのでできます。
どうしてそんなに大したことなの?良いoleの何が問題なのDELETE
ですか?
- リモートでお金に結び付けられているシステムでも、ハード削除は、アーカイブ/廃棄テーブルに移動された場合でも、あらゆる種類の会計上の期待に違反します。これを処理する正しい方法は、遡及的なイベントです。
- アーカイブテーブルは、ライブスキーマとは異なる傾向があります。新しく追加された列またはカスケードを1つでも忘れると、そのデータは永久に失われます。
- ハード削除は、特にカスケードの場合、非常に高価な操作になる可能性があります。多くの人が気付いていないという複数のレベルをカスケード接続(あるいは場合によっては任意のカスケード、DBMSに依存する)、レコード・レベルの操作の代わりに、一連の操作になります。
- 頻繁にハード削除を繰り返すと、インデックスの断片化のプロセスが高速化されます。
それで、ソフト削除の方が良いでしょう?いいえ、そうでもありません:
- カスケードの設定は非常に困難になります。ほとんど常に、孤立した行としてクライアントに表示される結果になります。
- 1つの削除のみを追跡できます。行が複数回削除および削除解除された場合はどうなりますか?
- 読み取りパフォーマンスは低下しますが、これはパーティション化、ビュー、および/またはフィルター選択されたインデックスによって多少軽減できます。
- 前に示唆したように、実際にはいくつかのシナリオ/管轄区域では違法である可能性があります。
真実は、これらのアプローチの両方が間違っているということです。削除は間違っています。 実際にこの質問をしているのであれば、トランザクションではなく現在の状態をモデリングしていることになります。これは、データベースランドでは悪い、悪い習慣です。
Udi Dahanはこれについて、Do n't Delete-Just Do n'tに書いています。「削除」を実際に表す何らかの種類のタスク、トランザクション、アクティビティ、または(私の優先用語)イベントが常に存在します。後でパフォーマンスのために「現在の状態」テーブルに非正規化する場合は問題ありませんが、前にではなく、トランザクションモデルを打ち込んだ後に行います。
この場合、「ユーザー」がいます。ユーザーは基本的に顧客です。顧客はあなたと取引関係があります。彼らのアカウントをキャンセルしたため、その関係は単に空に消えることはありません。実際に起こっていることは:
- 顧客がアカウントを作成します
- 顧客がアカウントをキャンセルする
- 顧客がアカウントを更新する
- 顧客がアカウントをキャンセルする
- ...
いずれの場合も、同じ顧客であり、場合によっては同じアカウントです(つまり、各アカウントの更新は新しいサービス契約です)。では、なぜ行を削除するのですか?これは非常に簡単にモデル化できます:
+-----------+ +-------------+ +-----------------+
| Account | --->* | Agreement | --->* | AgreementStatus |
+-----------+ +-------------+ +----------------+
| Id | | Id | | AgreementId |
| Name | | AccountId | | EffectiveDate |
| Email | | ... | | StatusCode |
+-----------+ +-------------+ +-----------------+
それでおしまい。これですべてです。何も削除する必要はありません。上記はかなりの柔軟性に対応するかなり一般的な設計ですが、少し単純化することもできます。「Agreement」レベルは不要で、「Account」を「AccountStatus」テーブルに移動させるだけでよいと判断するかもしれません。
アプリケーションで頻繁にアクティブな契約/アカウントのリストを取得する必要がある場合、それは(少し)トリッキーなクエリですが、それがビューの目的です。
CREATE VIEW ActiveAgreements AS
SELECT agg.Id, agg.AccountId, acc.Name, acc.Email, s.EffectiveDate, ...
FROM AgreementStatus s
INNER JOIN Agreement agg
ON agg.Id = s.AgreementId
INNER JOIN Account acc
ON acc.Id = agg.AccountId
WHERE s.StatusCode = 'ACTIVE'
AND NOT EXISTS
(
SELECT 1
FROM AgreementStatus so
WHERE so.AgreementId = s.AgreementId
AND so.EffectiveDate > s.EffectiveDate
)
これで完了です。これで、ソフト削除のすべての利点があり、欠点はないものがあります。
- すべてのレコードが常に表示されるため、孤立レコードは問題ではありません。必要に応じて別のビューから選択するだけです。
- 通常、「削除」は非常に安価な操作です。イベントテーブルに1行を挿入するだけです。
- 、任意の歴史を失うことのチャンス決してありません今まで、あなたが台無しにどのようにひどくどんなに。
- あなたはまだアカウントをハード削除することができた場合、あなたが(プライバシー上の理由など)に必要な、および削除は、アプリケーション/データベースの他の部分に干渉きれいに起こるとしないという知識を快適にします。
取り組まなければならない唯一の問題は、パフォーマンスの問題です。多くの場合、クラスター化インデックスがオンになっているため、実際には問題ではないことが判明していますAgreementStatus (AgreementId, EffectiveDate)
。そこでは、I / Oシークがほとんど行われていません。ただし、それが問題になる場合は、トリガー、インデックス付き/マテリアライズドビュー、アプリケーションレベルのイベントなどを使用して、それを解決する方法があります。
ただし、パフォーマンスを早めに心配しないでください。設計を正しくすることがより重要です。この場合の「正しい」とは、トランザクションシステムとしてデータベースを使用する方法でデータベースを使用することを意味します。