sql >> Databáze >  >> RDS >> Database

Provádění auditu změn dat pomocí časové tabulky

SQL Server 2016 zavedl funkci nazvanou ‚Dočasná tabulka verzí systému‘. Pomocí normální tabulky můžete získat aktuální data; při použití časové tabulky s verzí systému můžete načíst data, která byla v minulosti odstraněna nebo aktualizována. Chcete-li to provést, dočasná tabulka vytvoří tabulku historie. Tabulka historie bude ukládat stará data s „start_time “ a „end_time “. Což označuje časové období, po které byl záznam aktivní.

Příklad:Pokud aktualizujete cenu produktu z 30 na 50 dotazem na normální tabulku, můžete získat aktualizovanou cenu produktu, která je 50. Pomocí časové tabulky můžete získat starou hodnotu, která je 30.

Pomocí časových tabulek lze provést:

  1. Historie záznamu :můžeme zkontrolovat hodnotu konkrétního záznamu, která se v průběhu času změnila.
  2. Obnovení na úrovni záznamu :pokud jsme smazali konkrétní záznam z tabulky nebo je záznam poškozen, můžeme jej načíst z tabulky historie.

Časové tabulky zachycují datum a čas záznamu na základě fyzických dat (kalendářní datum) aktualizace a odstranění záznamu. V současné době nepodporuje verzování založené na logických datech. Pokud například aktualizujete název produktu pomocí příkazu UPDATE ve 13:00, dočasná tabulka bude udržovat historii názvu produktu až do 13:00. Poté bude platit nový název. Co když však změna názvu produktu měla začít od 14:00? To znamená, že musíte příkaz aktualizovat včas, aby fungoval, a příkaz UPDATE byste měli provést ve 14:00 namísto ve 13:00.

Časové tabulky mají následující předpoklady:

  1. Musí být definován primární klíč.
  2. Musí být definovány dva sloupce, aby bylo možné zaznamenávat čas zahájení a čas ukončení s datovým typem datetime2. Tyto sloupce se nazývají sloupce SYSTEM_TIME.

Mají také určitá omezení:

  1. MÍSTO spouštěčů a OLTP v paměti nejsou povoleny.
  2. Tabulky historie nemohou mít žádná omezení.
  3. Data v tabulce historie nelze upravit.

Vytvoření tabulky se systémovou verzí

Následující skript bude použit k vytvoření jednoduché tabulky s verzí systému:

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

Ve výše uvedeném skriptu jsem definoval HISTORY_TABLE s názvem dbo. Product_Change_History. Pokud nezadáte název pro tabulku historie, SQL Server automaticky vytvoří tabulku historie s následující strukturou.

Dbo.MSSQL_TemporalHistoryFor_xxx, kde xxx je ID objektu.

Dočasná tabulka bude vypadat tak, jak je zobrazena na níže uvedeném snímku obrazovky:

Jak budou aktualizovány sloupce období při provádění příkazu DML na Temporal Table?

Kdykoli provedeme vložení, aktualizaci a odstranění dotazu v časové tabulce, budou aktualizovány sloupce období (SysStartDate a SysEndDate).

Vložit dotaz

Když na časové tabulce provedeme operaci INSERT, systém nastaví hodnotu sloupce SysStartTime na čas zahájení aktuální transakce a označí řádek jako otevřený.

Vložíme několik řádků do „Produktů a zkontrolujte, jak jsou data v této tabulce uložena.

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

Jak ukazuje výše uvedený snímek obrazovky, hodnota „Product_Valid_From “ sloupec je „2018-04-02 06:55:04.4865670 “ což je datum vložení řádku. A hodnota atributu „Product_Valid_To “ sloupec je „9999-12-31 23:59:59.9999999 “, což znamená, že řádek je otevřený.

Aktualizovat dotaz

Když provedeme jakýkoli aktualizační dotaz na časové tabulce, systém uloží hodnoty předchozího řádku do tabulky historie a nastaví aktuální čas transakce jako EndTime a aktualizujte aktuální tabulku novou hodnotou. SysStartTime bude čas zahájení transakce a SysEndTime bude maximálně 9999-12-31.

