Database
 sql >> Datenbank >  >> RDS >> Database

Durchführen von Audits zu Datenänderungen mithilfe der temporären Tabelle

SQL Server 2016 hat ein Feature namens „System versionierte temporale Tabelle“ eingeführt. Mit der normalen Tabelle können Sie aktuelle Daten abrufen; Während Sie eine vom System versionierte temporale Tabelle verwenden, können Sie Daten abrufen, die in der Vergangenheit gelöscht oder aktualisiert wurden. Dazu erstellt eine temporale Tabelle eine Verlaufstabelle. Die Verlaufstabelle speichert alte Daten mit „start_time “ und „end_time “. Gibt einen Zeitraum an, in dem der Datensatz aktiv war.

Beispiel:Wenn Sie einen Produktpreis von 30 auf 50 aktualisieren, indem Sie eine normale Tabelle abfragen, können Sie den aktualisierten Produktpreis von 50 abrufen. Mithilfe einer temporalen Tabelle können Sie den alten Wert von 30 abrufen.

Unter Verwendung temporaler Tabellen kann man Folgendes ausführen:

  1. Verfolgen Sie den Verlauf eines Datensatzes :Wir können einen Wert des bestimmten Datensatzes überprüfen, der sich im Laufe der Zeit geändert hat.
  2. Wiederherstellung auf Rekordniveau :Wenn wir einen bestimmten Datensatz aus der Tabelle gelöscht haben oder ein Datensatz beschädigt ist, können wir ihn aus der Verlaufstabelle abrufen.

Temporäre Tabellen erfassen Datum und Uhrzeit eines Datensatzes basierend auf den physischen Daten (Kalenderdatum) der Datensatzaktualisierung und -löschung. Derzeit unterstützt es keine Versionierung basierend auf den logischen Daten. Wenn Sie beispielsweise den Produktnamen um 13:00 Uhr mit der UPDATE-Anweisung aktualisieren, behält die temporale Tabelle den Verlauf des Produktnamens bis 13:00 Uhr bei. Danach gilt ein neuer Name. Was aber, wenn die Änderung des Produktnamens um 14:00 Uhr beginnen sollte? Das bedeutet, dass Sie die Anweisung rechtzeitig aktualisieren müssen, damit sie funktioniert, und Sie sollten die UPDATE-Anweisung um 14:00 Uhr statt um 13:00 Uhr ausgeführt haben.

Temporäre Tabellen haben die folgenden Voraussetzungen:

  1. Ein Primärschlüssel muss definiert werden.
  2. Zwei Spalten müssen definiert werden, um die Start- und Endzeit mit dem Datentyp datetime2 aufzuzeichnen. Diese Spalten werden SYSTEM_TIME-Spalten genannt.

Sie haben auch einige Einschränkungen:

  1. Statt Trigger und In-Memory-OLTP sind nicht zulässig.
  2. Verlaufstabellen dürfen keine Einschränkung haben.
  3. Daten in der Verlaufstabelle können nicht geändert werden.

Erstellen einer systemversionierten Tabelle

Das folgende Skript wird verwendet, um eine einfache systemversionierte Tabelle zu erstellen:

Use DemoDatabase
Go
CREATE TABLE dbo.Prodcuts
	(
	      Product_ID int identity (1,1) primary key
	    , Product_Name varchar (500)
	    , Product_Cost int
	    , Quantity int
	    , Product_Valid_From datetime2 GENERATED ALWAYS AS ROW START NOT NULL
	    , Product_Valid_TO datetime2 GENERATED ALWAYS AS ROW END NOT NULL
	    , PERIOD FOR SYSTEM_TIME (Product_Valid_From,Product_Valid_TO)
	)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE =dbo.Product_Change_History));

Im obigen Skript habe ich HISTORY_TABLE namens dbo definiert. Produkt_Änderungsverlauf. Wenn Sie keinen Namen für die Verlaufstabelle angeben, erstellt SQL Server automatisch eine Verlaufstabelle mit der folgenden Struktur.

Dbo.MSSQL_TemporalHistoryFor_xxx, wobei xxx die Objekt-ID ist.

Die zeitliche Tabelle sieht so aus, wie sie im Screenshot unten gezeigt wird:

