不使用系统时间的时态表

5 sql-server temporal-tables sql-server-2016

我们希望在 SQL Server 2016 中实现时态表。我们正在创建一个数据仓库并开发类型 2 缓慢变化的维度表。

对于 BeginDate,我们希望它取决于交易日期而不是当前的 getdate 时间。我们正在重新处理交易历史记录。下面的示例中,客户具有健身房或银行状态,并且根据交易日期从不活动状态变为活动状态或待处理状态。

我们目前有这个。

CREATE TABLE dbo.Department
(
    CustomerId int primary key,
    MembershipStatus int,
    TransactionDate datetime
 );
Run Code Online (Sandbox Code Playgroud)

我们想创建一个这样的表。

CREATE TABLE dbo.DepartmentHistory
(
    CustomerId int primary key,
    MembershipStatus int,
    TransactionDate datetime,
    BeginDatetime datetime,
    EndDatettime datetime
 );
Run Code Online (Sandbox Code Playgroud)

示例用法如下:

  1. 2018 年 3 月 5 日的第一笔客户交易为待处理 P
    +------------+--------+------------+---------+
    | 客户 ID | 状态 | 开始日期 | 结束日期 |
    +------------+--------+------------+---------+
    | 1 | 普 | 2018 年 3 月 5 日 | 空|
    +------------+--------+------------+---------+
  1. 第二笔交易是 2018 年 4 月 21 日,处于活动 A 状态
    +------------+--------+------------+------------+
    | 客户 ID | 状态 | 开始日期 | 结束日期 |
    +------------+--------+------------+------------+
    | 1 | 普 | 2018 年 3 月 5 日 | 2018 年 4 月 21 日 |
    | 1 | 一个 | 2018 年 4 月 21 日 | 空|
    +------------+--------+------------+------------+

Pet*_*ier 7

临时表系统版本1 ,因此“手动设置”给定历史行的时间戳的唯一方法是更改​​该行被修改时的操作系统时间,而您可能不想这样做。

如果您想在历史表上“手动设置”时间范围以使其支持时态查询语法,您可以通过在现有数据之上手动应用时态表来实现。这有点棘手,需要底层数据符合时间历史版本控制规则。当时态表被引入时,Google 上就充斥着关于此问题的各种博客文章;因为我已经有一段时间没有使用这个确切的用例了,所以现在我将任意链接到这个看起来很有希望的示例

快速演示

请记住,系统版本控制的时态表实际上是两个表- 一个“现在”表和一个“历史”表压缩在一起......

create table dbo.b_now ( 
     i int not null primary key 
    ,info varchar(10)
    ,start_dt datetime2 generated always as row start
    ,end_dt   datetime2 generated always as row end
    ,period for system_time (start_dt, end_dt)
) with (system_versioning = on (history_table = dbo.b_history));
go

insert b_now (i,info)
values 
 (1,'AAA')
,(2,'BBB');
go
update b_now set info = 'XXX' where i = 1
go
select * from b_history
select * from b_now
go
Run Code Online (Sandbox Code Playgroud)

请注意,虽然b_now似乎是“基表”并支持时间语法 - 它只是一个单独的对象。删除对象之间的系统版本控制绑定强化了这一点。

使用此信息进行“手动版本”

牢记这一点 - 并记住我们可以来回切换SYSTEM_VERSIONING,我们可以告诉 SQL Server 对任意一对对象任意应用(ON并任意忽略)系统版本控制规则,只要OFF......ON

  1. 模式匹配
  2. 数据与经过系统版本控制后的数据完全一致

但我想要示例代码!

是的,我想你可能会 - 所以试试这个......

drop table if exists a_now ,a_history;
go
create table a_now ( 
     i    int not null primary key
    ,info varchar(10)
    ,start_dt datetime2 not null
    ,end_dt   datetime2 not null 
        -- you'll want this CHECK later...
        check (end_dt = convert(datetime2,'9999-12-31 23:59:59.9999999'))
);
go
create table a_history (
     i        int not null
    ,info     varchar(10)
    ,start_dt datetime2 not null
    ,end_dt   datetime2 not null
);
go

declare @end_of_time datetime2 = '9999-12-31 23:59:59.9999999';

insert a_now values 
 (1,'XXX','2017-01-01',@end_of_time)
,(2,'BBB','2017-01-01',@end_of_time)

insert a_history values
 (1,'AAA','2016-01-01','2017-01-01');
go
select * from a_now
select * from a_history
Run Code Online (Sandbox Code Playgroud)

...看起来很像b_now并且b_history,不是吗?太糟糕了,这不是一个合适的系统表......

alter table a_now
    add period for system_time (start_dt, end_dt);
go
alter table a_now
    set (system_versioning = on (history_table = dbo.a_history));
go
Run Code Online (Sandbox Code Playgroud)

等等呃……?您甚至可以在“现在”表的顶部放置一个视图,以隐藏您对命名约定的滥用。

create or alter view a 
as
select * from a_now
go

select * 
from a 
for system_time as of '2016-06-01'
Run Code Online (Sandbox Code Playgroud)

好的,是的...但是我如何添加新的行呢,Smarty Pants 先生?

非常仔细……就是这样。

alter table a_now
    set (system_versioning = off);
alter table a_now
    drop period for system_time;
go

declare @end_of_time datetime2 = '9999-12-31 23:59:59.9999999';

insert a_now values 
 (3,'CCC','2018-01-01',@end_of_time);

update a_now set 
     start_dt = '2018-01-01'
    ,info = 'YYYY'
where i = 1;

insert a_history 
values 
  (1,'XXX','2017-01-01','2018-01-01')

alter table a_now
    add period for system_time (start_dt, end_dt);
go
alter table a_now
    set (system_versioning = on (history_table = dbo.a_history));
go

select * from a for system_time as of '2017-06-01'
Run Code Online (Sandbox Code Playgroud)

我将其留给读者(您)将INSERT//UPDATE逻辑DELETE和相关的DROP/re-SET系统版本控制包装到可重用的模块中。


1. 注意,创建时态表所需的 DDLperiod for SYSTEM_time中的语法暗示了这一点