Pojďme změnit cenu produktu „myš “ od 500 do 250. Zkontrolujeme výstup „Produktu '.

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

select * from Prodcuts where Product_name='Mouse'

Jak můžete vidět na výše uvedeném snímku obrazovky, hodnota „Product_Valid_From “ byl změněn. Nová hodnota je aktuální čas transakce (UTC). A hodnota atributu „Product_Valid_To ’ sloupec je ‘9999-12-31 23:59:59.9999999 “, což znamená, že řádek je otevřený a aktualizoval cenu.

Podívejme se na výstup Product_change_history dotazem.

select * from Product_Change_History where Product_name='Mouse'

Jak můžete vidět na výše uvedeném snímku obrazovky, do Product_change_history byl přidán řádek tabulka, která má starou verzi řádku. Hodnota ‘Cost_produktu “ je 500, hodnota „Product_valid_From ’ je čas, kdy byl záznam vložen, a hodnota Product_Valid_To je hodnota sloupce Product_cost byl aktualizován. Tato verze řádku je považována za uzavřenou.

Smazat dotaz

Když odstraníme záznam z časové tabulky, systém uloží aktuální verzi řádku do tabulky historie a nastaví aktuální čas transakce jako EndTime a vymaže záznam z aktuální tabulky.

Odstraníme záznam „Náhlavní souprava“.

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

Podívejme se na výstup Product_change_history dotazem.

select * from Product_Change_History where Product_name='Headset'

Jak můžete vidět na výše uvedeném snímku obrazovky, do Product_change_history byl přidán řádek tabulka, která byla odstraněna z aktuální tabulky. Hodnota „Product_valid_From “ je čas, kdy byl záznam vložen, a hodnota Product_Valid_To sloupec je čas, kdy byl řádek odstraněn, což znamená, že verze řádku je uzavřena.

Kontrola změn dat pro konkrétní čas

Abychom mohli auditovat změny dat pro konkrétní tabulku, měli bychom provést časovou analýzu časových tabulek. K tomu musíme použít „FOR SYSTEM_TIME ' klauzule s níže uvedenými dílčími klauzulemi specifickými pro čas na data dotazu napříč aktuálními tabulkami a tabulkami historie. Dovolte mi vysvětlit výstup dotazů pomocí různých dílčích vět. Níže je nastavení:

  1. V 09:02:25 jsem do časové tabulky vložil produkt s názvem „Plochá podložka 8“ s katalogovou cenou 0,00.
  2. Katalogovou cenu jsem změnil v 10:13:56. Nová cena je 500,00.

OD

Tato klauzule bude použita k načtení stavu záznamů pro danou dobu vK D pod-klauzule. Abychom tomu porozuměli, provedeme několik dotazů:

Nejprve provedeme dotaz pomocí AS OF klauzule s „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'

Nyní, jak můžete vidět na výše uvedeném snímku obrazovky, dotaz vrátil jeden řádek s aktualizovanou hodnotou „ListPrice “ a hodnotu Product_Valid_To je maximální datum.

Proveďme další dotaz pomocí AS OF c lause pomocí „SystemTime =09:10:56: “.

Nyní, jak můžete vidět na výše uvedeném snímku obrazovky, hodnota „ListPrice “ je 0,00.

Od do

Tato klauzule vrátí řádky aktivní mezi a . Abyste tomu porozuměli, nechte provést následující dotaz pomocí Od..Do pod klauzuli s „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'

Následující snímek obrazovky ukazuje výsledek dotazu:

MEZI A

Tato klauzule je podobná klauzuli FROM.. Komu doložka. Jediný rozdíl je v tom, že bude zahrnovat záznamy, které byly aktivní v . Abychom to pochopili, proveďte následující dotaz:

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'

Následující snímek obrazovky ukazuje výsledek dotazu:

Zahrnuto (, )

Tento článek bude zahrnovat záznamy, které se staly aktivními a skončily v určeném časovém období. Nezahrnuje aktivní záznamy. Abyste tomu porozuměli, proveďte níže uvedený dotaz pomocí „Obsahováno V ‘2018-04-20 09:02:25do ‘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'

Následující snímek obrazovky ukazuje výsledek dotazu:

Scénář

Organizace používá inventární software. Tento inventární software používá tabulku produktů, což je dočasná tabulka verzí systému. Kvůli chybě aplikace bylo smazáno několik produktů a ceny produktů byly také chybně aktualizovány.

Jako správci databází musíme tento problém prošetřit a obnovit data, která byla nesprávně aktualizována a odstraněna z tabulky.

Pro simulaci výše uvedeného scénáře vytvořte tabulku s některými smysluplnými daty. Vytvořím novou časovou tabulku s názvem „tblProduct “ v demo databázi, která je klonem [Produkce].[Produkty] tabulky databáze AdventureWorks2014.

K provedení výše uvedeného úkolu jsem postupoval podle níže uvedených kroků:

  1. Extrahovaný „skript vytvoření tabulky“ [Produkce]. [Produkty] z databáze AdventureWorks2014.
  2. Ze skriptu byla odstraněna všechna „omezení a indexy“.
  3. Struktura sloupců zůstala nezměněna.
  4. Pro převod na dočasnou tabulku jsem přidal sloupce SysStartTime a SysEndTime.
  5. Povoleno System_Versioning.
  6. Uvedená tabulka historie.
  7. Spustil skript v databázi edemo.

Níže je 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

Importoval jsem data z tabulky produktů databáze „AdventureWorks2014“ do tabulky produktů „DemoDatabase“ spuštěním následujícího skriptu:

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

Smazal jsem záznamy o názvech produktů, které začínají „Thin-Jam Hex Nut“ z tblProduct. Také jsem změnil cenu produktů, jejichž názvy začínají na Flat Washer na ‘tblProduct ’ tabulky provedením následujícího dotazu:

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

Jsme si vědomi doby, kdy byla data vymazána. Proto k identifikaci, která data byla vymazána, použijeme článek Contained-IN. Jak jsem uvedl výše, dá mi to seznam záznamů, které mají verze řádků, které se staly aktivními a skončily v určeném časovém období. Poté se provede níže uvedený dotaz:

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)

Provedením výše uvedeného dotazu bylo načteno 22 řádků.

TheContained-IN klauzule vyplní řádky, které byly aktualizovány a odstraněny během daného času.

Vyplnit smazané záznamy:

Abychom naplnili odstraněné záznamy, musíme přeskočit záznamy, které byly aktualizovány během doby uvedené v klauzuli Contained-IN. Ve skriptu níže „Kde ” klauzule přeskočí produkty, které jsou přítomny v tblProduct stůl. Provedeme následující dotaz:

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)

Výše uvedený dotaz přeskočil záznamy, které byly aktualizovány; proto vrátil 13 řádků. Viz níže uvedený snímek obrazovky:

Pomocí výše uvedené metody budeme schopni získat seznam produktů, které byly smazány z tblProduct tabulka.

Vyplnit aktualizované záznamy

Abychom naplnili aktualizované záznamy, musíme přeskočit záznamy, které byly smazány během doby uvedené vContained-IN doložka. Ve skriptu níže „Kde ” klauzule bude zahrnovat produkty, které jsou přítomny v tblProduct stůl. Provedeme následující dotaz:

 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)

Výše uvedený dotaz přeskočil záznamy, které byly aktualizovány, a proto vrátil 9 řádků. Viz níže uvedený snímek obrazovky:

Pomocí výše uvedené metody budeme schopni identifikovat záznamy, které byly aktualizovány nesprávnými hodnotami, a záznamy, které byly smazány z časové tabulky.

Shrnutí

V tomto článku jsem se zabýval:

  1. Zavedení časových tabulek na vysoké úrovni.
  2. Vysvětleno, jak budou aktualizovány sloupce období prováděním dotazů DML.
  3. Ukázka k načtení seznamu produktů, které byly smazány a aktualizovány s nesprávnou cenou, z časové tabulky. Tuto zprávu lze použít pro účely auditu.

  1. Čitelné sekundární díly za rozpočet

  2. Výběr dat do pole Postgres

  3. Rozdíl mezi notací Oracle plus (+) a notací ansi JOIN?

  4. Práce s JavaFX Chart API