Wie werden Periodenspalten aktualisiert, wenn eine DML-Anweisung in der temporären Tabelle ausgeführt wird?

Immer wenn wir eine Abfrage in der temporalen Tabelle einfügen, aktualisieren oder löschen, werden die Periodenspalten (SysStartDate und SysEndDate) aktualisiert.

Abfrage einfügen

Wenn wir die INSERT-Operation für die temporale Tabelle ausführen, setzt das System den Wert der SysStartTime-Spalte auf die Startzeit der aktuellen Transaktion und markiert die Zeile als offen.

Lassen Sie uns einige Zeilen in die „Produkte“ einfügen ’-Tabelle und überprüfen Sie, wie Daten in dieser Tabelle gespeichert werden.

INSERT INTO prodcuts 
            (product_name, 
             product_cost, 
             quantity) 
VALUES      ( 'Mouse', 
              500, 
              10 ), 
            ( 'Key-Board', 
              200, 
              5 ), 
            ( 'Headset', 
              500, 
              1 ), 
            ( 'Laptop', 
              50000, 
              1 )
 select * from Prodcuts

Wie im obigen Screenshot gezeigt, ist der Wert der Datei „Product_Valid_From “ lautet „2018-04-02 06:55:04.4865670 ’, das ist das Zeileneinfügedatum. Und der Wert von „Product_Valid_To “ lautet „9999-12-31 23:59:59.9999999 ’, was anzeigt, dass die Zeile offen ist.

Aktualisierungsabfrage

Wenn wir eine Aktualisierungsabfrage für die temporale Tabelle ausführen, speichert das System die vorherigen Zeilenwerte in der Verlaufstabelle und setzt die aktuelle Transaktionszeit auf EndTime und aktualisieren Sie die aktuelle Tabelle mit einem neuen Wert. SysStartTime wird die Startzeit der Transaktion und SysEndTime sein ist das Maximum von 9999-12-31.

Lassen Sie uns die Produktkosten von „Maus“ ändern ’ von 500 auf 250. Wir prüfen die Ausgabe von ‘Product ’.

Begin tran UpdatePrice
Update Prodcuts set Product_cost=200 where Product_name='Mouse'
Commit tran UpdatePrice

select * from Prodcuts where Product_name='Mouse'

Wie Sie im obigen Screenshot sehen können, ist ein Wert von „Product_Valid_From ’ Spalte wurde geändert. Der neue Wert ist die aktuelle Transaktionszeit (UTC). Und der Wert von „Product_Valid_To “ lautet „9999-12-31 23:59:59.9999999 “, was anzeigt, dass die Zeile offen ist und den Preis aktualisiert hat.

Betrachten wir die Ausgabe von Product_change_history Tabelle, indem Sie sie abfragen.

select * from Product_Change_History where Product_name='Mouse'

Wie Sie im obigen Screenshot sehen können, wurde in Product_change_history eine Zeile hinzugefügt Tabelle, die eine alte Version der Zeile enthält. Wert von "Product_cost". “ ist 500, Wert von „Product_valid_From“. ’ ist die Zeit, zu der der Datensatz eingefügt wurde, und der Wert von Product_Valid_To -Spalte ist, wenn der Wert der Product_cost-Spalte wurde aktualisiert. Diese Zeilenversion wird als geschlossen betrachtet.

Abfrage löschen

Wenn wir einen Datensatz aus der temporalen Tabelle löschen, speichert das System die aktuelle Version der Zeile in der Verlaufstabelle, setzt die aktuelle Transaktionszeit als EndTime und löscht den Datensatz aus der aktuellen Tabelle.

Lassen Sie uns den Datensatz von „Headset“ löschen.

Begin tran DeletePrice
    delete from Prodcuts where product_name='Headset'
Commit tran DeletePrice
ist

Betrachten wir die Ausgabe von Product_change_history Tabelle, indem Sie sie abfragen.

select * from Product_Change_History where Product_name='Headset'

Wie Sie im obigen Screenshot sehen können, wurde in Product_change_history eine Zeile hinzugefügt Tabelle, die aus der aktuellen Tabelle gelöscht wurde. Wert von „Product_valid_From ’ ist die Zeit, zu der der Datensatz eingefügt wurde, und der Wert von Product_Valid_To Spalte ist die Zeit, zu der die Zeile gelöscht wurde, was darauf hinweist, dass die Zeilenversion geschlossen ist.

