SQL Server 2016 въведе функция, наречена „временна таблица с версии на системата“. Използвайки нормална таблица, можете да извлечете текущи данни; докато използвате времева таблица с версия на системата, можете да извлечете данни, които са били изтрити или актуализирани в миналото. За да направите това, временна таблица ще създаде таблица с история. Таблицата на историята ще съхранява стари данни с „start_time ” и „край_време “. Което показва период от време, за който записът е бил активен.
Пример:Ако актуализирате цена на продукта от 30 на 50 чрез заявка към нормална таблица, можете да извлечете актуализираната цена на продукта, която е 50. С помощта на временна таблица можете да извлечете старата стойност, която е 30.
С помощта на времеви таблици може да се извърши:
- История на проследяване на запис :можем да прегледаме стойност на конкретния запис, която е променена с течение на времето.
- Възстановяване на ниво запис :ако сме изтрили конкретен запис от таблицата или записът е повреден, можем да го извлечем от таблицата с историята.
Времевите таблици улавят дата и час на запис въз основа на физическите дати (календарна дата) на актуализиране и изтриване на записа. Понастоящем той не поддържа версия на базата на логическите дати. Например, ако актуализирате името на продукта с помощта на оператора UPDATE в 13:00 часа, временната таблица ще поддържа историята на името на продукта до 13:00 часа. След това ще бъде приложимо ново име. Но какво ще стане, ако промяната на името на продукта трябваше да започне от 14:00 часа? Това означава, че трябва да актуализирате изявлението навреме перфектно, за да го накарате да работи и трябва да сте изпълнили оператора UPDATE в 14:00 вместо в 13:00.
Темпоралните таблици имат следните предпоставки:
- Трябва да бъде дефиниран първичен ключ.
- Трябва да бъдат дефинирани две колони за записване на началния и крайния час с типа данни datetime2. Тези колони се наричат SYSTEM_TIME колони.
Те също имат някои ограничения:
- ВМЕСТО тригери и OLTP в паметта не са разрешени.
- Таблиците с история не могат да имат никакво ограничение.
- Данните в таблицата с историята не могат да бъдат променени.
Създаване на таблица с версия на системата
Следният скрипт ще бъде използван за създаване на проста таблица с версия на системата:
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));
В горния скрипт дефинирах HISTORY_TABLE с име dbo. Product_Change_History. Ако не посочите име за таблицата на историята, SQL Server автоматично ще създаде таблица с история със следната структура.
Dbo.MSSQL_TemporalHistoryFor_xxx, където xxx е идентификационният номер на обекта.
Времевата таблица ще изглежда така, както е показано на екранната снимка по-долу:
Как ще се актуализират колоните за периоди при изпълнение на DML израз във временна таблица?
Всеки път, когато изпълняваме вмъкване, актуализиране и изтриване на заявка във временната таблица, колоните за период (SysStartDate и SysEndDate) ще бъдат актуализирани.
Вмъкване на заявка
Когато изпълняваме операцията INSERT върху временната таблица, системата задава стойността на колоната SysStartTime на началния час на текущата транзакция и маркира реда като отворен.
Нека вмъкнем няколко реда в „Продукти ’ таблица и прегледайте как данните се съхраняват в тази таблица.
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
Както е показано на горната екранна снимка, стойността на „Product_Valid_From ’ колоната е „2018-04-02 06:55:04.4865670 “, което е датата на вмъкване на ред. И стойността на „Product_Valid_To ’ колоната е „9999-12-31 23:59:59,9999999 “, което показва, че редът е отворен.
Заявка за актуализиране
Когато изпълним каквато и да е заявка за актуализиране на временната таблица, системата ще съхрани стойностите на предишния ред в таблицата с историята и ще зададе текущото време на транзакция като EndTime и актуализирайте текущата таблица с нова стойност. SysStartTime ще бъде началният час на транзакцията и SysEndTime ще бъде максимумът 9999-12-31.
Нека променим цената на продукта на „мишка ’ от 500 до 250. Ще проверим изхода на „Продукт “.
Begin tran UpdatePrice Update Prodcuts set Product_cost=200 where Product_name='Mouse' Commit tran UpdatePrice select * from Prodcuts where Product_name='Mouse'
Както можете да видите на горната екранна снимка, стойност на „Product_Valid_From “ колоната е променена. Новата стойност е текущото време на транзакцията (UTC). И стойността на „Product_Valid_To ’ колоната е ‘9999-12-31 23:59:59.9999999 “, което показва, че редът е отворен и е актуализирал цената.
Нека наблюдаваме изхода от Product_change_history таблица, като я потърсите.
select * from Product_Change_History where Product_name='Mouse'
Както можете да видите на горната екранна снимка, в Product_change_history е добавен ред таблица, която има стара версия на реда. Стойност на „Product_cost “ е 500, стойността на „Product_valid_From ’ е времето, когато записът е бил вмъкнат и стойността на Product_Valid_To колона е, когато стойността на колона Product_cost беше актуализиран. Тази версия на ред се счита за затворена.
Изтриване на заявка
Когато изтрием запис от временната таблица, системата ще съхрани текущата версия на реда в таблицата с историята и ще зададе текущото време на транзакция като EndTime и ще изтрие записа от текущата таблица.
Нека изтрием записа на „Headset“.
Begin tran DeletePrice delete from Prodcuts where product_name='Headset' Commit tran DeletePrice
Нека наблюдаваме изхода на Product_change_history таблица, като я запитате.
select * from Product_Change_History where Product_name='Headset'
Както можете да видите на горната екранна снимка, в Product_change_history е добавен ред таблица, която е изтрита от текущата таблица. Стойност на „Product_valid_From “ е времето, когато записът е бил вмъкнат и стойността на Product_Valid_To колона е времето, когато редът е бил изтрит, което показва, че версията на реда е затворена.
Промени в данните за одит за определено време
За да извършим одит на промените в данните за конкретна таблица, трябва да извършим анализ на времеви таблици. За да направим това, трябва да използваме „ЗА SYSTEM_TIME ’ клауза с по-долу специфични за времето подклаузи към данните от заявката в текущите и исторически таблици. Позволете ми да обясня изхода на заявките, използвайки различни подклаузи. По-долу е настройката:
- Вмъкнах продукт на име „Плоска шайба 8“ с каталожна цена 0,00 във времевата таблица в 09:02:25 ч.
- Промених каталожната цена в 10:13:56 ч. Нова цена е 500.00.
КАТО <Дата_Час>
Тази клауза ще се използва за извличане на състоянието на записите за определено време вКАТО ОТ подклауза. За да го разберем, нека изпълним няколко заявки:
Първо, ще изпълним заявка с помощта на КАТО НА клауза с ”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'
Сега, както можете да видите на горната екранна снимка, заявката върна един ред с актуализираната стойност на „ListPrice ” и стойност на Product_Valid_To е максималната дата.
Нека изпълним друга заявка с помощта на КАТО ОТ c lause с „SystemTime =09:10:56: “.
Сега, както можете да видите на горната екранна снимка, стойността на „ListPrice ” е 0,00.
От До
Тази клауза ще върне активните редове между
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'
Следната екранна снимка показва резултата от заявката:
МЕЖДУ И
Тази клауза е подобна на ОТ... До клауза. Единствената разлика е, че ще включва записите, които са били активни към
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'
Следната екранна снимка показва резултата от заявката:
Включено IN (, )
Тази подклауза ще включва записите, които са станали активни и приключили в рамките на посочения период от време. Не включва активните записи. За да го разберете, изпълнете заявката по-долу, като използвате „Contained IN ‘2018-04-20 09:02:25 „ до „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'
Следната екранна снимка показва резултата от заявката:
Сценарий
Една организация използва софтуер за инвентаризация. Този софтуер за инвентаризация използва таблица с продукти, която е времева таблица на системната версия. Поради грешка в приложението, няколко продукта бяха изтрити, а цените на продуктите също бяха неправилно актуализирани.
Като администратори на база данни, ние трябва да проучим този проблем и да възстановим данните, които са били неправилно актуализирани и изтрити от таблицата.
За да симулираме горния сценарий, нека създадем таблица с някои значими данни. Ще създам нова временна таблица с име „tblProduct “ в демо базата данни, която е клонинг на [Продукция].[Продукти] таблица на базата данни AdventureWorks2014.
За да изпълня горната задача, изпълних следните стъпки:
- Извлечен „скрипт за създаване на таблица“ [Производство]. [Продукти] от базата данни AdventureWorks2014.
- Премахнаха всички „ограничения и индекси“ от скрипта.
- Запази структурата на колоните непроменена.
- За да го преобразувам във временна таблица, добавих колони SysStartTime и SysEndTime.
- Активирано System_Versioning.
- Посочена таблица с история.
- Изпълни скрипта в базата данни edemo.
По-долу е скриптът:
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
Импортирах данни от продуктовата таблица на базата данни „AdventureWorks2014“ в продуктовата таблица на „DemoDatabase“, като изпълних следния скрипт:
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
Изтрих записите с имената на продукта, които започват с „Thin-Jam Hex Nut“ от tblProduct. Също така промених цената на продуктите, чиито имена започват с Плоска шайба на „tblProduct ’ таблица, като изпълните следната заявка:
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%'
Ние сме наясно с времето, когато данните са били изтрити. Следователно, за да идентифицираме какви данни са били изтрити, ще използваме подклауза Contained-IN. Както споменах по-горе, ще ми даде списък със записи, които имат версии на редове, които са станали активни и приключили в рамките на посочения период от време. След това се изпълнява следната заявка:
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)
При изпълнение на горната заявка бяха извлечени 22 реда.
Всъдържащият се В клаузата ще попълни редовете, които са били актуализирани и изтрити през определеното време.
Попълване на изтрити записи:
За да попълним изтритите записи, трябва да пропуснем записите, които са били актуализирани през времето, посочено в клаузата Contained-IN. В скрипта по-долу „Къде ” клаузата ще пропусне продуктите, които присъстват в tblProduct маса. Ще изпълним следната заявка:
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)
Горната заявка е пропуснала записите, които са били актуализирани; следователно върна 13 реда. Вижте екранната снимка по-долу:
Използвайки горния метод, ще можем да получим списъка с продукти, които са били изтрити от tblProduct таблица.
Попълване на актуализирани записи
За да попълним актуализираните записи, трябва да пропуснем записите, които са били изтрити през времето, посочено вContained-IN клауза. В скрипта по-долу „Къде ” ще включва продуктите, които присъстват в tblProduct маса. Ще изпълним следната заявка:
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)
Горната заявка е пропуснала записите, които са били актуализирани, поради което е върнала 9 реда. Вижте екранната снимка по-долу:
Използвайки горния метод, ще можем да идентифицираме записите, които са били актуализирани с грешни стойности, и записите, които са били изтрити от времевата таблица.
Резюме
В тази статия разгледах:
- Въведение на високо ниво на времеви таблици.
- Обяснено как колоните за периоди ще бъдат актуализирани чрез изпълнение на DML заявки.
- Демонстрация за извличане на списъка с продукти, който е бил изтрит и актуализиран с грешна цена, от времевата таблица. Този отчет може да се използва за целите на одита.