具有时间间隔的 table 行实例的快照

Snapshot of table row instance with time intervals

我不知道该怎么称呼这种现象。我刚刚将其命名为 "Snapshot of table row instance with time intervals"。我们有一种情况,我们需要创建一个新实体,它是两个 table 的 JOIN,但这两个 table 有历史,我们需要将历史合并到这个实体中。请看下面的例子:

在图像中,您可以看到我们有两个 tables ABC 和 PQR,其中 ID 作为键列。我们需要创建一个 table ABCPQR,它将加入这两个 table 并且它们的历史 table 将包含来自它们各自 table 的两个属性的合并历史。

如何在数据库中实现这个?我正在使用 Teradata 作为数据库,是否有任何算法可以实现这一点?

没有复杂的算法。简单的 sql 和 cross-join , between 和 greatest/least 函数就可以了。

create table abc 
( id integer,
attr1 varchar(3),
start_date date,
end_date date)
PRIMARY INDEX(ID);


create table PQR 
( id integer,
attr2 varchar(3),
start_date date,
end_date date)
PRIMARY INDEX(ID);

INSERT INTO abc VALUES(1,'LMN','2017-01-01','2017-02-28');
INSERT INTO abc VALUES(1,'HGI','2017-02-28','2017-03-15');
INSERT INTO abc VALUES(1,'STI','2017-03-15','2099-12-31');


INSERT INTO PQR VALUES(1,'KLM','2017-01-01','2017-01-20');
INSERT INTO PQR VALUES(1,'TLF','2017-01-20','2017-04-04');
INSERT INTO PQR VALUES(1,'SNQ','2017-04-04','2099-12-31');


select a.id,a.attr1,p.attr2,
        cast(greatest(cast(a.start_date as int),cast(p.start_date as int)) as date)start_date,
        cast(least(cast(a.end_date as int),cast(p.end_date as int)) as date)end_date
from abc a
inner join pqr p
on a.id=p.id
where a.start_date between p.start_date and p.end_date
or a.end_date between p.start_date and p.end_date
order by 4;

输出:

    id  attr1   attr2   start_date  end_date
1   1   LMN KLM 1/1/2017    1/20/2017
2   1   LMN TLF 1/20/2017   2/28/2017
3   1   HGI TLF 2/28/2017   3/15/2017
4   1   STI TLF 3/15/2017   4/4/2017
5   1   STI SNQ 4/4/2017    12/31/2099

唯一不同的是我用了 2099 而不是 1999 来让事情变得简单。您也可以使用 1999 并自定义此查询(虽然不是首选)。

您可以使用 Teradata 的 PERIOD 数据类型快速完成这项工作。期间是日期或时间戳的范围。它需要两个日期或时间戳作为参数,第一个为开始日期,第二个为结束日期(直到但不包括)。

在您的情况下,我们会将您的 Start_DateEnd_Date 转换为句点以执行连接。我们将使用周期特定函数 P_INTERSECT 来查找我们将加入的重叠周期。

SELECT
    attr1,
    attr2,
    /*pull the BEGIN of the intersected period*/
    BEGIN(t1.validperiod P_INTERSECT t2.validperiod) as startdate,
    /*pull the END of the intersected period (subtracting a day since period end
      dates are "up to but not including")*/
     PRIOR(END(t1.validperiod P_INTERSECT t2.validperiod)) as enddate   
FROM
    (SELECT abc.*, PERIOD(start_date, NEXT(end_date)) as validperiod FROM abc) t1
    INNER JOIN (SELECT pqr.*, PERIOD(start_Date, NEXT(end_date)) as validperiod FROM pqr) t2 ON
        t1.id = t2.id 
        /*
        * Now P_INTERSECT our two periods and look for Non-Null intersections
        * The intersection is the date range where the two periods overlap
        */
        AND t1.validperiod P_INTERSECT t2.validperiod IS NOT NULL;

我们在这里获得了一些奖励:

  1. 逻辑简洁明了。没有测试 max(start_date) 和 min(end_date) 嵌套的 CASE 语句和所有可怕的东西
  2. 如果您必须加入第三个 table,只需将其开始日期和结束日期转换为有效期并加入 t1.validperiod P_INTERSECT t2.validperiod P_INTERSECT t3.validperiod IS NOT NULL 简单简单。
  3. 还有一些其他真正有用的函数是基于句点的,可以让这样的工作变得轻而易举。例如,NORMALIZE 将根据复合键以及重叠和会议期间将多个记录合并在一起。

最后,通常,当我创建 table 并且 table 具有开始日期和结束日期时,我总是创建一个名为 validperiod 的新字段并像我们在那些子查询。然后,您不必转换为句点即可让您的连接变得友好。只需获取您已经存储的 validperiod 列并开始 P_INTERSECTing。它消除了其他丑陋的连接和选择的所有工作。