Prüfen von Datenänderungen für eine bestimmte Zeit

Um die Datenänderungen für eine bestimmte Tabelle zu prüfen, sollten wir die zeitbasierte Analyse temporaler Tabellen durchführen. Dazu müssen wir die Datei „FOR SYSTEM_TIME verwenden ’-Klausel mit den folgenden zeitspezifischen Unterklauseln zu den Abfragedaten in den aktuellen und Verlaufstabellen. Lassen Sie mich die Ausgabe von Abfragen anhand verschiedener Unterklauseln erläutern. Unten ist die Einrichtung:

  1. Ich habe um 09:02:25 Uhr ein Produkt mit dem Namen „Flat Washer 8“ mit dem Listenpreis 0,00 in die temporale Tabelle eingefügt.
  2. Ich habe den Listenpreis um 10:13:56 Uhr geändert. Der Neupreis beträgt 500,00.

AB

Diese Klausel wird verwendet, um den Status der Aufzeichnungen für einen bestimmten Zeitpunkt im AB abzurufen Nebensatz. Um es zu verstehen, führen wir mehrere Abfragen aus:

Zuerst führen wir eine Abfrage mit AS OF aus Klausel mit ”SystemTime =10:15:59 “.

select Name, ListPrice,rowguid,Product_Valid_From,Product_Valid_TO from DemoDatabase.dbo.tblProduct  FOR system_time as of '2018-04-20 10:15:56
where name ='Flat Washer 8'

Wie Sie nun im obigen Screenshot sehen können, hat die Abfrage eine Zeile mit dem aktualisierten Wert „ListPrice“ zurückgegeben “ und den Wert von Product_Valid_To ist das Datumsmaximum.

Lassen Sie uns eine weitere Abfrage mit AS OF c ausführen Lausen Sie mit „SystemTime =09:10:56: “.

Wie Sie im obigen Screenshot sehen können, ist der Wert von „ListPrice ” ist 0,00.

Von bis

Diese Klausel gibt die Zeilen zurück, die zwischen aktiv sind und . Um dies zu verstehen, führen Sie die folgende Abfrage mit From..To aus Untersatz mit „SystemTime From ‘2018-04-20 09:02:25’ to ‘2018-04-20 10:14:56 ‘“.

select Name, ListPrice,rowguid,Product_Valid_From,Product_Valid_TO,ListPrice from DemoDatabase.dbo.tblProduct  FOR system_time from '2018-04-20 09:02:25 to '2018-04-20 10:13:56 where name =  'Flat Washer 8'

Der folgende Screenshot zeigt das Abfrageergebnis:

ZWISCHEN und

Diese Klausel ähnelt der FROM.. An Klausel. Der einzige Unterschied besteht darin, dass die Datensätze enthalten sind, die am aktiv waren . Um es zu verstehen, führen wir die folgende Abfrage aus:

select Name, ListPrice,rowguid,Product_Valid_From,Product_Valid_TO,ListPrice from DemoDatabase.dbo.tblProduct  FOR system_time between '2018-04-20 09:02:25.1265684' and '2018-04-20 10:13:56.1265684' where name =  'Flat Washer 8'

Der folgende Screenshot zeigt das Abfrageergebnis:

Eingeschlossen IN (, )

Dieser Unterabschnitt enthält die Aufzeichnungen, die innerhalb des angegebenen Datumsbereichs aktiv wurden und endeten. Die aktiven Datensätze sind nicht enthalten. Um es zu verstehen, führen Sie die folgende Abfrage mit „Contained IN ‘2018-04-20 09:02:25“ aus ‘ bis ‘2018-04-20 10:14:56’

select Name, ListPrice,rowguid,Product_Valid_From,Product_Valid_TO,ListPrice from DemoDatabase.dbo.tblProduct  FOR system_time Contained IN( '2018-04-20 09:02:25' , '2018-04-20 10:13:56 ') where name =  'Flat Washer 8'

Der folgende Screenshot zeigt das Abfrageergebnis:

Szenario

Eine Organisation verwendet eine Inventarisierungssoftware. Diese Bestandssoftware verwendet eine Produkttabelle, die eine zeitliche Systemversionstabelle ist. Aufgrund eines Anwendungsfehlers wurden einige Produkte gelöscht und die Preise der Produkte wurden auch fälschlicherweise aktualisiert.

