9

私は、データベースへの変更を追跡するソリューションの開発を任されています。

キャプチャする必要がある更新の場合:

  • 更新日
  • 古い値
  • 新しい価値
  • 影響を受ける分野
  • 変化をしている人
  • レコードID
  • テーブルレコードは

削除の場合:

  • 削除日
  • 削除している人
  • レコードのタイトル/説明/ID が削除されました。すべての変更を追跡しているテーブルには、タイトルまたは説明フィールドがあります。レコードが削除される前にこれをキャプチャしたいと思います。
  • テーブルレコードがありました

インサートの場合:

  • 挿入日
  • 変化をしている人
  • レコードID
  • テーブルレコードは

私はこれを行うためのいくつかの方法を考えました:

  • 更新/削除/挿入にストアド プロシージャを使用しています。一般的な「追跡」テーブルを作成します。すべてのデータをキャプチャするのに十分なフィールドがあります。次に、ストアド プロシージャごとに別の行を追加して、「レコードを追跡テーブルに挿入する」という効果を出します。
    • 欠点:すべての更新/削除/挿入がすべて同じテーブルでごちゃまぜになっている
    • 多くの NULL フィールド
    • バッチ更新/削除/挿入を追跡するにはどうすればよいですか? <----これは問題ではないかもしれません。私は実際にアプリケーションでこのようなことをしません。
    • 更新を行っているユーザーをキャプチャするにはどうすればよいですか。データベースは 1 つのアカウントしか認識しません。
    • 編集する既存のコードの多くを編集します。
  • 最後に、更新/削除/挿入後に呼び出されるトリガーを作成できます。最初の解決策と同じ欠点の多くは次のとおりです。多くのコードを編集する必要があります。更新を追跡する方法がわかりません。トリガーを使用して最近更新されたレコードを表示する方法はないようです。

私は asp.net、C#、SQL Server 2005、iis6、windows 2003 を使用しています。予算がないので、悲しいことに、これを支援するものを購入できません。

回答ありがとうございます。

4

8 に答える 8

5

この問題を回避したくありませんし、予算がないことも承知していますが、最も簡単な解決策は SQL Server 2008 にアップグレードすることです。SQL Server 2008 にはこの機能が組み込まれています。自分で使用できなくても、少なくともこの質問に出くわした他の人には言及する必要があると思いました.

(SQL 2008 の展開可能なエディションの中で、この機能は Enterprise でのみ使用できます。)

于 2008-11-18T20:09:51.690 に答える
5

トリガーには、さまざまな理由から必要なすべての情報が含まれているわけではありませんが、決定的なユーザー ID はありません。

変更が行われた場所に挿入する一般的な sp を使用して、正しい軌道に乗っていると思います。インターフェイスの sp を標準化する場合は、ゲームの先を行っていることになります。追跡されていない変更を忍び込むことは困難です。

これを、会計アプリケーションの監査証跡に相当するものと考えてください。これがジャーナルです。すべてのトランザクションが記録された単一のテーブルです。彼らは預金、引き出し、調整などに個別のジャーナルを実装しません。これは同じ原則です。

于 2008-11-18T20:16:08.290 に答える
3

すべてのテーブルで 2 つの列を使用することをお勧めします。rowhistoryIsDeletedという名前が付けられ、データ型は xml と bit になります。 行を削除せず、常に IsDeleted フラグを使用 します。更新トリガーを使用します。同じ例を挙げます Page と呼ばれるこの1つのテーブルがあります

    CREATE TABLE te_Page([Id] [int] IDENTITY(1,1) NOT NULL, [Name] [varchar](200) NOT NULL, [Description] [varchar](200) NULL,[CreatedBy] [uniqueidentifier] NULL, [CreatedDate] [datetime] NOT NULL, [UpdatedBy] [uniqueidentifier] NULL, [UpdatedDate] [datetime] NULL, [IsDeleted] [bit] NULL, [RowHistory] [xml] NULL, CONSTRAINT [PK_tm_Page] PRIMARY KEY CLUSTERED ([Id] ASC )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY] ) ON [PRIMARY]

