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

Esecuzione dell'audit delle modifiche ai dati utilizzando la tabella temporale

SQL Server 2016 ha introdotto una funzionalità denominata "Tabella temporale con versione del sistema". Usando la tabella normale, puoi recuperare i dati correnti; mentre si utilizza una tabella temporale con versione di sistema, è possibile recuperare i dati che sono stati eliminati o aggiornati in passato. Per fare ciò, una tabella temporale creerà una tabella della cronologia. La tabella della cronologia memorizzerà i vecchi dati con "ora_inizio ” e “end_time ”. Che indica un periodo di tempo per il quale il record era attivo.

Esempio:se aggiorni il prezzo di un prodotto da 30 a 50 interrogando una tabella normale, puoi recuperare il prezzo del prodotto aggiornato che è 50. Usando una tabella temporale, puoi recuperare il vecchio valore che è 30.

Utilizzando le tabelle temporali, si può eseguire:

  1. Traccia la cronologia di un record :possiamo rivedere un valore del record specifico, che è stato modificato nel tempo.
  2. Recupero a livello di record :se abbiamo eliminato un record specifico dalla tabella o un record è danneggiato, possiamo recuperarlo dalla tabella della cronologia.

Le tabelle temporali acquisiscono la data e l'ora di un record in base alle date fisiche (data di calendario) dell'aggiornamento e dell'eliminazione del record. Attualmente, non supporta il controllo delle versioni in base alle date logiche. Ad esempio, se aggiorni il nome del prodotto utilizzando l'istruzione UPDATE alle 13:00, la tabella temporale manterrà la cronologia del nome del prodotto fino alle 13:00. Successivamente, sarà applicabile un nuovo nome. Tuttavia, cosa accadrebbe se la modifica del nome del prodotto dovesse iniziare dalle 14:00? Ciò significa che devi aggiornare l'istruzione in tempo per farlo funzionare e dovresti aver eseguito l'istruzione UPDATE alle 14:00 anziché alle 13:00.

Le tabelle temporali hanno i seguenti prerequisiti:

  1. Deve essere definita una chiave primaria.
  2. Devono essere definite due colonne per registrare l'ora di inizio e l'ora di fine con il tipo di dati datetime2. Queste colonne sono denominate colonne SYSTEM_TIME.

Hanno anche alcune limitazioni:

  1. INVECE DI trigger e OLTP in memoria non sono consentiti.
  2. Le tabelle della cronologia non possono avere alcun vincolo.
  3. I dati nella tabella della cronologia non possono essere modificati.

Creazione di una tabella con versione di sistema

Il seguente script verrà utilizzato per creare una semplice tabella con versione di sistema:

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

Nello script sopra, ho definito HISTORY_TABLE chiamato dbo. Cronologia_Modifica_Prodotto. Se non si specifica un nome per la tabella della cronologia, SQL Server creerà automaticamente una tabella della cronologia con la struttura seguente.

Dbo.MSSQL_TemporalHistoryFor_xxx, dove xxx è l'ID oggetto.

La tabella temporale avrà l'aspetto mostrato nello screenshot seguente:

Come verranno aggiornate le colonne dei periodi durante l'esecuzione dell'istruzione DML sulla tabella temporale?

Ogni volta che eseguiamo l'inserimento, l'aggiornamento e l'eliminazione di una query sulla tabella temporale, le colonne dei periodi (SysStartDate e SysEndDate) verranno aggiornate.

Inserisci query

Quando eseguiamo l'operazione INSERT sulla tabella temporale, il sistema imposta il valore della colonna SysStartTime sull'ora di inizio della transazione corrente e contrassegna la riga come aperta.

Inseriamo alcune righe nei "Prodotti ' ed esamina come i dati vengono archiviati in questa tabella.

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

Come mostrato nello screenshot sopra, il valore di "Product_Valid_From La colonna è "2018-04-02 06:55:04.4865670 ' che è la data di inserimento della riga. E il valore di "Product_Valid_To ' colonna è '9999-12-31 23:59:59.9999999 ', che indica che la riga è aperta.

Aggiorna query