Als DBAs müssen wir dieses Problem untersuchen und die Daten wiederherstellen, die fälschlicherweise aktualisiert und aus der Tabelle gelöscht wurden.

Um das obige Szenario zu simulieren, erstellen wir eine Tabelle mit einigen aussagekräftigen Daten. Ich werde eine neue temporale Tabelle mit dem Namen „tblProduct“ erstellen ’ in der Demo-Datenbank, die ein Klon von [Production].[Products] ist Tabelle der AdventureWorks2014-Datenbank.

Um die obige Aufgabe auszuführen, habe ich die folgenden Schritte befolgt:

  1. Extrahiertes „Tabellenskript erstellen“ [Produktion]. [Produkte] aus der AdventureWorks2014-Datenbank.
  2. Alle „Einschränkungen und Indizes“ aus dem Skript entfernt.
  3. Die Spaltenstruktur wurde unverändert beibehalten.
  4. Um sie in eine temporale Tabelle umzuwandeln, habe ich SysStartTime- und SysEndTime-Spalten hinzugefügt.
  5. System_Versioning aktiviert.
  6. Angegebene Verlaufstabelle.
  7. Das Skript in der edemo-Datenbank ausgeführt.

Unten ist das Skript:

USE [DemoDatabase]
GO
CREATE TABLE [tblProduct](
	[ProductID] [int] IDENTITY(1,1) Primary Key,
	[Name] varchar(500) NOT NULL,
	[ProductNumber] [nvarchar](25) NOT NULL,
	[Color] [nvarchar](15) NULL,
	[SafetyStockLevel] [smallint] NOT NULL,
	[ReorderPoint] [smallint] NOT NULL,
	[StandardCost] [money] NOT NULL,
	[ListPrice] [money] NOT NULL,
	[Size] [nvarchar](5) NULL,
	[SizeUnitMeasureCode] [nchar](3) NULL,
	[WeightUnitMeasureCode] [nchar](3) NULL,
	[Weight] [decimal](8, 2) NULL,
	[DaysToManufacture] [int] NOT NULL,
	[ProductLine] [nchar](2) NULL,
	[Class] [nchar](2) NULL,
	[Style] [nchar](2) NULL,
	[ProductSubcategoryID] [int] NULL,
	[ProductModelID] [int] NULL,
	[SellStartDate] [datetime] NOT NULL,
	[SellEndDate] [datetime] NULL,
	[DiscontinuedDate] [datetime] NULL,
	[rowguid] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
	[ModifiedDate] [datetime] NOT NULL,
	Product_Valid_From datetime2 GENERATED ALWAYS AS ROW START NOT NULL
    , Product_Valid_TO datetime2 GENERATED ALWAYS AS ROW END NOT NULL
    , PERIOD FOR SYSTEM_TIME (Product_Valid_From,Product_Valid_TO)
 )
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE =dbo.Product_History));
GO

Ich habe Daten aus der Produkttabelle der „AdventureWorks2014“-Datenbank in die Produkttabelle der „DemoDatabase“ importiert, indem ich das folgende Skript ausgeführt habe:

insert into DemoDatabase.dbo.tblProduct
(Name
,ProductNumber
,Color
,SafetyStockLevel
,ReorderPoint
,StandardCost
,ListPrice
,Size
,SizeUnitMeasureCode
,WeightUnitMeasureCode
,Weight
,DaysToManufacture
,ProductLine
,Class
,Style
,ProductSubcategoryID
,ProductModelID
,SellStartDate
,SellEndDate
,DiscontinuedDate
,rowguid
,ModifiedDate)
select top 50
Name
,ProductNumber
,Color
,SafetyStockLevel
,ReorderPoint
,StandardCost
,ListPrice
,Size
,SizeUnitMeasureCode
,WeightUnitMeasureCode
,Weight
,DaysToManufacture
,ProductLine
,Class
,Style
,ProductSubcategoryID
,ProductModelID
,SellStartDate
,SellEndDate
,DiscontinuedDate
,rowguid
,ModifiedDate
from AdventureWorks2014.Production.Product

