【问题标题】:Snapshot of table row instance with time intervals具有时间间隔的表行实例的快照
【发布时间】:2017-10-16 10:27:04
【问题描述】:

我不知道如何称呼这种现象。我只是用“带有时间间隔的表行实例的快照”来命名它。我们有一种情况,我们需要创建一个新实体,它是两个表的一种 JOIN,但是这两个表有历史,我们需要将历史合并到这个实体中。请看下面的例子:

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

如何在数据库中实现这一点?我正在使用 Teradata 作为数据库,有没有可用的算法来实现这一点?

【问题讨论】:

    标签: sql analytics teradata data-warehouse window-functions


    【解决方案1】:

    您可以使用 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. 如果您必须加入第三个表,只需将其开始和结束日期转换为有效期限,然后使用t1.validperiod P_INTERSECT t2.validperiod P_INTERSECT t3.validperiod IS NOT NULL 加入即可。简单简单。
    3. 还有一些其他非常有用的基于周期的函数可以让这样的工作变得轻而易举。例如,NORMALIZE 将基于复合键以及重叠和会议期间将多条记录合并在一起。

    最后,作为一项规则,当我创建表并且该表具有开始日期和结束日期时,我总是创建一个名为 validperiod 的新字段并像在这些子查询中一样加载它。然后,您不必转换为句点即可使您的加入变得友好。只需获取您已经存储的有效期限列并开始 P_INTERSECTing。它消除了其他丑陋的连接和选择的所有工作。

    【讨论】:

    • 嗨 JNevill。感谢您的回答,但想问一个快速的问题。你知道当我们连接多个表时 P_INTERSECT 是如何执行的吗?
    • 它非常快。我有一个 p_intersects 6 或 7 个表,每个表都有数万条记录,它会在一分钟左右返回。那一个也是所有左连接,所以我合并每个可能的交叉点。
    • 我意识到我在 cmets 中声明我正在使用 end(period) - 1 但忘记在 SQL 中实现它。我已将该字段的答案更新为:Prior(End(t1.validperiod P_INTERSECT t2.validperiod))。此外,您可能会发现值得查看有关 Teradata 的 Period 功能的旧博客文章:downloads.teradata.com/partners/oracle/articles/… 这是一个包含示例和解释的绝佳资源。
    • 另外,我刚刚运行了我在此处的第二条评论中提到的大 P_INTERSECT sql。它需要 6 个表,每个表有 200k 到 600k 条记录,它在我们的单节点盒上在 2-3 秒内将 663746 条记录输出到一个新表。
    【解决方案2】:

    没有复杂的算法。带有交叉连接、介于和最大/最小函数的简单 sql 即可。

    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 并自定义此查询(虽然不是首选)。

    【讨论】:

    • 感谢 Bhavesh 的回答。
    猜你喜欢
    • 2016-11-02
    • 1970-01-01
    • 1970-01-01
    • 2015-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-16
    • 1970-01-01
    相关资源
    最近更新 更多