3

以下のような構造の非常に単純な tablestudentsがあり、主キーはidです。このテーブルは、頻繁に結合される約 2,000 万行のテーブルの代役です。

+----+----------+------------+
| | ID | 名前 | ドブ | ドブ |
+----+----------+------------+
| | 1 | アリス | 1989 年 1 月 12 日 |
| | 2 | ボブ | 1990 年 4 月 6 日 |
| | 3 | カスバート | 1988 年 1 月 23 日 |
+----+----------+------------+

ボブが生年月日を変更したい場合、いくつかの選択肢があります。

  1. students新しい生年月日で更新します。

    長所: 1 つの DML 操作。このテーブルには、1 回の主キー検索で常にアクセスできます。

    悪い点:ボブが自分の誕生日を 1990 年 4 月 6 日だと思っていたという事実を忘れてしまった

  2. 列 をテーブルに追加しcreated date default sysdate、主キーを に変更しますid, created。すべては次のupdateようになります。

    insert into students(id, name, dob) values (:id, :name, :new_dob)
    

    次に、最新の情報が必要なときはいつでも次のことを行います (Oracle ですが、質問はすべての RDBMS を表します)。

    select id, name, dob
      from ( select a.*, rank() over ( partition by id 
                                           order by created desc ) as "rank"
               from students a )
     where "rank" = 1
    

    良い点:情報を失うことはありません。

    欠点:データベース全体に対するすべてのクエリには、少し時間がかかります。テーブルが指定されたサイズである場合、これは問題ではありませんがleft outer join、一意のスキャンではなく範囲スキャンを使用して 5 番目になると、効果が現れ始めます。

  3. 別の列を追加するdeleted date default to_date('2100/01/01','yyyy/mm/dd')か、または過度に早い、または未来的な日付が好きです。主キーを次のように変更しid, deletedますupdate

    update students x
       set deleted = sysdate 
     where id = :id
       and deleted = ( select max(deleted) from students where id = x.id );
    insert into students(id, name, dob) values ( :id, :name, :new_dob );
    

    現在の情報を取得するクエリは次のようになります。

    select id, name, dob
      from ( select a.*, rank() over ( partition by id 
                                           order by deleted desc ) as "rank"
               from students a )
     where "rank" = 1
    

    良い点:情報を失うことはありません。

    欠点: 2 つの DML 操作。すべてのクエリで一意のインデックス スキャンではなく、追加コストまたは範囲スキャンを使用してランク付けされたクエリを使用する必要があります。

  4. 2番目のテーブルを作成し、student_archiveすべての更新を次のように変更します。

    insert into student_archive select * from students where id = :id;
    update students set dob = :newdob where id = :id;
    

    良い点:情報を失うことはありません。

    欠点: 2 つの DML 操作。すべての情報を取得したい場合は、使用する必要がunionあるか、追加のleft outer join.

  5. 完全を期すために、恐ろしく非正規化されたデータ構造を持っていますid, name1, dob, name2, dob2...

情報を失いたくなく、常に論理的な削除を行う場合、番号 1 はオプションではありません。5 番は、価値以上の問題を引き起こしているため、安全に破棄できます。

オプション 2、3、および 4 には、それに付随するマイナス面が残っています。私は通常、オプション 2 とそれに付随する恐ろしい 150 行 (適切な間隔) の複数のサブ選択結合を使用することになります。


tl;drここで「建設的ではない」投票のライン近くでスケートをしていることに気づきましたが、

データを削除せに論理的な一貫性を維持するための最適な(特異な!) 方法は何ですか?

私が文書化した方法よりも効率的な方法はありますか? このコンテキストでは、「DML 操作が少ない」および/または「サブクエリを削除できる」ことを効率的と定義します。(もし)答えるときにもっと良い定義を思いつくことができれば、どうぞお気軽に。

4

1 に答える 1

1

元のテーブルからデータを削除する必要はありません。元のレコードを更新する前 (または削除する前) に、古い値をアーカイブ テーブルにコピーするだけで十分です。これは、行レベルのトリガーで簡単に実行できます。私の意見では、すべての情報を取得することは頻繁な操作ではなく、余分な結合/結合に問題はありません。また、ビューを定義することもできるため、すべてのクエリはエンド ユーザーの観点から簡単になります。

于 2012-05-01T19:10:59.860 に答える