2017-06-22 1 views
2

Ich habe eine Tabelle, die in jede Nacht eingefügt wird, die eine Momentaufnahme der Daten ist. Zu jedem Zeitpunkt können sich die Daten in den Spalten ändern (AccountNo bleibt gleich, RunKey wird um 1 inkrementiert und RunDate wird um 1 Tag erhöht; alle anderen Spalten können ad hoc geändert werden). Unten ist ein Beispiel dafür, wie die Daten aussehen:Wie kann ich den Spaltennamen extrahieren, wenn sich die Daten in SQL Server 2014 ändern?

|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 | 

einen LAG-Funktion und CASE-Ausdruck verwendet ich in der Lage bin, um zu bestimmen, wann eine Änderung ist (Flag von 1 bedeutet, es geändert):

|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  | 

ich brauche nur die geänderten Datensätze in eine neue Tabelle und die neue Tabelle wie folgt aussehen einzufügen:

| 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 | 

Es wird ein neuer Datensatz für jede Spalte Änderung sein. Wenn sich also mehrere Spalten ändern, wird jede Änderung in einer neuen Zeile dokumentiert. Hier brauche ich Hilfe, ich weiß nicht, wie man nur geänderte Spalten dynamisch in die neue Tabelle einfügt.

+0

Sind Sie sicher, dass Sie Änderungen in der Zeilenversion oder im Auditing vornehmen möchten?Sie können das wirklich einfach mit 'except' sehen. – scsimon

+0

Ich bin offen für Ideen. Das war nur mein erster Gedanke und niemand, mit dem ich arbeite, schlug etwas anderes vor. Ich änderte die Daten, um es zu vereinfachen, aber ich arbeite mit Gebühren und Daten und das Problem ist, dass unser Manager fragen kann, warum sich etwas geändert hat und wir so viele Datensätze haben, die wir nicht erzählen können. Deshalb erstellen wir diese Tabelle, die alle verfolgt Änderungen, so dass wir zu jedem Zeitpunkt zurückgehen und sehen können, wo es eine Veränderung gab und was die Veränderung war. – bm11

+0

Nun, Sie schwenken die Daten in ein neues Format für Auditing. Dies kann für Sie in Ordnung sein, aber die Frage wäre, wofür würden Sie diese Daten verwenden? Wie möchten Sie es abfragen? Was für Aggregate/etc würdest du tun? Wenn Sie dieses Format verwenden, ist es schwierig, eine Verbindung zu einem beliebigen Dataset herzustellen, was zu einigen Problemen führen kann. Die erste Frage lautet also: Was wirst du damit machen? Das würde helfen zu bestimmen, ob dies ein logischer Ansatz ist. IMHO – scsimon

Antwort

1

Also, normalerweise wird dies mit einem Trigger gemacht. Jedes Mal, wenn eine Einfügung oder Aktualisierung in einer Tabelle durchgeführt wird, wird eine Folgeeinfügung in Ihre Audit-Tabelle durchgeführt. Also, ich würde mich wirklich darum kümmern. Aber wenn Sie diese Route nicht verwenden möchten oder dies ein System von Drittanbietern ist, dem Sie keine Trigger hinzufügen können, können Sie Änderungen einfach auf verschiedene Arten einfügen. Ein schneller Weg ist mit Ausnahme. Im Grunde fügt es Datensätze aus der Quellentabelle in die Audit-Tabelle ein, wenn sie nicht genau übereinstimmen. Hier ist ein Beispiel.

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 

Dann alles, was Sie tun müssen, ist wählen Sie aus der @audit Tabelle und Ordnung durch RunDate und man kann leicht sehen, was schnell geändert wurde, im Vergleich zu den Daten Schwenken und für jedes Konto für jede Änderung 1 Zeile mit. In diesem Beispiel sehen Sie nur eine zusätzliche Zeile, obwohl sich eine Gehalts- und Aktionsdatenänderung ergeben hat. Sie können Ihre LEAD- und LAG-Funktionen oder Self-Join auf der obersten 1 verwenden, wo RunDate < RunDate zu kennzeichnen, wenn die Spalte geändert, aber es ist wirklich unnötig.

+0

Gibt es auch eine Möglichkeit, den alten Wert zu erfassen? Das ist der Schlüssel hier, er möchte das Datum der Änderung, den alten Wert und den neuen Wert wissen, sowie welche Spalte geändert wurde. Anstatt also die gesamte Zeile einzufügen, die eine Änderung enthält, möchten sie nur die spezifische Spalte anzeigen, die geändert wurde, das Datum der Änderung und die alten und neuen Werte. – bm11

+0

Das Datum der Änderung ist das runDate, vorausgesetzt, Sie führen dies jeden Tag aus. Sie erhalten also nur eine neue Zeile, wenn sie geändert wurde, und das Datum wird getdate() sein, das das Datum anzeigt, an dem es sich geändert hat. Um es anders herum geht es wirklich 4 Schritte zu viel. Dieser Ansatz zeigt Ihnen die Änderungen und wann sie sich geändert haben, nur wenn sie sich geändert haben. Es löst das Kernproblem, ohne eine Lösung für ein schlechtes Design zu verwenden. IMHO – scsimon

+0

@ bm11 Ja, ein Trigger kann sowohl auf den alten Wert als auch auf den neuen Wert zugreifen. – Dijkgraaf

1

können die folgenden helfen, aber Sie werden die Felder definieren, müssen in der Cross Apply

Beispiel oder 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 

Returns

enter image description here

zu verfolgen

Wenn es mit der Visualisierung hilft

Der CTE generiert

enter image description here

+0

Ich testete dies mit tatsächlichen Beispieldaten und es funktionierte perfekt. Ich teste es mit dem vollen Datensatz, der ungefähr 900k Reihen ist. Es läuft sehr langsam, da ich 26 Spalten habe, um Änderungen zu verfolgen, und es scheint, dass cte0 eine Zeile für jede Spalte pro RunKey erstellt. – bm11

+0

@ bm11 Siehe EDIT-Zweite Option –

+0

Ich habe Ihr Skript zu einem Cursor hinzugefügt, der über die Kontonummer iteriert und in meine Tabelle eingefügt wird. Funktioniert hervorragend für alle vorherigen Aufzeichnungen und erzeugt einen Trigger für die Zukunft. Können Sie den Spaltennamen mit einem Trigger extrahieren? Wenn ich die nächtliche Einfügung ausführe, möchte ich, dass sie den letzten RunKey auf der Ebene der Kontonummer betrachtet und vergleicht, um festzustellen, ob irgendwelche Änderungen aufgetreten sind. – bm11

0

Ich bin mir nicht sicher, ob ich Ihre Anforderung klar verstanden haben, aber ich würde ausgesehen haben Eine Lösung, die INSERT/UPDATE-Trigger verwendet, um diese Art von Aufgaben zu lösen. Vielleicht würde das die Dinge vereinfachen.