Ich habe die Produktnamen aus tblProduct gelöscht, die mit „Thin-Jam Hex Nut“ beginnen. Ich habe auch den Preis der Produkte geändert, deren Namen mit Flat Washer auf „tblProduct“ beginnen ’-Tabelle, indem Sie die folgende Abfrage ausführen:

delete from DemoDatabase.dbo.Product where name like '%Thin-Jam Hex Nut%'
waitfor delay '00:01:00'
update DemoDatabase.dbo.tblProduct set ListPrice=500.00 where name like '%Flat Washer%'

Der Zeitpunkt der Datenlöschung ist uns bekannt. Um zu identifizieren, welche Daten gelöscht wurden, verwenden wir daher die Contained-IN-Unterklausel. Wie oben erwähnt, wird mir die Liste der Datensätze mit Zeilenversionen angezeigt, die innerhalb des angegebenen Datumsbereichs aktiv wurden und endeten. Führen Sie dann die folgende Abfrage aus:

declare @StartDateTime datetime
declare @EndDateTime datetime
set @StartDateTime=convert (datetime2, getdate()-1)
set @EndDateTime=convert (datetime2, getdate())
select ProductID, Name, ProductNumber,Product_Valid_From, Product_Valid_To from Product For SYSTEM_TIME Contained IN ( @StartDateTime , @EndDateTime)

Durch Ausführen der obigen Abfrage wurden 22 Zeilen abgerufen.

Das Contained-IN -Klausel füllt die Zeilen, die während der angegebenen Zeit aktualisiert und gelöscht wurden.

Gelöschte Datensätze auffüllen:

Um die gelöschten Datensätze aufzufüllen, müssen wir die Datensätze überspringen, die während der in der Contained-IN-Klausel angegebenen Zeit aktualisiert wurden. Im folgenden Skript wird das „Where ”-Klausel überspringt die Produkte, die in tblProduct vorhanden sind Tisch. Wir werden die folgende Abfrage ausführen:

declare @StartDateTime datetime
declare @EndDateTime datetime
set @StartDateTime=convert(datetime2,getdate()-1)
set @EndDateTime=convert(datetime2,getdate())

select ProductID, Name, ProductNumber,Product_Valid_From, Product_Valid_To from tblProduct For SYSTEM_TIME Contained IN ( @StartDateTime , @EndDateTime) Where Name not in (Select Name from tblProduct)

Die obige Abfrage hat die aktualisierten Datensätze übersprungen; daher wurden 13 Zeilen zurückgegeben. Siehe Screenshot unten:

Mit der obigen Methode können wir die Liste der Produkte abrufen, die aus tblProduct gelöscht wurden Tabelle.

Aktualisierte Datensätze auffüllen

Um die aktualisierten Datensätze aufzufüllen, müssen wir die Datensätze überspringen, die während der im Contained-IN angegebenen Zeit gelöscht wurden Klausel. Im folgenden Skript wird das „Where ”-Klausel enthält die Produkte, die im tblProduct enthalten sind Tisch. Wir werden die folgende Abfrage ausführen:

 declare @StartDateTime datetime
declare @EndDateTime datetime
set @StartDateTime=convert(datetime2,getdate()-1)
set @EndDateTime=convert(datetime2,getdate())
select ProductID, Name, ProductNumber,Product_Valid_From, Product_Valid_To from tblProduct For SYSTEM_TIME Contained IN ( @StartDateTime , @EndDateTime) Where Name in (Select Name from tblProduct)

Die obige Abfrage hat die aktualisierten Datensätze übersprungen, daher wurden 9 Zeilen zurückgegeben. Siehe Screenshot unten:

Mit der obigen Methode können wir die Datensätze identifizieren, die mit falschen Werten aktualisiert wurden, und die Datensätze, die aus der temporalen Tabelle gelöscht wurden.

Zusammenfassung

In diesem Artikel habe ich behandelt:

  1. Hochrangige Einführung von temporalen Tabellen.
  2. Erläutert, wie Periodenspalten durch Ausführen von DML-Abfragen aktualisiert werden.
  3. Eine Demo zum Abrufen der Liste von Produkten, die gelöscht und mit dem falschen Preis aktualisiert wurden, aus der temporären Tabelle. Dieser Bericht kann zu Prüfungszwecken verwendet werden.