SQL Server 2016 đã giới thiệu một tính năng được gọi là 'Bảng tạm thời được phiên bản hệ thống'. Sử dụng bảng bình thường, bạn có thể truy xuất dữ liệu hiện tại; trong khi sử dụng bảng tạm thời được tạo phiên bản hệ thống, bạn có thể truy xuất dữ liệu đã bị xóa hoặc cập nhật trong quá khứ. Để làm điều đó, một bảng thời gian sẽ tạo ra một bảng lịch sử. Bảng lịch sử sẽ lưu trữ dữ liệu cũ với “ start_time ”Và“ end_time ”. Điều này cho biết khoảng thời gian mà bản ghi đã hoạt động.
Ví dụ:Nếu bạn cập nhật giá sản phẩm từ 30 lên 50 bằng cách truy vấn bảng thông thường, bạn có thể truy xuất giá sản phẩm đã cập nhật là 50. Sử dụng bảng tạm thời, bạn có thể truy xuất giá trị cũ là 30.
Sử dụng bảng thời gian, người ta có thể thực hiện:
- Theo dõi lịch sử của một bản ghi :chúng tôi có thể xem lại giá trị của bản ghi cụ thể, giá trị này đã được thay đổi theo thời gian.
- Khôi phục mức bản ghi :nếu chúng tôi đã xóa một bản ghi cụ thể khỏi bảng hoặc một bản ghi bị hỏng, chúng tôi có thể truy xuất bản ghi đó từ bảng lịch sử.
Bảng tạm thời ghi lại ngày-giờ của một bản ghi dựa trên các ngày thực tế (Lịch ngày) của việc cập nhật và xóa bản ghi. Hiện tại, nó không hỗ trợ lập phiên bản dựa trên ngày tháng hợp lý. Ví dụ:nếu bạn cập nhật tên sản phẩm bằng cách sử dụng câu lệnh CẬP NHẬT lúc 1 giờ chiều, bảng hơn thời gian sẽ duy trì lịch sử của tên sản phẩm cho đến 1 giờ chiều. Sau đó, một tên mới sẽ được áp dụng. Tuy nhiên, điều gì sẽ xảy ra nếu việc thay đổi tên sản phẩm bắt đầu từ 2:00 chiều? Điều này có nghĩa là bạn phải cập nhật câu lệnh đúng giờ một cách hoàn hảo để nó hoạt động và lẽ ra bạn nên thực hiện câu lệnh UPDATE lúc 2:00 chiều thay vì 1:00 chiều.
Bảng tạm thời có các điều kiện tiên quyết sau:
- Một khóa chính phải được xác định.
- Hai cột phải được xác định để ghi lại thời gian bắt đầu và thời gian kết thúc với kiểu dữ liệu datetime2. Các cột này được gọi là cột SYSTEM_TIME.
Chúng cũng có một số hạn chế:
- INSTEAD OF trigger và OLTP trong bộ nhớ không được phép.
- Các bảng lịch sử không được có bất kỳ ràng buộc nào.
- Không thể sửa đổi dữ liệu trong bảng lịch sử.
Tạo bảng được tạo phiên bản hệ thống
Tập lệnh sau sẽ được sử dụng để tạo một bảng có phiên bản hệ thống đơn giản:
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));
Trong tập lệnh trên, tôi đã xác định HISTORY_TABLE có tên là dbo. Product_Change_History. Nếu bạn không chỉ định tên cho bảng lịch sử, SQL Server sẽ tự động tạo bảng lịch sử với cấu trúc sau.
Dbo.MSSQL_TemporalHistoryFor_xxx, trong đó xxx là ID đối tượng.
Bảng thời gian sẽ giống như được hiển thị trong ảnh chụp màn hình bên dưới:
Các cột khoảng thời gian sẽ được cập nhật như thế nào khi thực thi câu lệnh DML trên Bảng tạm thời?
Bất cứ khi nào chúng tôi thực hiện chèn, cập nhật và xóa một truy vấn trên bảng tạm thời, các cột chu kỳ (SysStartDate và SysEndDate) sẽ được cập nhật.
Chèn truy vấn
Khi chúng tôi thực hiện thao tác INSERT trên bảng tạm thời, hệ thống sẽ đặt giá trị của cột SysStartTime thành thời gian bắt đầu của giao dịch hiện tại và đánh dấu hàng là đang mở.
Hãy chèn một số hàng trong ‘ Sản phẩm 'Và xem lại cách dữ liệu được lưu trữ trong bảng này.
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
Như được hiển thị trong ảnh chụp màn hình ở trên, giá trị của ‘ Product_Valid_From 'Là cột ‘ 2018-04-02 06:55:04.4865670 'Là ngày chèn hàng. Và giá trị của ‘ Product_Valid_To 'Là cột ‘ 9999-12-31 23:59:59.9999999 ', Chỉ ra rằng hàng đang mở.
Cập nhật truy vấn
Khi chúng tôi thực hiện bất kỳ truy vấn cập nhật nào trên bảng tạm thời, hệ thống sẽ lưu trữ các giá trị hàng trước đó trong bảng lịch sử và đặt thời gian giao dịch hiện tại là EndTime và cập nhật bảng hiện tại với một giá trị mới. SysStartTime sẽ là thời gian bắt đầu giao dịch và SysEndTime sẽ là tối đa 9999-12-31.
Hãy thay đổi Giá thành sản phẩm của ‘ Chuột ’Từ 500 đến 250. Chúng tôi sẽ kiểm tra đầu ra của‘ Sản phẩm '.
Begin tran UpdatePrice Update Prodcuts set Product_cost=200 where Product_name='Mouse' Commit tran UpdatePrice select * from Prodcuts where Product_name='Mouse'
Như bạn có thể thấy trong ảnh chụp màn hình ở trên, giá trị của ‘ Product_Valid_From 'Đã được thay đổi. Giá trị mới là thời gian giao dịch hiện tại (UTC). Và giá trị của ‘ Product_Valid_To ’Là ‘ 9999-12-31 23:59:59.9999999 ', Cho biết rằng hàng đang mở và đã cập nhật giá.
Hãy quan sát đầu ra của Product_change_history bảng bằng cách truy vấn nó.
select * from Product_Change_History where Product_name='Mouse'
Như bạn có thể thấy trong ảnh chụp màn hình ở trên, một hàng đã được thêm vào Product_change_history bảng, có phiên bản cũ của hàng. Giá trị của ‘ Product_cost ’Là 500, Giá trị của‘ Product_valid_From ’Là thời điểm bản ghi được chèn và giá trị của Product_Valid_To là giá trị của cột Product_cost đã được cập nhật. Phiên bản hàng này được coi là đã đóng.
Xóa truy vấn
Khi chúng tôi xóa bản ghi khỏi bảng tạm thời, hệ thống sẽ lưu trữ phiên bản hiện tại của hàng trong bảng lịch sử và đặt thời gian giao dịch hiện tại là Thời gian kết thúc và xóa bản ghi khỏi bảng hiện tại.
Hãy xóa bản ghi của 'Tai nghe'.
Begin tran DeletePrice delete from Prodcuts where product_name='Headset' Commit tran DeletePrice
Hãy quan sát kết quả đầu ra của Product_change_history bảng bằng cách truy vấn nó.
select * from Product_Change_History where Product_name='Headset'
Như bạn có thể thấy trong ảnh chụp màn hình ở trên, một hàng đã được thêm vào Product_change_history bảng, đã bị xóa khỏi bảng hiện tại. Giá trị của ‘ Product_valid_From ’Là thời điểm bản ghi được chèn và giá trị của Product_Valid_To là thời gian hàng bị xóa, điều này cho biết rằng phiên bản hàng đã đóng.
Dữ liệu kiểm toán thay đổi trong một thời gian cụ thể
Để kiểm tra các thay đổi dữ liệu cho một bảng cụ thể, chúng ta nên thực hiện phân tích dựa trên thời gian của các bảng tạm thời. Để làm điều đó, chúng tôi phải sử dụng ‘ FOR SYSTEM_TIME 'Mệnh đề với các mệnh đề phụ theo thời gian cụ thể bên dưới cho dữ liệu truy vấn trên bảng hiện tại và bảng lịch sử. Hãy để tôi giải thích kết quả đầu ra của các truy vấn bằng cách sử dụng các mệnh đề phụ khác nhau. Dưới đây là thiết lập:
- Tôi đã chèn một sản phẩm có tên 'Máy giặt phẳng 8' với Giá niêm yết 0,00 vào bảng thời gian lúc 09:02:25 sáng.
- Tôi đã thay đổi Giá niêm yết lúc 10:13:56 sáng. Giá mới là 500.00.
AS OF
Mệnh đề này sẽ được sử dụng để truy xuất trạng thái của bản ghi trong một thời gian nhất định trong AS OF mục con. Để hiểu nó, hãy thực hiện một số truy vấn:
Đầu tiên, chúng tôi sẽ thực hiện một truy vấn bằng cách sử dụng AS OF mệnh đề với ” 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'
Bây giờ, như bạn có thể thấy trong ảnh chụp màn hình ở trên, truy vấn đã trả về một hàng với giá trị được cập nhật là “ ListPrice ”Và giá trị của Product_Valid_To là ngày tối đa.
Hãy thực hiện một truy vấn khác bằng cách sử dụng AS OF c lause với “ SystemTime =09:10:56: ”.
Bây giờ, như bạn có thể thấy trong ảnh chụp màn hình ở trên, giá trị của “ ListPrice ”Là 0,00.
Từ Đến
Mệnh đề này sẽ trả về các hàng hoạt động trong khoảng thời gian
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'
Ảnh chụp màn hình sau đây cho thấy kết quả truy vấn:
BETWEEN Và
Mệnh đề này tương tự như FROM .. Tới mệnh đề. Sự khác biệt duy nhất là nó sẽ bao gồm các bản ghi đã hoạt động vào
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'
Ảnh chụp màn hình sau đây cho thấy kết quả truy vấn:
Bao gồm IN (, )
Điều khoản phụ này sẽ bao gồm các bản ghi đã bắt đầu hoạt động và kết thúc trong phạm vi ngày được chỉ định. Nó không bao gồm các bản ghi đang hoạt động. Để hiểu nó, hãy thực thi truy vấn bên dưới bằng cách sử dụng “ Chứa trong‘ 2018-04-20 09:02:25 ‘ đến‘ 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'
Ảnh chụp màn hình sau đây cho thấy kết quả truy vấn:
Tình huống
Một tổ chức đang sử dụng một phần mềm kiểm kê. Phần mềm kiểm kê đó sử dụng bảng sản phẩm là bảng tạm thời của phiên bản hệ thống. Do lỗi ứng dụng, một số sản phẩm đã bị xóa và giá của các sản phẩm cũng bị cập nhật sai.
Với tư cách là DBA, chúng tôi phải điều tra vấn đề này và khôi phục dữ liệu đã bị cập nhật sai và bị xóa khỏi bảng.
Để mô phỏng tình huống trên, hãy tạo một bảng với một số dữ liệu có ý nghĩa. Tôi sẽ tạo một bảng tạm thời mới có tên là ‘ tblProduct 'Trên cơ sở dữ liệu Demo là bản sao của [Sản xuất]. [Sản phẩm] bảng của cơ sở dữ liệu AdventureWorks2014.
Để thực hiện tác vụ trên, tôi đã làm theo các bước sau:
- Đã trích xuất “tập lệnh tạo bảng” [Sản xuất]. [Sản phẩm] từ cơ sở dữ liệu AdventureWorks2014.
- Đã xóa tất cả "các ràng buộc và chỉ mục" khỏi tập lệnh.
- Giữ cấu trúc cột không thay đổi.
- Để chuyển đổi nó thành một bảng tạm thời, tôi đã thêm các cột SysStartTime và SysEndTime.
- Cấp phép Hệ thống đã bật.
- Bảng lịch sử đã chỉ định.
- Đã thực thi tập lệnh trên cơ sở dữ liệu edemo.
Dưới đây là tập lệnh:
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
Tôi đã nhập dữ liệu từ bảng sản phẩm của cơ sở dữ liệu “AdventureWorks2014” vào bảng sản phẩm của “DemoDatabase” bằng cách thực thi tập lệnh sau:
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
Tôi đã xóa các bản ghi tên sản phẩm bắt đầu bằng ‘Thin-Jam Hex Nut’ khỏi tblProduct. Tôi cũng đã thay đổi giá của các sản phẩm mà tên whtihc bắt đầu bằng Máy giặt phẳng trên ‘ tblProduct 'Bằng cách thực hiện truy vấn sau:
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%'
Chúng tôi biết thời điểm dữ liệu bị xóa. Do đó, để xác định, dữ liệu nào đã bị xóa, chúng ta sẽ sử dụng mệnh đề phụ Contained-IN. Như tôi đã đề cập ở trên, nó sẽ cung cấp cho tôi danh sách các bản ghi có phiên bản hàng đã bắt đầu hoạt động và kết thúc trong phạm vi ngày được chỉ định. Sau đó, thực thi truy vấn bên dưới:
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)
Bằng cách thực hiện truy vấn trên, 22 hàng đã được truy xuất.
Chứa trong mệnh đề sẽ điền các hàng đã được cập nhật và xóa trong thời gian nhất định.
Điền các bản ghi đã xóa:
Để điền các bản ghi đã xóa, chúng ta phải bỏ qua các bản ghi đã được cập nhật trong thời gian được chỉ định trong mệnh đề Contained-IN. Trong tập lệnh bên dưới, " Ở đâu ”Mệnh đề sẽ bỏ qua các sản phẩm có trong tblProduct bàn. Chúng tôi sẽ thực hiện truy vấn sau:
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)
Truy vấn trên đã bỏ qua các bản ghi đã được cập nhật; do đó nó trả về 13 hàng. Xem ảnh chụp màn hình bên dưới:
Bằng cách sử dụng phương pháp trên, chúng tôi sẽ có thể nhận được danh sách các sản phẩm đã bị xóa khỏi tblProduct bảng.
Điền các bản ghi đã cập nhật
Để điền các bản ghi đã cập nhật, chúng tôi phải bỏ qua các bản ghi đã bị xóa trong thời gian được chỉ định trong Contained-IN mệnh đề. Trong tập lệnh bên dưới, " Ở đâu ”Điều khoản sẽ bao gồm các sản phẩm có trong tblProduct bàn. Chúng tôi sẽ thực hiện truy vấn sau:
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)
Truy vấn trên đã bỏ qua các bản ghi đã được cập nhật do đó nó trả về 9 hàng. Xem ảnh chụp màn hình bên dưới:
Sử dụng phương pháp trên, chúng tôi sẽ có thể xác định các bản ghi đã được cập nhật với giá trị sai và các bản ghi đã bị xóa khỏi bảng tạm thời.
Tóm tắt
Trong bài viết này, tôi đã đề cập đến:
- Giới thiệu cấp cao về bảng thời gian.
- Giải thích, cách các cột khoảng thời gian sẽ được cập nhật bằng cách thực hiện các truy vấn DML.
- Bản demo để truy xuất danh sách các sản phẩm đã bị xóa và cập nhật sai giá từ bảng tạm thời. Báo cáo này có thể được sử dụng cho mục đích kiểm toán.