テーブルを作成したら、次のコードをコピーして貼り付けるだけで、Page テーブルのタスクは完了です。古い値と新しい値とともに更新される同じ行の行の履歴の記録を開始します。

                ALTER Trigger [dbo].[Trg_Te_Page]    
        On [dbo].[te_Page]                
        After Update                
        As                 
        --If @@rowcount = 0 Or Update(RowHistory)    
        --Return    

        Declare @xml NVARCHAR(MAX)     
        Declare @currentxml NVARCHAR(MAX)   
        Declare @node NVARCHAR(MAX)    
        Declare @ishistoryexists XML    

        Declare @FormLineAttributeValueId int  

        -- new Values  
        Declare @new_Name varchar(200)  
        Declare @new_Description varchar(200)  

        Declare @new_CreatedBy UNIQUEIDENTIFIER    
        Declare @new_CreatedDate DATETIME    
        Declare @new_UpdatedBy UNIQUEIDENTIFIER    
        Declare @new_UpdatedDate DATETIME    
        Declare @new_IsDeleted BIT  

        --old values  
        Declare @old_Name varchar(200)  
        Declare @old_Description varchar(200)  

        Declare @old_CreatedBy UNIQUEIDENTIFIER    
        Declare @old_CreatedDate DATETIME    
        Declare @old_UpdatedBy UNIQUEIDENTIFIER    
        Declare @old_UpdatedDate DATETIME    
        Declare @old_IsDeleted BIT  


        -- declare temp fmId  
        Declare @fmId int  
        -- declare cursor  
        DECLARE curFormId cursor   
        FOR select Id from INSERTED   
        -- open cursor       
        OPEN curFormId  
        -- fetch row  
        FETCH NEXT FROM curFormId INTO @fmId  

        WHILE @@FETCH_STATUS  = 0   
        BEGIN   

        Select   
        @FormLineAttributeValueId = Id,   
        @old_Name = Name,  
        @old_Description = [Description],  

        @old_CreatedBy = CreatedBy,    
        @old_CreatedDate =CreatedDate,  
        @old_UpdatedBy =UpdatedBy,    
        @old_UpdatedDate =UpdatedDate,  
        @old_IsDeleted  = IsDeleted,  
        @currentxml = cast(RowHistory as NVARCHAR(MAX))  
        From DELETED where Id=@fmId  



        Select      
        @new_Name = Name,  
        @new_Description = [Description],  

        @new_CreatedBy = CreatedBy,    
        @new_CreatedDate =CreatedDate,  
        @new_UpdatedBy =UpdatedBy,    
        @new_UpdatedDate =UpdatedDate,  
        @new_IsDeleted  = IsDeleted  
        From INSERTED where Id=@fmId  

        set @old_Name = Replace(@old_Name,'&','&amp;')
        set @old_Name = Replace(@old_Name,'>','&gt;')  
        set @old_Name = Replace(@old_Name,'<','&lt;')     
        set @old_Name = Replace(@old_Name,'"','&quot;')
        set @old_Name = Replace(@old_Name,'''','&apos;')          

        set @new_Name = Replace(@new_Name,'&','&amp;')      
        set @new_Name = Replace(@new_Name,'>','&gt;')  
        set @new_Name = Replace(@new_Name,'<','&lt;')     
        set @new_Name = Replace(@new_Name,'"','&quot;')
        set @new_Name = Replace(@new_Name,'''','&apos;') 

        set @old_Description = Replace(@old_Description,'&','&amp;')
        set @old_Description = Replace(@old_Description,'>','&gt;')  
        set @old_Description = Replace(@old_Description,'<','&lt;')     
        set @old_Description = Replace(@old_Description,'"','&quot;')
        set @old_Description = Replace(@old_Description,'''','&apos;')          

        set @new_Description = Replace(@new_Description,'&','&amp;')      
        set @new_Description = Replace(@new_Description,'>','&gt;')  
        set @new_Description = Replace(@new_Description,'<','&lt;')     
        set @new_Description = Replace(@new_Description,'"','&quot;')
        set @new_Description = Replace(@new_Description,'''','&apos;')   

        set @xml = ''     

        BEGIN      

        -- for Name  
        If ltrim(rtrim(IsNull(@new_Name,''))) != ltrim(rtrim(IsNull(@old_Name,'')))    
        set @xml = @xml + '<ColumnInfo ColumnName="Name" OldValue="'+ @old_Name + '" NewValue="' + @new_Name + '"/>'    

        -- for Description  
        If ltrim(rtrim(IsNull(@new_Description,''))) != ltrim(rtrim(IsNull(@old_Description,'')))    
        set @xml = @xml + '<ColumnInfo ColumnName="Description" OldValue="'+ @old_Description + '" NewValue="' + @new_Description + '"/>'    

        -- CreatedDate     
        If IsNull(@new_CreatedDate,'') != IsNull(@old_CreatedDate,'')  
        set @xml = @xml + '<ColumnInfo ColumnName="CreatedDate" OldValue="'+ cast(isnull(@old_CreatedDate,'') as varchar(100)) + '" NewValue="' + cast(isnull(@new_CreatedDate,'') as varchar(100)) + '"/>'    

        -- CreatedBy     
        If cast(IsNull(@new_CreatedBy,'00000000-0000-0000-0000-000000000000')as varchar (36)) != cast(IsNull(@old_CreatedBy,'00000000-0000-0000-0000-000000000000')as varchar(36))    
        set @xml = @xml + '<ColumnInfo ColumnName="CreatedBy" OldValue="'+ cast(IsNull(@old_CreatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36)) + '" NewValue="' + cast(isnull(@new_CreatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36))+
        '"/>'    

        -- UpdatedDate       
        If IsNull(@new_UpdatedDate,'') != IsNull(@old_UpdatedDate,'')    
        set @xml = @xml + '<ColumnInfo ColumnName="UpdatedDate" OldValue="'+ cast(IsNull(@old_UpdatedDate,'') as varchar(100)) + '" NewValue="' + cast(IsNull(@new_UpdatedDate,'') as varchar(100)) + '"/>'    

        -- UpdatedBy     
        If cast(IsNull(@new_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36)) != cast(IsNull(@old_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36))    
        set @xml = @xml + '<ColumnInfo ColumnName="UpdatedBy" OldValue="'+ cast(IsNull(@old_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36)) + '" NewValue="' + cast(IsNull(@new_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36))+
        '"/>'    

        -- IsDeleted  
        If cast(IsNull(@new_IsDeleted,'') as varchar(10)) != cast(IsNull(@old_IsDeleted,'') as varchar(10))    
        set @xml = @xml + '<ColumnInfo ColumnName="IsDeleted" OldValue="'+ cast(IsNull(@old_IsDeleted,'') as varchar(10)) + '" NewValue="' + cast(IsNull(@new_IsDeleted,'') as varchar(10)) + '" />'    

        END    

        Set @xml = '<RowInfo TableName="te_Page" UpdatedBy="' + cast(IsNull(@new_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(50)) +  '" UpdatedDate="' + Convert(Varchar(20),GetDate()) + '">' + @xml + '</RowInfo>'    
        Select @ishistoryexists = RowHistory From DELETED     

        --print @ishistoryexists  


        If @ishistoryexists is null    
        Begin     
        Set @xml = '<History>' + @xml + '</History>'      
        Update te_Page    
        Set    
        RowHistory = @xml    
        Where     
        Id = @FormLineAttributeValueId    

        End    

        Else    
        Begin     
        set @xml = REPLACE(@currentxml, '<History>', '<History>' + @xml)  
        Update te_Page  
        Set  
        RowHistory = @xml  
        Where   
        Id = @FormLineAttributeValueId     
        End  


        FETCH NEXT FROM curFormId INTO @fmId  
        END   


        CLOSE curFormId  
        DEALLOCATE curFormId  

更新を実行するたびに、データはrowhistory列に保存されます

于 2012-07-19T11:09:43.990 に答える
1

これが処理されるのを見た1つの方法(正直なところ、お勧めしませんが)は、ストアドプロシージャを介して処理し、userid / username/whateverをパラメーターとして渡すことです。ストアドプロシージャはロギングプロシージャを呼び出し、中央のログテーブルに関連する詳細を書き込みます。

ここで少し風変わりになりましたが...

INSERT / UPDATEの場合、INSERT / UPDATEが正常に完了すると、関連する行がXMLデータとしてテーブルに格納されました。DELETEの場合、行はDELETEが実行される前に保存されていました(ただし、現実的には、少なくともSQL Server 2005ではDELETEステートメントの出力から行を取得できたはずです)。

私の記憶が正しければ、テーブルには、UserID、ログの日時、トランザクションタイプ(I / U / D)、関連する行を含むXMLデータ、テーブル名、および主キー値(主にクイック検索に使用)の2つの列しかありませんでした。彼らが望んでいたレコードの)。

猫の皮を剥ぐ方法はたくさんありますが...

私のアドバイスは、シンプルに保つことです。必要に応じて、後で拡張してください。

そうすることができる場合は、ユーザーをロックダウンして、ストアドプロシージャを介してテーブルに対して実行可能なステートメントのみを実行できるようにし、そこからログを処理します(必要に応じて)。

于 2008-11-18T20:32:41.813 に答える
0

独自に構築し、ユーザーとPCを各追加/更新ストアドプロシージャに渡す必要がありました。次に、元のレコードを取得して変数にデータを入力し、それらを渡された変数と比較して、データをテーブルに記録するだけです。削除の場合、元のテーブルのコピーとタイムスタンプフィールドがあるだけなので、レコードが実際に削除されることはなく、必要なときにいつでも復元できます(明らかに、削除ルーチンはFK関係などをチェックします)。

ログテーブルの追加/更新は、datetime、table_name、column_name、record_id、old_value、new_value、user_id、computerのようになります

nullを挿入することはないため、nullを空の文字列に変換します。新しいエントリは、old_value列に「{newentry}」とマークされます。record_idは、その単一のレコードを一意に識別するための多数のキー列で構成されています(field1+'。'+field2 + ...)

于 2008-11-18T20:36:16.987 に答える
0

DB を変更するユーザーのログ記録について: DB に必要な数の SQL ユーザーを作成できます。セッションを使用し、プログラム/スクリプトへの制限付き/登録済みアクセスを使用する場合は、その情報を使用してさまざまな DB 接続設定 (つまり、ユーザー名) を開始できます。 、DBでの操作の前。

少なくとも PHP 用のスクリプトでは実行できるはずですが、asp.net では間違っている可能性があります。

于 2012-06-27T10:27:38.127 に答える
0

まず、すべてのテーブルで、少なくともこれらの列をデータ列 DateCreated、UserCreated、DateModified、UserModified に追加する必要があります。おそらく、「ステータス」または「LastAction」列を追加して、行を実際に削除せずに、削除済み/挿入済み/更新済みのステータスに設定したい場合があります。次に、最初のテーブルの正確なコピーである「履歴テーブル」を作成できます。次に、すべての更新または削除で、トリガにより削除されたテーブル エントリが履歴テーブルにコピーされ、DateModified、UserModified、および Status フィールドが同時に変更されます。

于 2008-11-19T14:25:02.553 に答える
0

ビューを使用してデータにアクセスし、INSTEAD OF トリガーを使用して挿入、更新、および削除を処理する SQL Server をセットアップしました。

例: ビューのINSTEAD OF DELETEトリガーは、基になるテーブルのレコードを削除済みとしてマークし、ビューは削除されたレコードを表示しないようにフィルター処理されました。

すべてのトリガーで、変更日とユーザー名を更新しました。問題は、最終的なアプリケーション ユーザー名とは異なるデータベース ユーザー名がログに記録されることです。

これが機能するには、ビューがスキーマにバインドされている必要があります。

于 2009-02-12T21:24:01.980 に答える