Quando eseguiamo una query di aggiornamento sulla tabella temporale, il sistema memorizzerà i valori delle righe precedenti nella tabella della cronologia e imposterà il tempo di transazione corrente come EndTime e aggiorna la tabella corrente con un nuovo valore. SysStartTime sarà l'ora di inizio della transazione e SysEndTime sarà il massimo di 9999-12-31.

Cambiamo il costo del prodotto di "Mouse ' da 500 a 250. Verificheremo l'output di 'Prodotto '.

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

select * from Prodcuts where Product_name='Mouse'

Come puoi vedere nello screenshot sopra, un valore di "Product_Valid_From La colonna ' è stata modificata. Il nuovo valore è il tempo di transazione corrente (UTC). E il valore di "Product_Valid_To ' colonna è "9999-12-31 23:59:59.9999999 ', che indica che la riga è aperta e ha aggiornato il prezzo.

Osserviamo l'output di Product_change_history tabella interrogandola.

select * from Product_Change_History where Product_name='Mouse'

Come puoi vedere nello screenshot qui sopra, è stata aggiunta una riga in Product_change_history tabella, che ha una versione precedente della riga. Valore di "Costo_prodotto ' è 500, Valore di 'Product_valid_From ' è l'ora in cui è stato inserito il record e il valore di Product_Valid_To colonna è quando il valore di colonna Costo_prodotto è stato aggiornato. Questa versione di riga è considerata chiusa.

Elimina query

Quando eliminiamo un record dalla tabella temporale, il sistema memorizzerà la versione corrente della riga nella tabella della cronologia e imposterà l'ora di transazione corrente come EndTime ed eliminerà il record dalla tabella corrente.

Eliminiamo il record di "Headset".

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

Osserviamo l'output di Product_change_history tabella interrogandola.

select * from Product_Change_History where Product_name='Headset'

Come puoi vedere nello screenshot qui sopra, è stata aggiunta una riga in Product_change_history tabella, che è stata eliminata dalla tabella corrente. Valore di "Prodotto_valido_da ' è l'ora in cui è stato inserito il record e il valore del Product_Valid_To colonna è l'ora in cui la riga è stata eliminata che indica che la versione della riga è chiusa.

Controllo delle modifiche ai dati per un periodo di tempo specifico

Per controllare le modifiche ai dati per una tabella specifica, è necessario eseguire l'analisi basata sul tempo delle tabelle temporali. Per farlo, dobbiamo usare "FOR SYSTEM_TIME ' clausola con sotto-clausole specifiche temporali di seguito ai dati della query nelle tabelle corrente e cronologia. Consentitemi di spiegare l'output delle query utilizzando diverse sottoclausole. Di seguito la configurazione:

  1. Ho inserito un prodotto denominato 'Flat Washer 8' con Prezzo di Listino 0.00 nella tabella temporale alle 09:02:25.
  2. Ho modificato il prezzo di listino alle 10:13:56. Il nuovo prezzo è 500,00.

AL

Questa clausola verrà utilizzata per recuperare lo stato dei record per un determinato periodo di tempo in AS OF sottoclausola. Per capirlo, eseguiamo diverse query:

Innanzitutto, eseguiremo una query utilizzando AS OF clausola con ”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'

Ora, come puoi vedere nello screenshot sopra, la query ha restituito una riga con il valore aggiornato di "ListPrice ” e il valore di Product_Valid_To è la data massima.

Eseguiamo un'altra query utilizzando AS OF c lause con “SystemTime =09:10:56: ”.

Ora, come puoi vedere nello screenshot sopra, il valore di "Prezzo di listino ” è 0,00.

Da a

Questa clausola restituirà le righe attive tra e . Per capirlo, esegui la seguente query utilizzando Da...A sottoclausola con "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'

Lo screenshot seguente mostra il risultato della query:

TRA e

Questa clausola è simile a DA.. A clausola. L'unica differenza è che includerà i record attivi a . Per capirlo, eseguiamo la seguente query:

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'

Lo screenshot seguente mostra il risultato della query:

Incluso IN (, )

