2011-02-23 57 views
2

在我們的業務規則中,我們需要跟蹤指定哪一行被更改。該表包含根據我們的業務目的指定爲不相關的多個列(例如輸入日期字段,時間戳,審查位字段或接收位字段)。該表有很多列,我試圖找到一個優雅的方式來確定是否有任何相關字段已更改,然後在審計表中記錄條目(輸入行的PK值 - 無法編輯PK)。我甚至不需要知道哪一列實際發生了變化(儘管在這條路上會很好)。將行標識爲排除了某些列中的更改

我能夠通過一個存儲過程來完成它,但它是使用以下語法的更新一個醜陋的SP(OR陳述大大縮短後):

INSERT INTO [TblSourceDataChange] (pkValue) 
    SELECT d.pkValue 
    FROM deleted d INNER JOIN inserted i ON d.pkValue=i.pkValue 
    WHERE ( i.[F440] <> d.[F440] 
      OR i.[F445] <> d.[F445] 
      OR i.[F450] <> d.[F450]) 

我試圖找到一個通用的方式,我可以指定忽略字段和存儲過程仍然可以工作,即使我添加了額外的相關字段到表中。不相關的領域不經常改變,而相關的領域往往更具動態性。

+0

我已經添加了一個新的答案,這是相關的老問題:http://stackoverflow.com/questions/1254787/sql-server-update-trigger-get-only-modified-fields/8020461#8020461 – 2011-11-05 13:36:56

回答

1

看看Change Data Capture。這是在SQL Server 2008中

首先一個新的功能,您的數據庫上啓用CDC:

EXEC sys.sp_cdc_enable_db

然後你就可以在特定的表啓用它,並且指定要跟蹤的列:

EXEC sys.sp_cdc_enable_table 
    @source_schema = 'dbo', 
    @source_name = 'xxx', 
    @supports_net_changes = 1, 
    @role_name = NULL, 
    @captured_column_list = N'xxx1,xxx2,xxx3' 

這將創建一個名爲cdc.dbo_xxx的更改表。表中對記錄所做的任何更改都記錄在該表中。

+0

這可能是一個解決方案,但我真的想用一種方法來指定哪些列不能跟蹤(如果可能)。 – 2011-02-23 20:28:25

+0

考慮二進制校驗和或校驗和聚合(但不真正熟悉它們,如果他們會工作) – 2011-02-23 20:29:58

+0

你可以看看更改跟蹤。這就像Change Data Capture Lite一樣。您不必指定任何列來跟蹤(我認爲),但您獲得的更改信息較少(特定刪除)。請參閱:http://msdn.microsoft.com/en-us/library/cc280462.aspx – TGnat 2011-02-23 20:40:13

0

使用觸發器並確保它可以處理多行插入。

1

我反對!我不能用來描述可用選項的一個詞是優雅的。我還沒有找到一個令人滿意的方式來實現你想要的。有選擇,但他們都覺得有點不滿意。何時/爲何選擇這些選項取決於您未提及的一些因素。

  • 您需要多長時間「問」哪些字段發生了變化?意思是,用戶很少點擊「審覈歷史」鏈接?或者,這是一直在理清你的應用應該如何表現?
  • 磁盤空間花費多少錢?我並沒有太過輕率,但是我曾經根據我們對san space收取的存儲策略開展了百萬美元的存儲策略 - 這意味着SQL server重新構建成本並不是一個考慮因素,存儲大小是。你可能是相同的或相反的。

變化數據捕捉

正如@TGnat提到你可以使用CDC。這種方法非常棒,因爲您只需啓用更改跟蹤,然後調用sproc即可開始跟蹤。 CDC很好,因爲它非常有效的存儲和馬力明智。你也可以設置它並忘記它 - 也就是說,在開發人員出現並想要改變表格形狀之前。爲了開發人員的理智,您需要生成一個禁用/啓用實體跟蹤的腳本。

我注意到你想排除某些列,而不是包括它們。您可以使用FOR XML PATH技巧完成此操作。你可以編寫一個查詢類似以下內容,然後調用sys.sp_cdc_enable_table時使用@capturedColList變量..

SET @capturedColList = SELECT Substring((
       SELECT ',' + COLUMN_Name 
       FROM INFORMATION_SCHEMA.COLUMNS 
       WHERE TABLE_NAME = '<YOUR_TABLE>' AND 
         COLUMN_NAME NOT IN ('excludedA', 'excludedB') 

       FOR XML PATH('') 
      ) , 2, 8000) 

觸發瓦特/案例 我看到的第二個選擇是有某種代碼生成的。它可能是一個外部線束或寫入觸發器的SPROC。無論你的毒藥是什麼,它都需要自動化和通用化。但是,基本上,編寫DDL的觸發器會使用每個列的unweildy CASE語句來比較當前到INSERTED或DELETED的代碼。

有一種風格here的討論。

日誌一切,排序出來後

的最後一個選項是使用觸發器來記錄每一行的變化。然後,編寫代碼(SPROCS/UDF),可以查看日誌數據並識別何時發生更改。你爲什麼選擇這個選項?磁盤空間不是問題,雖然你需要能夠理解改變了什麼,但你很少問系統這個問題。

HTH,

-Eric

+0

感謝您的信息!我的問題令人困惑的是,看起來好像我正在審覈更改 - 我不是(磁盤空間不是問題)。我們的系統計算納稅申報表,並將表格標識爲源數據 - 任何值的更改都會影響稅務計算。我們的系統查看TblSourceDataChange表(它包含未列出的其他字段)並用改變的源數據標識計算。由於計算非常複雜,我們不需要列級別,我們只需要「可能」更改的結果。我們隨後每年都使用相同的表結構。 – 2011-02-24 12:53:38

+0

@user <使用奇怪的數字> - 雖然您的用例與上面提到的不同,但它聽起來像「稍後記錄並整理」是合適的。雖然,不清楚你的變化頻率是多少(每年一次)。我沒有提到如何「稍後整理」,但是您可以通過子句有效地做出一些聰明的組,這樣您的查詢就會爲每次更改生成一個獨特的行。您需要自動生成這些查詢。 HTH,-eric – EBarr 2011-02-24 15:53:59

0

我發現在後SQL Server Update, Get only modified fields的答案,適應了SQL適合我的需要(這條SQL是一個觸發器)。這些SQL下面貼:

DECLARE @idTable INT SELECT @idTable = T.id FROM系統對象P JOIN系統對象T ON P.parent_obj = T.id WHERE P.id = @@ PROCID

如果存在,則返回0,並且name NOT IN('timestamp());如果存在,則返回一個值爲0的數組, ','''') ) BEGIN - 在這裏做合適的東西 END

+0

參考:儘管COLUMNS_UPDATED()函數似乎表明列已更改,但並非總是如此。這意味着一列被「觸動」,但它可能是:更新myTable SET myField = MyField。再說一遍,這可能是一個很好的開始,所以你不需要匹配插入和刪除,如果沒有任何列被觸摸。 PS:ColumnsUpdated比使用UPDATE()檢查每個列更快! – deroby 2011-05-23 12:43:12

相關問題