2017-06-22 118 views
2

我有一張桌子,插入到每個夜晚都是數據的快照。在任何時候,數據可能在列中發生變化(AccountNo保持不變,RunKey增加1,RunDate增加1天;所有其他列可以改變特別)。下面是數據的外觀的例子:如何在SQL Server 2014中的數據發生更改時提取列名稱?

|AccountNo | RunKey | RunDate |  Address  | Salary | PromotionDate| 
---------------------------------------------------------------------------- 
| 12345 | 2 | 06/20/2017 | 123 Main Street | 60,000 | 01/15/2017 | 
| 12345 | 3 | 06/21/2017 | 123 Main Street | 60,000 | 01/15/2017 | 
| 12345 | 4 | 06/22/2017 | 123 Main Street | 65,000 | 06/21/2017 | 

使用LAG函數和CASE表達式我能確定何時有變化(1意味着它改變標誌):

|AccountNo | RunKey | RunDate |  Address  | AddressLAG |AddressFlag| Salary | SalaryLAG |SalaryFlag| PromotionDate|PromotionDateLag|PromotionFlag| 
----------------------------------------------------------------------------------------------------------------------------------------------------------------- 
| 12345 | 2 | 06/20/2017 | 123 Main Street | 123 Main Street |  0  | 60,000 | 60,000 | 0  | 01/15/2017 | 01/15/2017 |  0  | 
| 12345 | 3 | 06/21/2017 | 123 Main Street | 123 Main Street |  0  | 60,000 | 60,000 | 0  | 01/15/2017 | 01/15/2017 |  0  | 
| 12345 | 4 | 06/22/2017 | 123 Main Street | 123 Main Street |  0  | 65,000 | 60,000 | 1  | 01/15/2017 | 06/21/2017 |  1  | 

我只需要更改的記錄插入到一​​個新表,新表如下所示:

| RunKey | AccountNo | ChangedCol | PrevRunDate | RunDate | PrevValue | NewValue | 
------------------------------------------------------------------------------------------- 
| 4 | 12345 | Salary  | 06/21/2017 | 06/22/2017 | 60,000 | 65,000  | 
| 4 | 12345 | PromotionDate | 06/21/2017 | 06/22/2017 | 01/15/2017 | 06/21/2017 | 

將有每列變化的新紀錄。因此,如果多列更改,每個更改都會記錄在一個新行中。這是我需要幫助的地方,我不知道如何動態地只將已更改的列插入到新表中。

+0

您確定這是您想要如何進行劃轉或審覈更改?你可以看到這個很容易使用'除' – scsimon

+0

我接受想法。這只是我的第一個想法,沒有人和我一起工作。我改變了數據以簡化它,但我正在處理費用和日期,問題是我們的管理員可能會問爲什麼發生了變化,並且我們有很多記錄是我們無法分辨的,所以我們要創建此表以追蹤任何改變,以便我們可以回到任何時間點,看看哪裏發生了變化以及變化是什麼。 – bm11

+0

那麼你有點將數據轉換爲審計的新格式。這可能對你很好,但問題是你會使用這些數據?你將如何查詢它。你會做什麼聚合/等?把它放在這種格式中很難將其鏈接回任何數據集,這可能會導致一些問題。所以第一個問題是你將如何處理它。這將有助於確定這是否是一種合理的方法恕我直言 – scsimon

回答

1

所以,通常這是通過觸發器完成的。任何時候在表上完成插入或更新操作時,都會對審計表執行後續插入操作。所以,我真的會研究這一點。但是如果你不想走這條路線,或者這是一個你不能添加觸發器的第三方系統,你可以用幾種方式插入更改。一個快速的方法是使用除了。基本上,它將來自源表的記錄插入審計表中,當它們不完全匹配時。這是一個例子。

declare @source table (
         AccountNo int 
         ,Address varchar(256) 
         ,Salary decimal(16,4) 
         ,PromotionDate datetime) 
insert into @source 
values 
(12345,'123 Main Street',60000,'20170115') 


declare @audit table (
         AccountNo int 
         ,Address varchar(256) 
         ,Salary decimal(16,4) 
         ,PromotionDate datetime 
         ,RunDate datetime) 

--load the audit table with the current version of the source table 
insert into @audit 
select *, getdate() from @source 