Questa sottoclausola includerà i record che sono diventati attivi e sono terminati nell'intervallo di date specificato. Non include i record attivi. Per capirlo, esegui la query di seguito utilizzando "Contenuto IN '2018-04-20 09:02:25al ‘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'

Lo screenshot seguente mostra il risultato della query:

Scenario

Un'organizzazione sta utilizzando un software di inventario. Quel software di inventario utilizza una tabella dei prodotti che è una tabella temporale della versione del sistema. A causa di un bug dell'applicazione, alcuni prodotti sono stati eliminati e anche i prezzi dei prodotti sono stati aggiornati in modo errato.

In qualità di DBA, dobbiamo indagare su questo problema e recuperare i dati che sono stati aggiornati ed eliminati in modo errato dalla tabella.

Per simulare lo scenario sopra, creiamo una tabella con alcuni dati significativi. Creerò una nuova tabella temporale denominata "tblProduct ' nel database Demo che è un clone di [Produzione].[Prodotti] tabella del database AdventureWorks2014.

Per eseguire l'attività sopra, ho seguito i passaggi seguenti:

  1. Estratto "crea script tabella" [Produzione]. [Prodotti] dal database AdventureWorks2014.
  2. Rimossi tutti i "vincoli e indici" dallo script.
  3. Ha mantenuto inalterata la struttura delle colonne.
  4. Per convertirlo in una tabella temporale, ho aggiunto le colonne SysStartTime e SysEndTime.
  5. Versioning del sistema abilitato.
  6. Tabella della cronologia specificata.
  7. Eseguito lo script sul database edem.

Di seguito lo script:

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

Ho importato i dati dalla tabella dei prodotti del database "AdventureWorks2014" alla tabella dei prodotti di "DemoDatabase" eseguendo il seguente script:

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

Ho eliminato i record dei nomi dei prodotti che iniziano con "Thin-Jam Hex Nut" da tblProduct. Ho anche cambiato il prezzo dei prodotti i cui nomi iniziano con Flat Washer su "tblProduct ' tabella eseguendo la seguente query:

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

Siamo a conoscenza del momento in cui i dati sono stati cancellati. Quindi, per identificare quali dati sono stati cancellati, utilizzeremo la sottoclausola Contained-IN. Come accennato in precedenza, mi darà l'elenco di record con versioni di riga che sono diventate attive e sono terminate entro l'intervallo di date specificato. Quindi, eseguito sotto la query:

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)

Eseguendo la query precedente, sono state recuperate 22 righe.

IlContenuto-IN la clausola popolerà le righe che sono state aggiornate ed eliminate durante il tempo specificato.

Popolare i record eliminati:

Per popolare i record eliminati, dobbiamo saltare i record che sono stati aggiornati durante il tempo specificato nella clausola Contained-IN. Nello script sottostante, "Dove " La clausola salterà i prodotti che sono presenti nel tblProduct tavolo. Eseguiremo la seguente query:

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)

La query precedente ha saltato i record che sono stati aggiornati; quindi ha restituito 13 righe. Vedi sotto screenshot:

Utilizzando il metodo sopra, saremo in grado di ottenere l'elenco dei prodotti che sono stati eliminati da tblProduct tabella.

Popolare i record aggiornati

Per popolare i record aggiornati, dobbiamo saltare i record che sono stati eliminati durante il tempo specificato in Contained-IN clausola. Nello script sottostante, "Dove La clausola includerà i prodotti che sono presenti nel tblProduct tavolo. Eseguiremo la seguente query:

 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)

La query precedente ha saltato i record che sono stati aggiornati, quindi ha restituito 9 righe. Vedi sotto screenshot:

Utilizzando il metodo sopra, saremo in grado di identificare i record che sono stati aggiornati con valori errati e i record che sono stati eliminati dalla tabella temporale.

Riepilogo

In questo articolo ho trattato:

  1. Introduzione di alto livello delle tabelle temporali.
  2. Spiegazione di come verranno aggiornate le colonne dei periodi eseguendo query DML.
  3. Una demo per recuperare dalla tabella temporale l'elenco dei prodotti che è stato cancellato e aggiornato con il prezzo sbagliato. Questo rapporto può essere utilizzato a scopo di controllo.