2016-08-23 5 views
1

私は、トリガを使用してsalesファクトを持つテーブルの変更ログテーブルを更新しています。誰かがテーブルを更新しようとすると、説明を書いてから更新が記録されます(ユーザー、時間、テーブル、フィールド、Table_Unique_ID、oldvalue、newvalue)UPDATE()T-SQLトリガ関数のローカル変数

私はUPDATE()ローカル変数@fieldnameのトリガー関数。目標は、各列をチェックするために循環するのではなく、更新された列の削除された/挿入された値のみを比較することです。私は、明示的に列名を指定するときにUPDATE()関数を使用することができましたが、チェックしたい列名に変数を設定した場合は使用できませんでした。

本当にこのプロセスを最適化する方法についてのアドバイスをいただきありがとうございます。私はまだ多くのことを学ぶ必要があります。これが私の最初の試みです。以下は

は、全体が私のトリガーである:カラムをリスト

ALTER TRIGGER [dbo].[TR_UpdateLog] 

ON [dbo].[Test_Update_Trigger] 
    AFTER Update 
AS 

Declare @description nvarchar(1000) 
, @UserName nvarchar(128) 
, @oldValue nvarchar(255) 
, @newValue nvarchar(255) 
, @UniqueID nvarchar(255) 
, @fieldname nvarchar(128) 
, @oname NVARCHAR(100) 
, @OldSQL nvarchar(max) 
, @NewSQL nvarchar(max) 
, @i int 
, @c int 
, @numcolumns int 
, @numrows int; 
DECLARE @updated_table TABLE ( 
idx int Primary Key identity(1,1) 
, uniqueID nvarchar(255) NULL) 
; 


--Require the User to submit a description for the update 
Set @description = (SELECT * FROM [dbo].[Fact_Bookings_Audit_Description]) 
TRUNCATE TABLE [dbo].[Fact_Bookings_Audit_Description] 

IF @description is null 
BEGIN 
    PRINT 'You must provide a description. Use the following text: 

    TRUNCATE TABLE [dbo].[Fact_Bookings_Audit_Description] 
    INSERT INTO [dbo].[Fact_Bookings_Audit_Description] 
    SELECT "Your Text Here--use Single Quotes" 

    Copy and paste above your query, type your description, and run the update again. 
    '; 
    ROLLBACK TRANSACTION; 
END 

--Set User and Table name 
Set @Username = system_User 
SET @oname = (SELECT OBJECT_NAME(parent_id) from sys.triggers where object_ID = @@PROCID) 


------------------------------------------------------------- 
---create tables of updated items to reference for old/new values 
INSERT INTO @updated_table 
SELECT distinct Table_ID from DELETED 

SELECT * INTO dbo.tempDelete FROM DEleted 
SELECT * INTO dbo.tempInsert FROM inserted 
-------------------------------------------------- 
--Set variables for loop through updated items 
Set @i = 1 
Set @numrows = (SELECT count(*) from @updated_table) 
------------------------------------------------------------ 
---Set number of columns for loop through each field 
Set @numcolumns = (SELECT MAx(ordinal_position) FROM information_schema.columns where table_name = @oname) 

--------------------------------------------------------------- 
--If there was an update 
IF @numrows > 0 
BEGIN 
    --loop through each individual updated row 
    WHILE (@i <= (select max(idx) from @updated_table)) 
    BEGIN 
     --reset the column variable 
     Set @c = 1 
     --loop through each column 
     WHILE (@c <= @numcolumns) 
     BEGIN 
       Set @fieldname = (SELECT Column_name FROM information_schema.columns 
             Where table_name = @oname AND ORDINAL_POSITION = @c) 

        /**This is what I want to use-- 
        I would only like to do the comparison on columns which were updated, not cycle through every column**/ 

        --IF UPDATE(@fieldname) 
        --BEGIN 

        --set values for old and new values as well as unique ID for log table 
        Set @UniqueID = (SELECT uniqueID from @updated_table u WHERE idx = @i) 
        Set @OldSQL = 'SELECT @oldvalue = ' + @fieldname + ' from dbo.tempdelete d WHERE d.Table_ID = ' + @UniqueID; 
        Set @NewSQL = 'SELECT @newvalue = ' + @fieldname + ' from dbo.tempInsert i WHERE i.Table_ID = ' + @UniqueID; 

        EXEC sp_executesql @oldSQL, N'@oldvalue nvarchar(128) output', @oldValue = @oldValue output 
        EXEC sp_executesql @newSQL, N'@newvalue nvarchar(128) output', @newValue = @newValue output 

        ; 
         --Insert into log table if value is changed 
         IF isnull(@oldvalue,0) <> isnull(@newvalue,0) 
         BEGIN 
          INSERT INTO dbo.Fact_Bookings_ChangeAudit (Change_Date, Change_Time, [User], Table_Name, Field, Table_ID, Oldvalue, Newvalue, [Description]) 
          VALUES(cast(datediff(DAY,0,getdate())as datetime),cast(getdate() as datetime),@UserName, @oname, @fieldname, @UniqueID, @oldValue, @newValue, @description); 
         END 
        --END 
       --next column 
       Set @c = @c + 1 
     END 
    --next record 
    Set @i = @i + 1 
    END 

