SQL Server 2016 wprowadził funkcję o nazwie „Tabela czasowa w wersji systemowej”. Używając normalnej tabeli, możesz pobrać aktualne dane; podczas korzystania z tabeli czasowej z wersjonowaniem systemu można odzyskać dane, które zostały usunięte lub zaktualizowane w przeszłości. Aby to zrobić, tabela czasowa utworzy tabelę historii. Tabela historii będzie przechowywać stare dane z „czas_początku ” i „czas_zakończenia ”. Co wskazuje okres czasu, przez który rekord był aktywny.
Przykład:Jeśli zaktualizujesz cenę produktu z 30 do 50, wysyłając zapytanie do normalnej tabeli, możesz pobrać zaktualizowaną cenę produktu, która wynosi 50. Używając tabeli tymczasowej, możesz pobrać starą wartość, która wynosi 30.
Korzystając z tabel czasowych można wykonać:
- Śledź historię rekordu :możemy przejrzeć wartość konkretnego rekordu, która zmieniła się w czasie.
- Odzyskiwanie na poziomie rekordu :jeśli usunęliśmy określony rekord z tabeli lub rekord jest uszkodzony, możemy go pobrać z tabeli historii.
Tabele czasowe rejestrują datę i godzinę rekordu na podstawie dat fizycznych (daty kalendarza) aktualizacji i usunięcia rekordu. Obecnie nie obsługuje wersjonowania na podstawie dat logicznych. Na przykład, jeśli zaktualizujesz nazwę produktu za pomocą instrukcji UPDATE o godzinie 13:00, tabela czasowa będzie przechowywać historię nazwy produktu do godziny 13:00. Po tym będzie obowiązywać nowa nazwa. Co jednak, jeśli zmiana nazwy produktu miała rozpocząć się od godziny 14:00? Oznacza to, że musisz idealnie zaktualizować oświadczenie na czas, aby działało, i powinieneś wykonać instrukcję UPDATE o 14:00 zamiast o 13:00.
Tabele czasowe mają następujące wymagania wstępne:
- Należy zdefiniować klucz podstawowy.
- Należy zdefiniować dwie kolumny, aby rejestrować czas rozpoczęcia i czas zakończenia z typem danych datetime2. Te kolumny są nazywane kolumnami SYSTEM_TIME.
Mają też pewne ograniczenia:
- Wyzwalacze INSTEAD OF i OLTP w pamięci są niedozwolone.
- Tabele historii nie mogą mieć żadnego ograniczenia.
- Danych w tabeli historii nie można modyfikować.
Tworzenie tabeli z wersjonowaniem systemu
Poniższy skrypt zostanie użyty do utworzenia prostej tabeli z wersjami systemowymi:
Użyj DemoDatabaseGoCREATE TABLE dbo.Prodcuts ( Product_ID int identity (1,1) klucz podstawowy , Product_Name varchar (500) , Product_Cost int , Quantity int , Product_Valid_From datetime2 NOT NULL , OKRES DLA SYSTEM_TIME (Product_Valid_From,Product_Valid_TO)) Z (SYSTEM_VERSIONING =ON (HISTORY_TABLE =dbo.Product_Change_History));
W powyższym skrypcie zdefiniowałem HISTORY_TABLE o nazwie dbo. Product_Change_Historia. Jeśli nie określisz nazwy tabeli historii, SQL Server automatycznie utworzy tabelę historii o następującej strukturze.
Dbo.MSSQL_TemporalHistoryFor_xxx, gdzie xxx to identyfikator obiektu.
Tabela czasowa będzie wyglądać tak, jak pokazano na poniższym zrzucie ekranu:
W jaki sposób zostaną zaktualizowane kolumny okresu podczas wykonywania instrukcji DML w tabeli czasowej?
Za każdym razem, gdy wykonujemy wstawianie, aktualizowanie i usuwanie zapytania w tabeli czasowej, kolumny okresu (SysStartDate i SysEndDate) zostaną zaktualizowane.
Wstaw zapytanie
Kiedy wykonujemy operację INSERT na tabeli czasowej, system ustawia wartość kolumny SysStartTime na czas rozpoczęcia bieżącej transakcji i oznacza wiersz jako otwarty.
Wstawmy kilka wierszy w „Produkty ’ i sprawdź, jak dane są przechowywane w tej tabeli.
WSTAW DO produktów (nazwa_produktu, koszt_produktu, ilość) WARTOŚCI ( 'Mysz', 500, 10 ), ( 'Klawiatura', 200, 5 ), ( 'Zestaw słuchawkowy', 500, 1 ), ( 'Laptop' , 50000, 1 ) wybierz * z produktów
Jak pokazano na powyższym zrzucie ekranu, wartość „Product_Valid_From Kolumna to „2018-04-02 06:55:04.4865670 ’, który jest datą wstawienia wiersza. Oraz wartość „Product_Valid_To” ” kolumna to „9999-12-31 23:59:59,9999999 ’, co oznacza, że wiersz jest otwarty.
Zaktualizuj zapytanie
Kiedy wykonujemy dowolne zapytanie aktualizujące w tabeli tymczasowej, system zapisze wartości poprzedniego wiersza w tabeli historii i ustawi bieżący czas transakcji jako EndTime i zaktualizuj bieżącą tabelę o nową wartość. SysStartTime będzie godzina rozpoczęcia transakcji i SysEndTime będzie maksymalny 9999-12-31.
Zmieńmy koszt produktu „Mysz ’ od 500 do 250. Sprawdzimy wynik ‘Produkt „.
Rozpocznij transakcję UpdatePriceUpdate Prodcuts set Product_cost=200 gdzie Product_name='Mysz'Potwierdź trans UpdatePriceselect * z Prodcuts gdzie Product_name='Mysz'
Jak widać na powyższym zrzucie ekranu, wartość „Product_Valid_From Kolumna została zmieniona. Nowa wartość to aktualny czas transakcji (UTC). Oraz wartość „Product_Valid_To” ” to kolumna „9999-12-31 23:59:59,9999999 ”, co oznacza, że wiersz jest otwarty i zaktualizował cenę.
Przyjrzyjmy się wynikom Product_change_history tabeli, wysyłając zapytanie.
wybierz * z Product_Change_History, gdzie Product_name='Mysz'
Jak widać na powyższym zrzucie ekranu, wiersz został dodany w Historia_zmiany_produktu tabela, która ma starą wersję wiersza. Wartość „Koszt_produktu ” to 500, wartość „Product_valid_From ’ to czas wstawienia rekordu i wartość Product_Valid_To kolumna to wartość kolumny Product_cost został zaktualizowany. Ta wersja wiersza jest uważana za zamkniętą.
Usuń zapytanie
Kiedy usuniemy rekord z tabeli tymczasowej, system przechowa bieżącą wersję wiersza w tabeli historii i ustawi bieżący czas transakcji jako EndTime i usunie rekord z bieżącej tabeli.
Usuńmy rekord „zestawu słuchawkowego”.
Rozpocznij usuwanie DeletePrice z produktów, gdzie product_name='Headset'Zatwierdź trans DeletePrice
Przyjrzyjmy się wynikom Product_change_history tabeli, wysyłając zapytanie.
wybierz * z Product_Change_History, gdzie Product_name='Zestaw słuchawkowy'
Jak widać na powyższym zrzucie ekranu, wiersz został dodany w Historia_zmiany_produktu tabela, która została usunięta z bieżącej tabeli. Wartość „Produkt_ważny_Od ’ to czas wstawienia rekordu i wartość Product_Valid_To kolumna to czas, w którym wiersz został usunięty, co oznacza, że wersja wiersza jest zamknięta.
Kontrola zmian danych w określonym czasie
Aby przeprowadzić audyt zmian danych dla określonej tabeli, powinniśmy przeprowadzić analizę czasową tabel czasowych. Aby to zrobić, musimy użyć „FOR SYSTEM_TIME ’ klauzula z poniższymi podklauzulami specyficznymi dla czasu do danych zapytania w tabelach bieżących i historycznych. Pozwólcie, że wyjaśnię wynik zapytań przy użyciu różnych podrozdziałów. Poniżej znajduje się konfiguracja:
- Wstawiłem produkt o nazwie „Płaska podkładka 8” z ceną katalogową 0,00 w tabeli czasowej o 09:02:25.
- Zmieniłem cenę katalogową o 10:13:56. Nowa cena to 500,00.
OD
Ta klauzula zostanie użyta do pobrania stanu rekordów dla danego czasu w AS OF podpunkt. Aby to zrozumieć, wykonajmy kilka zapytań:
Najpierw wykonamy zapytanie za pomocą AS OF klauzula z „SystemTime =10:15:59 ”.
wybierz Name, ListPrice,rowguid,Product_Valid_From,Product_Valid_TO z DemoDatabase.dbo.tblProduct FOR system_time na dzień „2018-04-20 10:15:56where name =„Płaska podkładka 8”
Teraz, jak widać na powyższym zrzucie ekranu, zapytanie zwróciło jeden wiersz ze zaktualizowaną wartością „ListPrice ” i wartość Product_Valid_To to maksymalna data.
Wykonajmy kolejne zapytanie, używając AS OF c lause z „SystemTime =09:10:56: ”.
Teraz, jak widać na powyższym zrzucie ekranu, wartość „ListPrice ” to 0,00.
Od do
Ta klauzula zwróci wiersze aktywne między
wybierz Name, ListPrice,rowguid,Product_Valid_From,Product_Valid_TO,ListPrice z DemoDatabase.dbo.tblProduct FOR system_time od „2018-04-20 09:02:25 do „2018-04-20 10:13:56 gdzie nazwa =„Płaska podkładka 8”
Poniższy zrzut ekranu przedstawia wynik zapytania:
POMIĘDZY i
Ta klauzula jest podobna do OD... Do klauzula. Jedyną różnicą jest to, że będzie zawierał rekordy, które były aktywne w
wybierz Name, ListPrice,rowguid,Product_Valid_From,Product_Valid_TO,ListPrice z DemoDatabase.dbo.tblProduct FOR system_time między „2018-04-20 09:02:25.1265684” a „2018-04-20 10:13:56.1265684” gdzie name =„Płaska podkładka 8”
Poniższy zrzut ekranu przedstawia wynik zapytania:
Wliczone w (, )
Ta podklauzula będzie zawierać rekordy, które stały się aktywne i zakończyły się w określonym zakresie dat. Nie obejmuje aktywnych rekordów. Aby to zrozumieć, wykonaj poniższe zapytanie, używając „Zawarte w ‘2018-04-20 09:02:25 ‘ do ‘2018-04-20 10:14:56’ ”
Poniższy zrzut ekranu przedstawia wynik zapytania:
Scenariusz
Organizacja korzysta z oprogramowania do inwentaryzacji. To oprogramowanie inwentaryzacyjne wykorzystuje tabelę produktów, która jest tabelą czasową wersji systemu. Z powodu błędu aplikacji kilka produktów zostało usuniętych, a ceny produktów również niewłaściwie zaktualizowane.
Jako administratorzy baz danych musimy zbadać ten problem i odzyskać dane, które zostały błędnie zaktualizowane i usunięte z tabeli.
Aby zasymulować powyższy scenariusz, stwórzmy tabelę z pewnymi znaczącymi danymi. Zamierzam utworzyć nową tabelę czasową o nazwie „tblProduct ’ w bazie danych Demo, która jest klonem [Produkcja].[Produkty] tabela bazy danych AdventureWorks2014.
Aby wykonać powyższe zadanie, wykonałem poniższe kroki:
- Wyodrębnione „utwórz skrypt tabeli” [Produkcja]. [Produkty] z bazy danych AdventureWorks2014.
- Usunięto wszystkie „ograniczenia i indeksy” ze skryptu.
- Zachowaj niezmienioną strukturę kolumn.
- Aby przekonwertować go na tabelę czasową, dodałem kolumny SysStartTime i SysEndTime.
- Włączone System_Versioning.
- Określona tabela historii.
- Wykonał skrypt w bazie danych edemo.
Poniżej znajduje się skrypt:
USE [DemoDatabase] GOCREATE TABLE [tblProduct]( [ProductID] [int] IDENTITY (1,1) Klucz podstawowy, [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, [Waga] [dziesiętny](8, 2) NULL, [DaysToManufacture] [int] NOT NULL, [ProductLine] [nchar](2) NULL, [Klasa] [nchar](2) NULL, [Styl] [nchar](2) NULL, [ProductSubcategoryID] [int] NULL, [ProductModelID] [int] NULL , [SellStartDate] [datetime] NOT NULL, [SellEndDate] [datetime] NULL, [DiscontinuedDate] [datetime] NULL, [rowguid] [unikalny identyfikator] ROWGUIDCOL NOT NULL, [ModifiedDate] [datetime] NOT NULL, Product_Valid_From datetime2 ZAWSZE GENEROWANE ROW START NOT NULL , Product_Valid_TO datetime2 GENEROWANY ZAWSZE JAKO ROW END NOT NULL , OKRES DLA S YSTEM_TIME (Product_Valid_From,Product_Valid_TO) )Z (SYSTEM_VERSIONING =ON (HISTORY_TABLE =dbo.Product_History));GO
Zaimportowałem dane z tabeli produktów bazy danych „AdventureWorks2014” do tabeli produktów „DemoDatabase”, wykonując następujący skrypt:
wstaw w DemoDatabase.dbo.tblProdukt(Nazwa,Numer produktu,Kolor,BezpieczeństwoPoziom zapasów,ReorderPoint,Standardowy koszt,ListCena,Rozmiar,RozmiarJednostkaKod pomiaru,Waga,Dni produkcji,Produkt,Klasa,Styl,ProduktRozpoczęcieID,SprzedażDedModel,Date ,rowguid,Data modyfikacji)select top 50Nazwa,Numer produktu,Kolor,Poziom bezpieczeństwa,Punkt zmiany kolejności,Koszt standardowy,Cena na liście,Rozmiar,RozmiarJednostkaKod miary,WagaJednostkaKod miary,Waga,Dni produkcji,Linia produktów,Klasa,Styl,ID podkategorii produktu,Data daty produktu,Dane,Sprzedaż AdventureWorks2014.Produkcja.Produkt
Usunąłem rekordy nazwy produktu, które zaczynają się od „Thin-Jam Hex Nut” z tblProduct. Zmieniłem również cenę produktów, których nazwy zaczynają się od Flat Washer w „tblProduct ’, wykonując następujące zapytanie:
usuń z DemoDatabase.dbo.Produkt, gdzie nazwa taka jak '%Thin-Jam Hex Nut%'czekaj na opóźnienie '00:01:00'zaktualizuj DemoDatabase.dbo.tblZestaw produktów ListPrice=500.00 gdzie nazwa taka jak '%Flat Washer%'
Mamy świadomość, kiedy dane zostały usunięte. Stąd w celu zidentyfikowania, jakie dane zostały usunięte, posłużymy się podpunktem Zawarte-IN. Jak wspomniałem powyżej, otrzymam listę rekordów, które mają wersje wierszy, które stały się aktywne i zakończyły się w określonym zakresie dat. Następnie wykonaj poniższe zapytanie:
declare @StartDateTime datetimedeclare @EndDateTime datetimeset @StartDateTime=convert (datetime2, getdate()-1)set @EndDateTime=convert (datetime2, getdate())select ProductID, Name, ProductNumber, Product_Valid_From, Product_Valid_To z produktu dla SYSTEM_TIME zawarte IN ( @StartDateTime , @EndDateTime)
Wykonując powyższe zapytanie, pobrano 22 wiersze.
Zawarty-IN klauzula wypełni wiersze, które zostały zaktualizowane i usunięte w określonym czasie.
Wypełnij usunięte rekordy:
Aby wypełnić usunięte rekordy, musimy pominąć rekordy, które zostały zaktualizowane w czasie określonym w klauzuli Contained-IN. W poniższym skrypcie „Gdzie ” klauzula pominie produkty, które są obecne w tblProduct stół. Wykonamy następujące zapytanie:
declare @StartDateTime datetimedeclare @EndDateTime datetimeset @StartDateTime=convert(datetime2,getdate()-1)set @EndDateTime=convert(datetime2,getdate())select ProductID, Name, ProductNumber, Product_Valid_From, Product_Valid_To z tblProduct Contained For SYSTEM_TIME IN ( @StartDateTime , @EndDateTime) Gdzie nie ma nazwy (wybierz nazwę z tblProduct)
Powyższe zapytanie pominęło rekordy, które zostały zaktualizowane; stąd zwrócił 13 wierszy. Zobacz poniższy zrzut ekranu:
Korzystając z powyższej metody, będziemy mogli uzyskać listę produktów, które zostały usunięte z tblProduct tabela.
Wypełnij zaktualizowane rekordy
Aby wypełnić zaktualizowane rekordy, musimy pominąć rekordy, które zostały usunięte w czasie określonym w Zawarte-IN klauzula. W poniższym skrypcie „Gdzie ” klauzula będzie obejmować produkty, które są obecne w tblProduct stół. Wykonamy następujące zapytanie:
define @StartDateTime datetimedeclare @EndDateTime datetimeset @StartDateTime=convert(datetime2,getdate()-1)set @EndDateTime=convert(datetime2,getdate())select ProductID, Name, ProductNumber, Product_Valid_From, Product_Valid_To z tblProduct dla SYSTEM_TIME zawarte IN ( @StartDateTime , @EndDateTime) Gdzie Nazwa w (Wybierz nazwę z tblProduct)
Powyższe zapytanie pominęło rekordy, które zostały zaktualizowane, stąd zwróciło 9 wierszy. Zobacz poniższy zrzut ekranu:
Korzystając z powyższej metody, będziemy w stanie zidentyfikować rekordy, które zostały zaktualizowane o nieprawidłowe wartości oraz rekordy, które zostały usunięte z tabeli czasowej.
Podsumowanie
W tym artykule omówiłem:
- Wprowadzenie wysokiego poziomu tabel czasowych.
- Wyjaśniono, w jaki sposób kolumny okresu będą aktualizowane przez wykonywanie zapytań DML.
- Demo do pobrania listy produktów, które zostały usunięte i zaktualizowane z niewłaściwą ceną, z tabeli tymczasowej. Ten raport może być wykorzystany do celów audytu.