24

アプリのSQLServerバックエンドで、一連のキーテーブルの履歴テーブルを作成します。これにより、行への変更の履歴が追跡されます。

私のアプリケーション全体はストアドプロシージャを使用しており、埋め込みSQLはありません。これらのテーブルを変更するためのデータベースへの唯一の接続は、アプリケーションとSPインターフェースを介して行われます。従来、私が協力してきたショップは、トリガーを使用してこのタスクを実行していました。

ストアドプロシージャとトリガーのどちらかを選択できる場合、どちらが良いですか?どちらが速いですか?

4

11 に答える 11

41

トリガー。

監査ログ トリガーを簡単に作成/管理できるように、GUI (社内ではRed Matrix Reloadedと呼ばれます) を作成しました。

使用されているもののDDLは次のとおりです。


監査ログ テーブル

CREATE TABLE [AuditLog] (
    [AuditLogID] [int] IDENTITY (1, 1) NOT NULL ,
    [ChangeDate] [datetime] NOT NULL CONSTRAINT [DF_AuditLog_ChangeDate] DEFAULT (getdate()),
    [RowGUID] [uniqueidentifier] NOT NULL ,
    [ChangeType] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
    [TableName] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
    [FieldName] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
    [OldValue] [varchar] (8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
    [NewValue] [varchar] (8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
    [Username] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
    [Hostname] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
    [AppName] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
    [UserGUID] [uniqueidentifier] NULL ,
    [TagGUID] [uniqueidentifier] NULL ,
    [Tag] [varchar] (8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL 
)

挿入をログに記録するトリガー

CREATE TRIGGER LogInsert_Nodes ON dbo.Nodes
FOR INSERT
AS

/* Load the saved context info UserGUID */
DECLARE @SavedUserGUID uniqueidentifier

SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID

DECLARE @NullGUID uniqueidentifier
SELECT @NullGUID = '{00000000-0000-0000-0000-000000000000}'

IF @SavedUserGUID = @NullGUID
BEGIN
    SET @SavedUserGUID = NULL
END

    /*We dont' log individual field changes Old/New because the row is new.
    So we only have one record - INSERTED*/

    INSERT INTO AuditLog(
            ChangeDate, RowGUID, ChangeType, 
            Username, HostName, AppName,
            UserGUID, 
            TableName, FieldName, 
            TagGUID, Tag, 
            OldValue, NewValue)

    SELECT
        getdate(), --ChangeDate
        i.NodeGUID, --RowGUID
        'INSERTED', --ChangeType
        USER_NAME(), HOST_NAME(), APP_NAME(), 
        @SavedUserGUID, --UserGUID
        'Nodes', --TableName
        '', --FieldName
        i.ParentNodeGUID, --TagGUID
        i.Caption, --Tag
        null, --OldValue
        null --NewValue
    FROM Inserted i

更新をログに記録するトリガー

CREATE TRIGGER LogUpdate_Nodes ON dbo.Nodes
FOR UPDATE AS

/* Load the saved context info UserGUID */
DECLARE @SavedUserGUID uniqueidentifier

SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID

DECLARE @NullGUID uniqueidentifier
SELECT @NullGUID = '{00000000-0000-0000-0000-000000000000}'

IF @SavedUserGUID = @NullGUID
BEGIN
    SET @SavedUserGUID = NULL
END

    /* ParentNodeGUID uniqueidentifier */
    IF UPDATE (ParentNodeGUID)
    BEGIN
        INSERT INTO AuditLog(
            ChangeDate, RowGUID, ChangeType, 
            Username, HostName, AppName,
            UserGUID, 
            TableName, FieldName, 
            TagGUID, Tag, 
            OldValue, NewValue)
        SELECT 
            getdate(), --ChangeDate
            i.NodeGUID, --RowGUID
            'UPDATED', --ChangeType
            USER_NAME(), HOST_NAME(), APP_NAME(), 
            @SavedUserGUID, --UserGUID
            'Nodes', --TableName
            'ParentNodeGUID', --FieldName
            i.ParentNodeGUID, --TagGUID
            i.Caption, --Tag
            d.ParentNodeGUID, --OldValue
            i.ParentNodeGUID --NewValue
        FROM Inserted i
            INNER JOIN Deleted d
            ON i.NodeGUID = d.NodeGUID
        WHERE (d.ParentNodeGUID IS NULL AND i.ParentNodeGUID IS NOT NULL)
        OR (d.ParentNodeGUID IS NOT NULL AND i.ParentNodeGUID IS NULL)
        OR (d.ParentNodeGUID <> i.ParentNodeGUID)
    END

    /* Caption varchar(255) */
    IF UPDATE (Caption)
    BEGIN
        INSERT INTO AuditLog(
            ChangeDate, RowGUID, ChangeType, 
            Username, HostName, AppName,
            UserGUID, 
            TableName, FieldName, 
            TagGUID, Tag, 
            OldValue, NewValue)
        SELECT 
            getdate(), --ChangeDate
            i.NodeGUID, --RowGUID
            'UPDATED', --ChangeType
            USER_NAME(), HOST_NAME(), APP_NAME(), 
            @SavedUserGUID, --UserGUID
            'Nodes', --TableName
            'Caption', --FieldName
            i.ParentNodeGUID, --TagGUID
            i.Caption, --Tag
            d.Caption, --OldValue
            i.Caption --NewValue
        FROM Inserted i
            INNER JOIN Deleted d
            ON i.NodeGUID = d.NodeGUID
        WHERE (d.Caption IS NULL AND i.Caption IS NOT NULL)
        OR (d.Caption IS NOT NULL AND i.Caption IS NULL)
        OR (d.Caption <> i.Caption)
    END

...

/* ImageGUID uniqueidentifier */
IF UPDATE (ImageGUID)
BEGIN
    INSERT INTO AuditLog(
        ChangeDate, RowGUID, ChangeType, 
        Username, HostName, AppName,
        UserGUID, 
        TableName, FieldName, 
        TagGUID, Tag, 
        OldValue, NewValue)
    SELECT 
        getdate(), --ChangeDate
        i.NodeGUID, --RowGUID
        'UPDATED', --ChangeType
        USER_NAME(), HOST_NAME(), APP_NAME(), 
        @SavedUserGUID, --UserGUID
        'Nodes', --TableName
        'ImageGUID', --FieldName
        i.ParentNodeGUID, --TagGUID
        i.Caption, --Tag
        (SELECT Caption FROM Nodes WHERE NodeGUID = d.ImageGUID), --OldValue
        (SELECT Caption FROM Nodes WHERE NodeGUID = i.ImageGUID) --New Value
    FROM Inserted i
        INNER JOIN Deleted d
        ON i.NodeGUID = d.NodeGUID
    WHERE (d.ImageGUID IS NULL AND i.ImageGUID IS NOT NULL)
    OR (d.ImageGUID IS NOT NULL AND i.ImageGUID IS NULL)
    OR (d.ImageGUID <> i.ImageGUID)
END

ログを記録するトリガー 削除

CREATE TRIGGER LogDelete_Nodes ON dbo.Nodes
FOR DELETE
AS

/* Load the saved context info UserGUID */
DECLARE @SavedUserGUID uniqueidentifier

SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID

DECLARE @NullGUID uniqueidentifier
SELECT @NullGUID = '{00000000-0000-0000-0000-000000000000}'

IF @SavedUserGUID = @NullGUID
BEGIN
    SET @SavedUserGUID = NULL
END

    /*We dont' log individual field changes Old/New because the row is new.
    So we only have one record - DELETED*/

    INSERT INTO AuditLog(
            ChangeDate, RowGUID, ChangeType, 
            Username, HostName, AppName,
            UserGUID, 
            TableName, FieldName, 
            TagGUID, Tag, 
            OldValue,NewValue)

    SELECT
        getdate(), --ChangeDate
        d.NodeGUID, --RowGUID
        'DELETED', --ChangeType
        USER_NAME(), HOST_NAME(), APP_NAME(), 
        @SavedUserGUID, --UserGUID
        'Nodes', --TableName
        '', --FieldName
        d.ParentNodeGUID, --TagGUID
        d.Caption, --Tag
        null, --OldValue
        null --NewValue
    FROM Deleted d

また、ソフトウェアのどのユーザーが更新を行ったかを知るために、ストアド プロシージャを呼び出して、すべての接続が "SQL Server にログオン" します。

CREATE PROCEDURE dbo.SaveContextUserGUID @UserGUID uniqueidentifier AS

/* Saves the given UserGUID as the session's "Context Information" */
IF @UserGUID IS NULL
BEGIN
    PRINT 'Emptying CONTEXT_INFO because of null @UserGUID'
    DECLARE @BinVar varbinary(128)
    SET @BinVar = CAST( REPLICATE( 0x00, 128 ) AS varbinary(128) )
    SET CONTEXT_INFO @BinVar
    RETURN 0
END

DECLARE @UserGUIDBinary binary(16) --a guid is 16 bytes
SELECT @UserGUIDBinary = CAST(@UserGUID as binary(16))
SET CONTEXT_INFO @UserGUIDBinary


/* To load the guid back 
DECLARE @SavedUserGUID uniqueidentifier

SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID

select @SavedUserGUID AS UserGUID
*/

ノート

  • Stackoverflow コード形式ではほとんどの空白行が削除されるため、書式設定がうまくいきません
  • 統合セキュリティではなく、ユーザーのテーブルを使用します
  • このコードは利便性のために提供されています。デザインの選択に対する批判は許可されていません。純粋主義者は、すべてのロギング コードはビジネス レイヤーで実行する必要があると主張する場合があります。
  • SQL Server のトリガーを使用して BLOB をログに記録することはできません (BLOB の "前" のバージョンはありません。あるものだけがあります)。Text と nText は blob です。これにより、メモがログに記録されなくなるか、varchar(2000) になります。
  • タグ列は、行を識別するための任意のテキストとして使用されます (たとえば、顧客が削除された場合、タグは監査ログ テーブルに「General Motors North America」と表示されます。
  • TagGUID は、行の「親」を指すために使用されます。たとえば、InvoiceLineItems のログInvoiceHeaderを指します。このようにして、特定の請求書に関連する監査ログ エントリを検索する人は、監査証跡の明細項目の TagGUID によって、削除された「明細項目」を見つけることができます。
  • 意味のある文字列を取得するために、「OldValue」と「NewValue」の値が副選択として書き込まれることがあります。つまり」

    OldValue: {233d-ad34234..} NewValue: {883-sdf34...}

監査証跡では、次のものよりも役に立ちません。

OldValue: Daimler Chrysler
NewValue: Cerberus Capital Management

最後の注意: 私たちがしていることを気にしないでください。これは私たちにとっては素晴らしいことですが、他の誰もが自由に使用しないことができます。

于 2008-12-12T18:44:59.797 に答える
17

SQL Server 2008では、MSDNのCDC(Change data Capture)CDCと呼ばれる新機能が役立ちます。CDCは、トリガーやその他のメカニズムを書き込まずに、テーブルデータへの変更を別のテーブルに記録する機能です。変更データキャプチャは、SQLサーバーのテーブルへの挿入、更新、削除などの変更を記録するため、変更の詳細をリレーショナルで利用できるようにします。フォーマット。

Channel9ビデオ

于 2008-12-08T13:26:05.710 に答える
15

トリガーを生成するために使用したサードパーティ製ツールApexSQL Auditがあります。

バックグラウンドでトリガーがどのように表示され、データがどのように保存されるかを次に示します。うまくいけば、プロセスをリバースエンジニアリングするのに十分なほど便利です。各列を個別に監査できるため、Ian Boyd が例で示したものとは少し異なります。

表 1 – トランザクションの詳細 (誰が、いつ、アプリケーション、ホスト名など) を保持します。

CREATE TABLE [dbo].[AUDIT_LOG_TRANSACTIONS](
    [AUDIT_LOG_TRANSACTION_ID] [int] IDENTITY(1,1) NOT NULL,
    [DATABASE] [nvarchar](128) NOT NULL,
    [TABLE_NAME] [nvarchar](261) NOT NULL,
    [TABLE_SCHEMA] [nvarchar](261) NOT NULL,
    [AUDIT_ACTION_ID] [tinyint] NOT NULL,
    [HOST_NAME] [varchar](128) NOT NULL,
    [APP_NAME] [varchar](128) NOT NULL,
    [MODIFIED_BY] [varchar](128) NOT NULL,
    [MODIFIED_DATE] [datetime] NOT NULL,
    [AFFECTED_ROWS] [int] NOT NULL,
    [SYSOBJ_ID]  AS (object_id([TABLE_NAME])),
  PRIMARY KEY CLUSTERED 
  (
       [AUDIT_LOG_TRANSACTION_ID] ASC
  )
)

表 2 – 前後の値を保持します。

CREATE TABLE [dbo].[AUDIT_LOG_DATA](
   [AUDIT_LOG_DATA_ID] [int] IDENTITY(1,1) NOT NULL,
   [AUDIT_LOG_TRANSACTION_ID] [int] NOT NULL,
   [PRIMARY_KEY_DATA] [nvarchar](1500) NOT NULL,
   [COL_NAME] [nvarchar](128) NOT NULL,
   [OLD_VALUE_LONG] [ntext] NULL,
   [NEW_VALUE_LONG] [ntext] NULL,
   [NEW_VALUE_BLOB] [image] NULL,
   [NEW_VALUE]  AS (isnull(CONVERT([varchar](8000),      [NEW_VALUE_LONG],0),CONVERT([varchar](8000),CONVERT([varbinary](8000),substring([NEW_VALUE_BLOB],(1),(8000)),0),0))),
   [OLD_VALUE]  AS (CONVERT([varchar](8000),[OLD_VALUE_LONG],0)),
   [PRIMARY_KEY]  AS ([PRIMARY_KEY_DATA]),
   [DATA_TYPE] [char](1) NOT NULL,
   [KEY1] [nvarchar](500) NULL,
   [KEY2] [nvarchar](500) NULL,
   [KEY3] [nvarchar](500) NULL,
   [KEY4] [nvarchar](500) NULL,
PRIMARY KEY CLUSTERED 
 (
    [AUDIT_LOG_DATA_ID] ASC
)
)

トリガーを挿入

更新のトリガーは非常に長く、これと同じロジックを持っているため、表示していません。

CREATE TRIGGER [dbo].[tr_i_AUDIT_Audited_Table]
ON [dbo].[Audited_Table]
FOR INSERT
NOT FOR REPLICATION
As
BEGIN
DECLARE 
    @IDENTITY_SAVE              varchar(50),
    @AUDIT_LOG_TRANSACTION_ID       Int,
    @PRIM_KEY               nvarchar(4000),
    @ROWS_COUNT             int

SET NOCOUNT ON
Select @ROWS_COUNT=count(*) from inserted
Set @IDENTITY_SAVE = CAST(IsNull(@@IDENTITY,1) AS varchar(50))

INSERT
INTO dbo.AUDIT_LOG_TRANSACTIONS
(
    TABLE_NAME,
    TABLE_SCHEMA,
    AUDIT_ACTION_ID,
    HOST_NAME,
    APP_NAME,
    MODIFIED_BY,
    MODIFIED_DATE,
    AFFECTED_ROWS,
    [DATABASE]
)
values(
    'Audited_Table',
    'dbo',
    2,  --  ACTION ID For INSERT
    CASE 
      WHEN LEN(HOST_NAME()) < 1 THEN ' '
      ELSE HOST_NAME()
    END,
    CASE 
      WHEN LEN(APP_NAME()) < 1 THEN ' '
      ELSE APP_NAME()
    END,
    SUSER_SNAME(),
    GETDATE(),
    @ROWS_COUNT,
    'Database_Name'
)

Set @AUDIT_LOG_TRANSACTION_ID = SCOPE_IDENTITY()    

--This INSERT INTO code is repeated for each columns that is audited. 
--Below are examples for only two columns
INSERT INTO dbo.AUDIT_LOG_DATA
(
    AUDIT_LOG_TRANSACTION_ID,
    PRIMARY_KEY_DATA,
    COL_NAME,
    NEW_VALUE_LONG,
    DATA_TYPE
    , KEY1
)
SELECT
    @AUDIT_LOG_TRANSACTION_ID,
    convert(nvarchar(1500), IsNull('[PK_Column]='+CONVERT(nvarchar(4000), NEW.[PK_Column], 0), '[PK_Column] Is Null')),
    'Column1',
    CONVERT(nvarchar(4000), NEW.[Column1], 0),
    'A'
    , CONVERT(nvarchar(500), CONVERT(nvarchar(4000), NEW.[PK_Column], 0))
FROM inserted NEW
WHERE NEW.[Column1] Is Not Null

 --value is inserted for each column that is selected for auditin
INSERT INTO dbo.AUDIT_LOG_DATA
(
    AUDIT_LOG_TRANSACTION_ID,
    PRIMARY_KEY_DATA,
    COL_NAME,
    NEW_VALUE_LONG,
    DATA_TYPE
    , KEY1
)
SELECT
    @AUDIT_LOG_TRANSACTION_ID,
    convert(nvarchar(1500), IsNull('[PK_Column]='+CONVERT(nvarchar(4000), NEW.[PK_Column], 0), '[PK_Column] Is Null')),
    'Column2',
    CONVERT(nvarchar(4000), NEW.[Column2], 0),
    'A'
    , CONVERT(nvarchar(500), CONVERT(nvarchar(4000), NEW.[PK_Column], 0))
    FROM inserted NEW
    WHERE NEW.[Column2] Is Not Null
End

免責事項:私は Apex とはまったく関係がありませんが、現在の仕事で Apex のツールを使用しています。

于 2013-08-30T09:43:03.720 に答える
4

他のみんなが言ったように、トリガー。単体テストが簡単で、ランダムなクエリを実行するテーブルに直接予期せずアクセスするパワーユーザーに対してはるかに回復力があります。

速いのは?データベース内で何が高速であるかを判断することは、変数の数が多い場合の難しい問題です。「両方の方法で試して比較する」以外では、どちらの方法が速いかについての有用な答えは得られません。変数には、関連するテーブルのサイズ、更新の通常のパターン、サーバー内のディスクの速度、メモリの量、キャッシュ専用のメモリの量などが含まれます。このリストは無限であり、各変数はトリガーするかどうかに影響しますSP内のカスタムSQLよりも高速です。

良い。速い。安いです。2つ選んでください。トリガーは、整合性の点では優れており、メンテナンスの点ではおそらく安価です。おそらく、それらは、一度機能すると、それで終わりになるという点でも高速です。SPはメンテナンスの問題であり、メンテナンスにスタッフをプッシュするのは高速ですが、決して良いことでも安いことでもありません。

幸運を。

于 2008-12-08T14:31:21.007 に答える
3

推奨されるアプローチは、要件によって異なります。履歴テーブルに監査証跡がある場合は、各操作をキャプチャする必要があります。履歴テーブルがパフォーマンス上の理由のみである場合は、スケジュールされたSQLエージェントのデータ転送ジョブで十分です。

各操作をキャプチャするには、AFTERTRIGGERまたはChangeDataCaptureのいずれかを使用します。

Afterトリガーは、トリガー内で操作する2つの一時テーブルを提供します。

  • INSERTまたはUPDATEの後にINSERTED
  • DELETEの後にDELETED

これらの一時テーブルから履歴テーブルへの挿入を実行でき、履歴テーブルは常に最新の状態になります。履歴テーブルにバージョン番号、タイムスタンプ、またはその両方を追加して、変更を1つのソース行に分けることができます。

Change Data Capture(CDC)は、データウェアハウス(または履歴テーブル)にデータをロードするためのソースとして使用できるデルタテーブルを作成するために設計されています。トリガーとは異なり、CDCは非同期であり、宛先(sprocs、SSIS)にデータを入力するために任意の方法とスケジューリングを使用できます。

CDCを使用すると、元のデータと変更の両方にアクセスできます。変更追跡(CT)は、変更された行のみを検出します。CDCでは完全な監査証跡を作成できますが、CTでは作成できません。CDCとCTはどちらも、MSSQL2008EnterpriseエディションとDeveloperエディションでのみ使用できます。

于 2008-12-12T17:27:21.217 に答える
2

これにはトリガーを使用します。これは、ソースに関係なく、変更が履歴テーブルに反映されることを意味します。これはセキュリティに役立ち、履歴テーブルを更新するためのコードを追加するのを忘れる人などの障害モードに対して回復力があります。

実行時間はI/Oによって支配されるため、このタイプの操作でも特に速度の違いはありません。

于 2008-12-08T13:19:28.203 に答える
2

非常に注意する必要がある 1 つの問題は、このテーブルの意図したユース ケースを特定し、その目的のために適切に構築されていることを確認することです。

具体的には、利害関係者の運用監査証跡の場合、それはテーブルのレコード変更の前後のスナップショットとはまったく異なります。(実際、デバッグ以外にレコード変更の有効な用途を想像するのは難しいです。)

通常、監査証跡には、少なくともユーザー ID、タイムスタンプ、操作コード、およびおそらく操作に関する詳細が必要です。例 - 発注書の品目の注文数量を変更します。

また、このタイプの監査証跡では、トリガーを使用したくありません。これらのイベントの生成を埋め込む BR レイヤーの上位にあるほど、より優れたものになります。

OTOH、レコードレベルの変更の場合、トリガーが適切に一致します。しかし、多くの場合、dbms ジャーナリング ファイルからこれを取得する方が簡単です。

于 2008-12-12T17:39:35.873 に答える
1

トリガーは、特定のストアド プロシージャによって呼び出される更新、挿入、および削除だけでなく、すべての更新、挿入、および削除をキャプチャできるため、監査テーブルにトリガーを使用することを好みます。

CREATE TRIGGER [dbo].[tr_Employee_rev]
ON [dbo].[Employee]
AFTER UPDATE, INSERT, DELETE
AS
BEGIN
    IF EXISTS(SELECT * FROM INSERTED) AND EXISTS (SELECT * FROM DELETED)
    BEGIN
        INSERT INTO [EmployeeRev](EmployeeID,Firstname,Initial,Surname,Birthdate,operation, updated, updatedby) SELECT inserted.ID, inserted.Firstname,inserted.Initial,inserted.Surname,inserted.Birthdate,'u', GetDate(), SYSTEM_USER FROM INSERTED
    END 

    IF EXISTS (SELECT * FROM INSERTED) AND NOT EXISTS(SELECT * FROM DELETED)
    BEGIN
        INSERT INTO [EmployeeRev](EmployeeID,Firstname,Initial,Surname,Birthdate,operation, updated, updatedby) SELECT inserted.ID, inserted.Firstname,inserted.Initial,inserted.Surname,inserted.Birthdate,'i', GetDate(), SYSTEM_USER FROM INSERTED
    END

    IF EXISTS(SELECT * FROM DELETED) AND NOT EXISTS(SELECT * FROM INSERTED)
    BEGIN
        INSERT INTO [EmployeeRev](EmployeeID,Firstname,Initial,Surname,Birthdate,operation, updated, updatedby) SELECT deleted.ID, deleted.Firstname,deleted.Initial,deleted.Surname,deleted.Birthdate,'d', GetDate(), SYSTEM_USER FROM DELETED 
    END
END

リビジョン テーブルの SQL を手作業でコーディングする代わりに、SQLServer を使用して生成します。このコードはhttps://github.com/newdigate/sqlserver-revision-tablesで入手できます

于 2015-09-05T13:15:40.900 に答える
0

トリガー。今のところ、データを更新する唯一の方法はSPを使用することであると言えるかもしれませんが、状況が変わる可能性があるか、SPの使用が面倒になる大量の挿入/更新を行う必要がある場合があります。トリガーを使用します。

于 2008-12-08T13:25:08.503 に答える
0

トリガー。これが私のアプローチです:

  1. 監査試行が必要な重要なテーブルごとに 1 つの監査テーブルを作成します。
  2. 監査テーブルには、ソース テーブルのすべての列と、誰、いつ、アクションなどの監査レコード情報の列が含まれます。
  3. UPDATE および DELETE のみのトリガー。INSERT 操作では、元のレコードがソース テーブル自体に含まれます。
  4. 更新または削除する前に、元のレコード + 監査情報を監査テーブルにコピーします
  5. (オプション - UPDATE のみ:) どの列が更新されたかを知るには、SQL 関数に組み込まれている UPDATE(ColumnName) または COLUMNS_UPDATED() を使用して、影響を受ける列を特定します。

この方法で監査を行うと、現在のステータスがソース テーブルに保持され、すべての履歴が監査テーブルに保持され、キー列によって簡単に識別されます。

于 2010-12-02T15:47:28.383 に答える
0

アプリケーションの性質とテーブル構造、インデックスの数、データ サイズなど、外部キーなどによって異なります。データが制限された比較的単純なテーブル (datetime/integer 列のインデックスのようなインデックスがない、またはほとんどない) の場合セット (< 100 万行) の場合、おそらくトリガーを使用しても問題ありません。

トリガーがロックの問題の原因になる可能性があることに注意してください。監査証跡の一種として履歴テーブルを使用している場合は、後で参照できるようにインデックスを作成することになると思います。インデックスが原因で挿入/更新/削除が遅い履歴テーブルをトリガーが更新する場合、プロシージャー呼び出しはトリガーが終了するまでブロックされます。また、トリガーで更新される外部キー制約がある場合、これもパフォーマンスを妨げる可能性があります。

この場合、それはすべてテーブルのインデックスに依存します。1 日あたり 10 万件を超える金融取引を処理する 24 時間年中無休のアプリに Sql Server 2000 を使用しています。最大のメイン テーブルには、1 億を超える行と 15 のインデックスがあります (アップタイムが必要な場合、大量の削除は合理的に不可能です)。すべての SQL はストアド プロシージャで実行されますが、パフォーマンスが低下するため、トリガーや外部キーは使用しません。

于 2008-12-12T18:39:36.453 に答える