END 
DROP TABLE dbo.tempDelete 
DROP table dbo.tempInsert 

GO 
+0

私はそこに多くのことが悪い考えだと思います。 mysqlタグは必要ありません。私はそれがあなたの最初の質問だと知っていますが、コメントするのはおそらく多すぎます。 – shawnt00

+1

私の意見では、この質問の種類は、2つのスタックエクスチェンジサイト、この1つとコードレビューに跨っています。あなたは最適化を探しているので、そこに投稿したいかもしれません。 http://codereview.stackexchange.com/ – scsimon

+0

ありがとう、@ shawnt00。私はMysqlタグを削除しました。今でもまだ学んでいます。変更の追跡に関する良い記事があれば、それを感謝します! –

答えて

1
with i as (
    select 
     case when updated(colA) 
      then coalesce(cast(ColA as varchar(32)), 'null') end as newColA, 
     case when updated(colB) 
      then coalesce(cast(ColB as varchar(32)), 'null') end as newColB, 
     ... 
    from inserted 
), d as (
    select 
     case when updated(colA) 
      then coalesce(cast(ColA as varchar(32)), 'null') end as oldColA, 
     case when updated(colB) 
      then coalesce(cast(ColB as varchar(32)), 'null') end as oldColB, 
     ... 
    from deleted 
), old as (
    select c.Id, Col, Change 
    from i unpivot (Change for Col in (newColA, newColB, ...)) 
    where Change is not null 
), new as (
    select c.Id, Col, Change 
    from d unpivot (Change for Col in (oldColA, oldColB, ...)) 
    where Change is not null 
) 
-- insert into Log 
select ... 
from 
    old o inner join new n on n.Id = o.Id and n.Col = o.Col 

はこれを行うには正しい方法です。私はあなたが情報スキーマテーブルにループを使いたいと思っていることを知っています。私はすべての動的SQLが良いアイデアだとは思わない。

上記のクエリは、ログに記録する行のリストを作成するための開始点と考えることができます。私はそれを試してみませんでした。一括更新をしようとすると時間がかかることがあります。

これは、結合を使用するのではなく、一括更新で高速になる場合がありますか?

with ... 
, new as (select 'i' as src ...), 
, old as (select 'd' as src ...), 
, combined as (select * from new union all select * from old) 
select 
    Id, Col 
    min(case when src = 'i' then Change end) as newVal, 
    min(case when src = 'd' then Change end) as oldVal, 
from combined 
group by Id, Col; 
+0

これは別の戦略を試してみるのに最適な出発点です!私はそれがどのように進むのかを知らせます。私はフィードバックに感謝します。ちょうど興味深い、あなたは動的SQLに見える潜在的な短所は何ですか? –

+0

ダイナミックSQLには特にセキュリティ上の欠点が多く、トリガーが十分に複雑になる可能性があります。あなたが周りを検索する場合、私はあなたがこの種のことをするときにいくつかのバグと奇妙な行動を見つけるだろうと思う。私の頭の上から私は名前を付けることはできません。しかし、ほとんどの場合、簡単な更新の途中でこれらの余分なクエリがすべて実行されると、パフォーマンスが大きな問題になると思います。 – shawnt00

+0

多くのテーブルの履歴が必要なプロジェクトでは、データディクショナリをクエリしてトリガをプログラムで作成するか、場合によっては変更して、型を減らすという良い結果が得られました。 –

関連する問題