--show that the tables match currently 
select * from @source 
select * from @audit 

--insert into @audit if there are any changes (notice we haven't made any updates yet) 
insert into @audit 
select AccountNo, Address, Salary, PromotionDate, getdate() from @source 
except 
select AccountNo, Address, Salary, PromotionDate, getdate() from @audit 

--show that a record WAS NOT inserted since there was no change. There is only 1 record, the orignal version 
select * from @audit 

--update the promotion and salary 
update @source 
set 
    PromotionDate = '20170331' 
    ,Salary = '65000' 

--insert into @audit if there are any changes 
insert into @audit 
select AccountNo, Address, Salary, PromotionDate, getdate() from @source 
except 
select AccountNo, Address, Salary, PromotionDate, getdate() from @audit 

--show that a record was inserted since there was a change 
select * from @audit 

然後,所有你需要做的就是從@audit表和order by RunDate選擇,你可以很容易地看到什麼是迅速改變,對旋轉的數據並具有用於每一個變化,每帳戶1行。在這個例子中,儘管薪水和促銷數據發生變化,但您只能看到1行。您可以使用您的LEAD和LAG函數,或者在RunDate < RunDate的前1處自行加入以標記列是否更改,但實際上沒有必要。

+0

有沒有辦法捕捉舊的價值呢?這是關鍵,他想知道變化的日期,舊的價值和新的價值,以及哪一列發生了變化。所以不是插入包含更改的整個行,而是隻想查看已更改的特定列,更改日期以及舊值和新值。 – bm11

+0

更改的日期是runDate,假定您每天都運行該日期。所以你只會得到一個新的行,如果它改變了,日期將是getdate(),它會顯示它更改的日期。走另一條路是真的需要4個步驟太多。這種方法向您顯示更改以及更改的時間,僅在更改時才更改。它解決了核心問題,而無需爲糟糕的設計使用解決方案恕我直言 – scsimon

+0

@ bm11是的,觸發器可以訪問舊值和新值。 – Dijkgraaf

1

下可能會有幫助,但你必須定義字段中Cross Apply

例跟蹤dbFiddle

Declare @YourTable Table ([AccountNo] int,[RunKey] int,[RunDate] date,[Address] varchar(50),[Salary] int,[PromotionDate] date) 
Insert Into @YourTable Values 
(12345,2,'06/20/2017','123 Main Street',60000,'01/15/2017') 
,(12345,3,'06/21/2017','123 Main Street',60000,'01/15/2017') 
,(12345,4,'06/22/2017','123 Main Street',65000,'06/21/2017') 


;with cte as (
    Select A.AccountNo 
      ,A.RunKey 
      ,A.RunDate 
      ,B.* 
     ,PreValue=Lag(Value) over (Partition By AccountNo,Item Order by RunDate) 
     ,PreDate =Lag(RunDate) over (Partition By AccountNo,Item Order by RunDate) 
    From @YourTable A 
    Cross Apply (values ('Address'  ,cast(A.[Address] as varchar(max))) 
         ,('Salary'  ,cast(A.[Salary] as varchar(max))) 
         ,('PromotionDate',cast(A.[PromotionDate] as varchar(max))) 
       ) B (Item,Value) 
) 
Select * 
From cte 
Where Value<>PreValue and PreValue is not null 

返回

enter image description here

如果與可視化幫助

熱膨脹係數生成

enter image description here

+0

我用實際的樣本數據測試了它,並且它完美地工作。我正在用大約900k行的完整數據集來測試它。它的運行速度非常緩慢,因爲我有26列來跟蹤更改,看起來cte0會爲每個RunKey的每列創建一行。 – bm11

+0

@ bm11參見編輯 - 第二選項 –

+0

我將您的腳本添加到遍歷帳號並插入我的表格的光標。對所有以前的記錄都很有用,並且會創建一個前進的觸發器。你知道如何使用觸發器來提取列名嗎?當我運行夜間插入時,我希望它在賬號級別查看最近的RunKey,並比較以查看是否發生了任何更改。 – bm11

0

我不知道我已經清楚地理解你的要求,但我會找一個使用INSERT/UPDATE觸發器解決這種審計試驗任務的解決方案。也許這會讓事情變得更簡單。

相關問題