SQL Server 2016 introduces a new type of table that is designed to keep the full history of data changes, where row validity is managed by the system. This new table type is called a System-Versioned Temporal Table. In earlier SQL Server versions, user tables would enable you to hold only the most recent copy of the row, without being able to query the value before the UPDATE or DELETE operations. Using a Temporal Table, you will be able to query the recent state of the row as usual, in addition to the ability to query the full history of that row, which is fully managed by the SQL Server Engine, as you can’t define the rows validity period explicitly.
SQL Server 2016引入了一种新类型的表,该表旨在保留数据更改的完整历史记录,其中行有效性由系统管理。 这种新的表类型称为系统版本的时态表 。 在早期SQL Server版本中,用户表使您只能保存该行的最新副本,而不必在UPDATE或DELETE操作之前查询该值。 使用临时表,除了可以查询该行的完整历史记录(由SQL Server Engine完全管理)之外,您还可以照常查询该行的最新状态,因为您无法定义行有效期明确。
Each System-Versioned Temporal Table should contain two datetime period columns that are explicitly defined in the table; the Period Start column that will be used by the system to store the start time of the row, and the Period End column in which the system will store the end time of the row.
每个系统版本的时间表应包含两个在表中明确定义的日期时间段列; 系统将使用“ Period Start”列来存储行的开始时间,以及“ Period End”列,系统将在其中存储行的结束时间。
The main Temporal table will keep only the current version of the table rows, but it will be linked automatically to another table with the same exact schema, in which the previous versions of the modified rows will be stored automatically, this table is called a History table. When you create a Temporal table, the History table will be created automatically. If you don’t specify the history table name as a parameter in the SYSTEM_VERSIONING clause, it will be named with the naming pattern MSSQL_TemporalHistoryFor_<object_id>. Specifying the history table name while creating the Temporal table will allow the system to create the history table with the default system configuration, with the ability to create your own history table and provide its name in the Temporal table creation statement. Take into consideration that no changes can be performed on the history table’s data. If you try to delete any row from the history table, you will get the (Cannot delete rows from a temporal history table ‘<tableName>’) error, as it is only managed by the system.
主Temporal表将仅保留表行的当前版本,但是它将自动链接到具有相同精确模式的另一个表,在该表中,已修改的行的先前版本将自动存储在该表中,该表称为“ 历史记录”表。 创建临时表时,将自动创建历史表。 如果未在SYSTEM_VERSIONING子句中将历史记录表名称指定为参数,则将使用命名模式MSSQL_TemporalHistoryFor_ <object_id>对其进行命名。 在创建临时表时指定历史表名称将允许系统使用默认的系统配置创建历史表,并能够创建自己的历史表并在临时表创建语句中提供其名称。 考虑到历史记录表的数据不能执行任何更改。 如果尝试从历史记录表中删除任何行,则会收到(无法从时间历史记录表'<tableName>'中删除行)错误,因为它仅由系统管理。
When a new row is inserted, the system will assign the Period Start time column with the current transaction start time-based on the system clock, and assign the Period End time column with the maximum datetime2 value equal to 9999-12-31, and the row will be opened. If an existing row is updated, the system will copy the previous version of the row in the history table and fill the Period End time column with the current transaction start time based on the system clock, and the row will be closed. The Period End time column value of the main temporal table will be 9999-12-31 without any change.
插入新行时,系统将基于系统时钟为“周期开始时间”列分配当前交易开始时间,并为“周期结束时间”列分配最大datetime2值等于9999-12-31,并且该行将被打开。 如果现有行已更新 ,则系统将复制历史记录表中该行的先前版本,并根据系统时钟用当前交易开始时间填充“周期结束时间”列,然后该行将关闭。 主时间表的“周期结束时间”列的值将为9999-12-31,而无需进行任何更改。
Deleting an existing row, the row will be stored in the history table, with the Period End time column value equal to the current transaction start time based on the system clock, and the row will be closed after deleting it from the main temporal table.
删除现有行后,该行将存储在历史记录表中,“ Period End time”列的值等于基于系统时钟的当前交易开始时间,并且从主时态表中删除该行后,该行将关闭。
System-Versioning can be enabled when a table is created using the CREATE TABLE statement or after creating the table using the ALTER DATABASE statement. In order to enable System-Versioning in a table, it must have a primary key, with two not-nullable datetime2 period columns defined as GENERATED ALWAYS AS ROW START or END, passed as parameters in the PERIOD FOR SYSTEM_TIME within the table definition.
使用CREATE TABLE语句创建表时或使用ALTER DATABASE语句创建表后,可以启用系统版本控制。 为了在表中启用系统版本控制,它必须具有主键,并且将两个不可为空的datetime2期间列定义为GENERATED ALWAYS AS ROW START或END,并作为参数在表定义的PERIOD FOR SYSTEM_TIME中传递。
The below CREATE TABLE statement is used to create the Temporal_Table_Demo temporal table, with DempID primary key, the two-period columns; TimeStart GENERATED ALWAYS AS ROW START and TimeEnd GENERATED ALWAYS AS ROW END, both designated as PERIOD FOR SYSTEM_TIME, the temporal table is linked to the dbo.Temporal_Table_Demo_History history table, taking into consideration that identifying the history table schema name is mandatory, and finally enabling the SYSTEM_VERSIONING feature as follows:
下面的CREATE TABLE语句用于创建Temporal_Table_Demo时态表,该表具有DempID主键,两个时期的列; 将时态表链接到dbo.Temporal_Table_Demo_History历史记录表,同时考虑到必须确定历史记录表架构名称,并最终启用SYSTEM_VERSIONING功能如下:
USE SQLShackDemo
GO
CREATE TABLE dbo.Temporal_Table_Demo
(
[DempID] int NOT NULL PRIMARY KEY CLUSTERED
, [DepName] nvarchar(100) NOT NULL
, [DepLocation] varchar(100) NOT NULL
, [NumOfEmp] int NOT NULL
, [TimeStart] datetime2 (2) GENERATED ALWAYS AS ROW START
, [TimeEnd] datetime2 (2) GENERATED ALWAYS AS ROW END
, PERIOD FOR SYSTEM_TIME (TimeStart, TimeEnd)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Temporal_Table_Demo_History));
Browse the new temporal table from the Object Explorer, where you will see a special icon with a small clock (see below image) for the temporal table with System-Versioned between the parentheses. The history table will not have a separate table node; it can be shown by expanding the temporal table with a node under the main table identified with History between parentheses. A default clustered index will be created for the history table that contains the two Period Start and Period End columns as below:
从对象资源管理器中浏览新的时态表,您将在时态表中看到一个带有小时钟的特殊图标(请参见下图),括号之间为“ 系统版本 ”。 历史记录表将没有单独的表节点; 可以通过在主表下面的节点上用圆括号之间的“ 历史记录”标识的节点来扩展时间表来显示它。 将为包含两个Period Start和Period End列的历史记录表创建默认的聚集索引,如下所示:
Let’s assume that we need to enable System-Versioning on an existing table in our database. To do that, two Period Start and Period End not-nullable datetime2 columns should be added with all specs specified previously as in the below script:
假设我们需要在数据库中的现有表上启用系统版本控制。 为此,应添加两个“ Period Start”和“ Period End”不可为空的datetime2列,并使用以下脚本中先前指定的所有规范:
ALTER TABLE dbo.AWBuildVersion ADD
[TimeStart] DATETIME2(0) GENERATED ALWAYS AS ROW START NOT NULL CONSTRAINT DFT_AWBuildVersion_TimeStart DEFAULT ('19000101'),
[TimeEnd] DATETIME2(0) GENERATED ALWAYS AS ROW END NOT NULL CONSTRAINT DFT_AWBuildVersion_TimeEnd DEFAULT ('99991231 23:59:59'),
PERIOD FOR SYSTEM_TIME ([TimeStart], [TimeEnd]);
ALTER TABLE dbo.AWBuildVersion
DROP CONSTRAINT DFT_AWBuildVersion_TimeStart, DFT_AWBuildVersion_TimeEnd;
ALTER TABLE dbo.AWBuildVersion SET ( SYSTEM_VERSIONING = ON ( HISTORY_TABLE = dbo.AWBuildVersion_History ) );
In the previous script, the default constraints are defined as the period columns should be not-nullable and then dropped before enabling System_Versioning as the SQL Engine will be responsible for filling these two columns. The SQL Server Engine will perform a consistency check on both the temporal and history tables to verify that all schema requirements are applied before enabling System_Versioning. This includes checking that the Period End time is greater than the Period Start time and that the periods of the different versions of the same row will not overlap. The consistency check is enabled by default and can be disabled using the DATA_CONSISTENCY_CHECK = OFF statement in the System_Versioning enable part. Now the AWBuildVersion table is defined as a temporal table successfully:
在上一个脚本中,默认约束定义为period列应为不可为空,然后在启用System_Versioning之前将其删除,因为SQL引擎将负责填充这两列。 SQL Server引擎将在临时表和历史记录表上执行一致性检查,以验证在启用System_Versioning之前是否应用了所有架构要求。 这包括检查“期间结束时间”是否大于“期间开始时间”,以及同一行的不同版本的期间是否重叠。 一致性检查默认情况下处于启用状态,可以使用System_Versioning启用部分中的DATA_CONSISTENCY_CHECK = OFF语句禁用一致性检查。 现在,AWBuildVersion表已成功定义为临时表:
Enabling System_Versioning on the table will not prevent you from modifying the table schema. The system will hold a schema lock on both the temporal and history table, where these changes will be replicated to the history table. A specific schema change can’t be performed directly using an ALTER DATABASE statement on a table with System_Versioning enabled, such as adding or removing an IDENTITY, COMPUTED, SPARSE, COLUMN_SET or ROWGUIDCOL columns. To apply such changes on a table with System_Versioning enabled, you need first to disable System_Versioning, perform the change you need then enable the System_Versioning again. The below script is used to add a new IDENTITY column to the Temporal_Table_Demo temporal table (we will not consider this added column in the coming demo):
在表上启用System_Versioning不会阻止您修改表架构。 系统将在时态表和历史记录表上都拥有模式锁,这些更改将被复制到历史记录表中。 无法在启用了System_Versioning的表上使用ALTER DATABASE语句直接执行特定的架构更改,例如添加或删除IDENTITY,COMPUTED,SPARSE,COLUMN_SET或ROWGUIDCOL列。 要在启用了System_Versioning的表上应用此类更改,首先需要禁用System_Versioning,执行所需的更改,然后再次启用System_Versioning。 下面的脚本用于将新的IDENTITY列添加到Temporal_Table_Demo时态表中(在接下来的演示中,我们将不考虑此添加的列):
ALTER TABLE dbo.[dbo].[Temporal_Table_Demo] SET ( SYSTEM_VERSIONING = OFF );
ALTER TABLE [dbo].[dbo].[Temporal_Table_Demo] ADD ID int IDENTITY (1,1);
ALTER TABLE dbo.[dbo].[Temporal_Table_Demo] SET ( SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.[dbo].[Temporal_Table_Demo_History]));
Let’s start a demo to investigate this new feature. First, we will insert 5 records to the Temporal_Table_Demo temporal table using the following simple INSERT statement:
让我们开始演示以研究此新功能。 首先,我们将使用以下简单的INSERT语句将5条记录插入Temporal_Table_Demo时态表:
USE [SQLShackDemo]
GO
INSERT INTO [dbo].[Temporal_Table_Demo]
([DempID]
,[DepName]
,[DepLocation]
,[NumOfEmp])
VALUES
(1,'Accounting','Build1',10),
(2,'HR','Build1',3),
(3,'Recruitment','Build1',2),
(4,'IT','Build2',7),
(5,'Security','Build2',3)
GO
Querying the temporal table to check the inserted rows:
查询时态表以检查插入的行:
SELECT [DempID]
,[DepName]
,[DepLocation]
,[NumOfEmp]
,[TimeStart]
,[TimeEnd]
FROM [SQLShackDemo].[dbo].[Temporal_Table_Demo]
The result in our case will be like:
在我们的案例中,结果将是:
As we discussed previously, the Period start time will be filled by the system with the transaction start time, and the Period End time will be filled by the system with the maximum datetime2 value keeping the records opened. No changes applied to the history table as no action performed on that rows yet. If you try to query it, you will find it empty.
如前所述,系统将使用事务开始时间来填充“期间开始时间”,并且系统将使用最大datetime2值来填充“期间结束时间”,以保持记录的打开状态。 由于尚未对该行执行任何操作,因此未对历史记录表进行任何更改。 如果尝试查询它,将发现它为空。
If we manage to delete the last row in the temporal table with DempID equal to 5:
如果我们设法删除时态表中DempID等于5的最后一行:
DELETE FROM [SQLShackDemo].[dbo].[Temporal_Table_Demo] WHERE DempID = 5
And query the temporal table, we will find that the record is removed from the table, which is the normal situation:
并查询时态表,我们会发现记录已从表中删除,这是正常情况:
But the new thing here is that, if we query the related history table:
但是这里的新问题是,如果我们查询相关的历史记录表:
SELECT [DempID]
,[DepName]
,[DepLocation]
,[NumOfEmp]
,[TimeStart]
,[TimeEnd]
FROM [SQLShackDemo].[dbo].[Temporal_Table_Demo_History]
We will find that the deleted record is inserted into the history table, and the Period End time is updated with the transaction start time, marking the row as closed:
我们将发现已删除的记录已插入到历史记录表中,并且使用事务开始时间来更新“期间结束时间”,将行标记为已关闭:
Again, if we manage to update the number of employees in department 1:
同样,如果我们设法更新部门1的员工人数:
UPDATE [SQLShackDemo].[dbo].[Temporal_Table_Demo] SET NumOfEmp=11 WHERE DempID = 1
And query the temporal table again, we will find that the row is updated and the Period Start time also updated with the new transaction time, keeping the row opened:
并再次查询时间表,我们将发现该行已更新,并且Period Start时间也随着新的事务时间而更新,从而使该行保持打开状态:
And the old record before the UPDATE operation will be inserted into the history table with the Period End time as the transaction current time, closing the row:
并将UPDATE操作之前的旧记录插入到历史记录表中,并将Period End时间作为事务当前时间,从而关闭该行:
The previous results show us how useful the new System_Versioningfeature is since it allows us to rollback or minimum track the changes applied to that table without getting back to the old backup files for one or few records.
先前的结果向我们展示了新的System_Versioning功能的实用性,因为它允许我们回滚或最小化跟踪应用于该表的更改,而无需返回一个或几条记录的旧备份文件。
A new clause FOR SYSTEM_TIME is added to the SELECT statement to query the data across the history and main temporal tables, with five sub-clauses to provide you with many options to specify the required period of time. The first clause is the AS OF clause, which returns values after performing the union between the temporal and history tables and filtering the rows that are valid at the specified point in time. The row can be considered as valid if the Period Start time value is less than or equal to the specified time and that the Period End time is greater than the specified time.
SELECT语句中添加了一个新的FOR SYSTEM_TIME子句,以查询历史记录和主要时态表中的数据,并具有五个子句,可为您提供许多选项来指定所需的时间段。 第一个子句是AS OF子句,在执行时间和历史表之间的联合并过滤在指定时间点有效的行之后,它返回值。 如果“期间开始时间”值小于或等于指定的时间,并且“期间结束时间”大于指定的时间,则可以将该行视为有效。
The below SELECT statement uses the FOR SYSTEM_TIME AS FOR clause to retrieve all rows with TimeStart less than or equal to the provided time and the TimeEnd is greater than that provided time:
下面的SELECT语句使用FOR SYSTEM_TIME AS FOR子句检索TimeStart小于或等于提供的时间且TimeEnd大于提供的时间的所有行:
SELECT * FROM [SQLShackDemo].[dbo].[Temporal_Table_Demo]
FOR SYSTEM_TIME AS OF '2016-09-18 13:40:05.06'
GO
The final result for the valid rows after the internal union between the temporal and history tables will be as follows:
时间和历史表之间的内部联合之后,有效行的最终结果将如下所示:
The second clause is the CONTAINED IN clause that returns all opened and closed rows within the time range defined by the provided two-period values. The below SELECT statement uses the FOR SYSTEM_TIME CONTAINED IN clause to retrieve all rows that opened and closed within the time range specified by the @StartTime and @EndTime values:
第二个子句是CONTAINED IN子句,它返回在提供的两个期间值定义的时间范围内的所有打开和关闭的行。 下面的SELECT语句使用FOR SYSTEM_TIME CONTAINED IN子句来检索在@StartTime和@EndTime值指定的时间范围内打开和关闭的所有行:
DECLARE @StartTime datetime2 = '2016-09-18 13:40:05.06'
DECLARE @EndTime datetime2 = '2016-09-18 13:46:03.28'
SELECT * FROM [SQLShackDemo].[dbo].[Temporal_Table_Demo]
FOR SYSTEM_TIME CONTAINED IN(@StartTime, @EndTime)
The result in our case will be:
在我们的案例中,结果将是:
The third clause is the FROM…TO clause that returns all active rows within the time range defined by the provided two values, regardless of the active starting time. A union will be performed internally between the main temporal table and the history table to retrieve the active rows within the provided range of time. The below SELECT statement uses the FOR SYSTEM_TIME FROM TO clause to retrieve all active rows within the time range specified by the @StartTime and @EndTime values:
第三个子句是FROM…TO子句,它返回由所提供的两个值定义的时间范围内的所有活动行,而与活动开始时间无关。 将在主时态表和历史表之间内部执行并集,以检索提供的时间范围内的活动行。 下面的SELECT语句使用FOR SYSTEM_TIME FROM TO子句来检索@StartTime和@EndTime值指定的时间范围内的所有活动行:
DECLARE @StartTime datetime2 = '2016-09-18 13:40:05.06'
DECLARE @EndTime datetime2 = '2016-09-18 13:46:03.28'
SELECT * FROM [SQLShackDemo].[dbo].[Temporal_Table_Demo]
FOR SYSTEM_TIME FROM @StartTime TO @EndTime
The below result shows all active records within the provided range of time:
以下结果显示了提供的时间范围内的所有活动记录:
The Fourth clause is the BETWEEN…AND clause that works same as the FROM…TO clause except that it returns the only the rows that became active on the lower boundary or became inactive on the upper boundary provided in the statement. The below SELECT statement uses the FOR SYSTEM_TIME BETWEEN AND clause to retrieve the rows that became active on @StartTime or the rows that became inactive on @EndTime:
第四个子句是BETWEEN…AND子句,其功能与FROM…TO子句相同,不同之处在于它仅返回在语句中提供的下边界处于活动状态或在上边界处于非活动状态的行。 下面的SELECT语句使用FOR SYSTEM_TIME BETWEEN AND子句检索在@StartTime上变为活动的行或在@EndTime上变为无效的行:
DECLARE @StartTime datetime2 = '2016-09-18 13:40:05.06'
DECLARE @EndTime datetime2 = '2016-09-18 13:46:03.28'
SELECT * FROM [SQLShackDemo].[dbo].[Temporal_Table_Demo]
FOR SYSTEM_TIME BETWEEN @StartTime AND @EndTime
The query output will be like:
查询输出将类似于:
The last clause is the ALL clause that performs an internal union between the rows in the main temporal table and the history table without specifying any time range as follows:
最后一个子句是ALL子句,它在主时态表和历史记录表中的行之间执行内部联合,而无需指定任何时间范围,如下所示:
SELECT * FROM [SQLShackDemo].[dbo].[Temporal_Table_Demo]
FOR SYSTEM_TIME ALL
The result will be similar to:
结果将类似于:
As any new feature introduced in SQL Server, a related DMV or property within the DMV will be added in order to query that feature’s information. The sys.tables system tables can be queried for the tables with temporal_type not equal to zero, to retrieve all tables with System_Versioning enabled with the corresponding history tables as below:
作为SQL Server中引入的任何新功能,将添加相关的DMV或DMV中的属性,以查询该功能的信息。 可以查询sys.tables系统表中的temporal_type不等于零的表,以检索启用了System_Versioning且具有相应历史记录表的所有表,如下所示:
SELECT Tab1.name as Temporal_Table_Name,
Tab2.name as History_Table_Name,
Tab1.temporal_type_desc
FROM sys.tables Tab1
LEFT JOIN sys.tables Tab2 ON Tab1.history_table_id = Tab2.object_id
WHERE Tab1.temporal_type <> 0
ORDER BY Temporal_Table_Name
GO
The result of the query will show us the two tables that we enabled the System_Versioning on with its related history tables:
查询结果将向我们显示启用了System_Versioning的两个表及其相关的历史记录表:
The new system catalog view sys.periods can be used also to list all period columns of the tables with System_Versioning enabled on it. Full information can be retrieved by joining the sys.periods with the sys.tables as in the below query:
新的系统目录视图sys.periods也可以用于列出启用了System_Versioning的表的所有期间列。 可以通过将sys.periods与sys.tables联接来检索完整信息,如以下查询所示:
SELECT PS.name as Period_Name,
Tbl.name as Temporal_Table_Name,
clms1.name as Period_Start_CName,
clms2.name as Period_Enf_CName
FROM sys.periods PS
INNER JOIN sys.tables Tbl ON PS.object_id = Tbl.object_id
INNER JOIN sys.columns clms1 ON Tbl.object_id = clms1.object_id AND PS.start_column_id = clms1.column_id
INNER JOIN sys.columns clms2 ON Tbl.object_id = clms2.object_id AND PS.end_column_id = clms2.column_id
GO
The query result will show us the two System_Versioning enabled tables with the Period Start and Period End columns names:
查询结果将向我们显示两个启用了System_Versioning的表,它们具有Period Start和Period End列名称:
SQL Server allows you to add indexes to the temporal and history tables to enhance the query’ performance. Assume that we need to tune the performance of the below query that is running very frequent in our system and any enhancement in it will make a difference in the overall performance:
SQL Server允许您将索引添加到临时表和历史记录表,以增强查询的性能。 假设我们需要调整以下查询的性能,该查询在我们的系统中非常频繁地运行,并且对它的任何增强都会使整体性能有所不同:
SET STATISTICS TIME ON
DECLARE @StartTime datetime2 = '2016-09-18 13:40:05.06'
DECLARE @EndTime datetime2 = '2016-09-18 13:46:03.28'
SELECT * FROM [SQLShackDemo].[dbo].[Temporal_Table_Demo]
FOR SYSTEM_TIME FROM @StartTime TO @EndTime
SET STATISTICS TIME OFF
We turned the STATISTICS TIME on in order to get the query execution time accurately. Let’s run the query and check the query execution plan using the APEXSQL PLAN application and execution time. The execution plan of the query shows us that a Clustered Index Scan is performed in the temporal table as there is no index on the TimeStart and TimeEnd columns:
我们打开了STATISTICS TIME,以便准确获取查询执行时间。 让我们运行查询并使用APEXSQL PLAN应用程序和执行时间检查查询执行计划。 该查询的执行计划向我们显示,由于TimeStart和TimeEnd列上没有索引,因此在时态表中执行了聚集索引扫描:
And the query took 107ms to run completely:
该查询需要107毫秒才能完全运行:
Let’s create a non-clustered index on the TimeStart and TimeEnd columns:
让我们在TimeStart和TimeEnd列上创建一个非聚集索引:
USE [SQLShackDemo]
GO
CREATE NONCLUSTERED INDEX [IX_Temporal_Table_Demo_TStart_TEnd] ON [dbo].[Temporal_Table_Demo]
(
[TimeStart] ASC,
[TimeEnd] ASC
)
GO
And execute the query again, the execution plan generated using the APEXSQL PLAN application shows us now that the Clustered Index Scan on the Temporal table changed to Index Seek:
然后再次执行查询,使用APEXSQL PLAN应用程序生成的执行计划现在向我们显示了临时表上的聚簇索引扫描已更改为索引查找:
And that the execution time is decreased clearly to 37ms:
并且执行时间明显减少到37ms:
As you can see, the index that we created in the temporal table enhanced the query performance and decreased the execution time considerably. Depending on the workload on your environment, you can customize the best index that fit your queries and enhance your system performance.
如您所见,我们在时态表中创建的索引提高了查询性能,并大大减少了执行时间。 根据环境上的工作负载,您可以自定义适合您查询的最佳索引并增强系统性能。
结论 (Conclusion)
The System-Versioned Temporal table is a new type of table that is linked with a related history table in order to keep a history for each row change performed on that table. You can develop your own solution depending on the temporal table to track the data changes with the change time and rollback any disaster changes without the need to restore the backup files. You can get information about the temporal tables and the period columns using the existing and the newly introduced SQL Server system objects. The good point here is that it is managed only by the system requiring no effort from your side to define retention periods and follow up the insertion or deletion processes on the historical tables. As any new feature, test it carefully in your test environment before applying it to the live environment.
System-Versioned Temporal表是一种新型表,该表与相关的历史记录表链接,以便保留对该表执行的每个行更改的历史记录。 您可以根据时态表开发自己的解决方案,以跟踪数据随更改时间的变化并回滚任何灾难更改,而无需还原备份文件。 您可以使用现有的和新引入SQL Server系统对象来获取有关时间表和期间列的信息。 这样做的好处是,仅由系统管理,无需您费力地定义保留期限并跟踪历史表中的插入或删除过程。 作为任何新功能,请先在测试环境中对其进行仔细测试,然后再将其应用于实时环境。