我有两个表跟踪相同对象的不同属性历史记录集。表格可能如下所示:
T1:
ID | VERSION_ST | VERSION_END | Attr1
-----------------------------------------------
1 | 2012-01-01 | 2013-05-07 | Red
1 | 2013-05-08 | 2014-04-01 | Blue
1 | 2014-04-02 | NULL | Green
T2:
ID | VERSION_ST | VERSION_END | Attr2
-----------------------------------------------
1 | 2012-01-01 | 2013-06-04 | Large
1 | 2013-06-05 | 2014-07-15 | Medium
1 | 2014-07-16 | NULL | Large
如何编写将这些属性历史记录组合在一起的查询,以便我可以看到两个属性的准确版本开始和结束?
结果集可能如下所示:
ID | VERSION_ST | VERSION_END | Attr1 | Attr2
-----------------------------------------------------------
1 | 2012-01-01 | 2013-05-07 | Red | Large
1 | 2013-05-08 | 2013-06-04 | Blue | Large
1 | 2013-06-05 | 2014-04-01 | Blue | Medium
1 | 2014-04-02 | 2014-07-15 | Green | Medium
1 | 2014-07-16 | NULL | Green | Large
当我尝试加入on T1.ID = T2.ID and T1.START between T2.START and T2.END
时,只返回了三行,因此它无法准确跟踪T2中的更改。如果我扭转它也是一样的。不知道如何同时做两件事。
我可以访问各种数据库系统来完成这项工作,如果其中任何具有此功能,我会接受它作为答案。
答案 0 :(得分:1)
这适用于您的示例数据,但在某些情况下可能会失败:
SELECT
CASE WHEN t1.version_st < t2.version_st THEN t2.version_st ELSE t1.version_st END,
CASE WHEN t1.version_end < t2.version_end THEN t1.version_end ELSE t2.version_end END,
t1.attr1,
t2.attr2
FROM t1 JOIN t2
ON T1.ID = T2.ID
AND (t1.VERSION_ST, COALESCE(t1.VERSION_END, DATE '9999-12-31')) OVERLAPS
(t2.VERSION_ST, COALESCE(t2.VERSION_END, DATE '9999-12-31'))
编辑: Teradata支持OVERLAPS(大多数DBMS不知道),但可以替换为:
FROM t1 JOIN t2
ON T1.ID = T2.ID
AND t1.version_st < COALESCE(t2.version_end, DATE '9999-12-31')
AND t2.version_st < COALESCE(t1.version_end, DATE '9999-12-31')
答案 1 :(得分:0)
您尝试过的是正确的,因为当您查看输出时,行3 蓝色|中等是不可能的,因为表1中的蓝色Version_st不在指定范围之间。 如果您尝试使用' ISNULL(T2.End,'9999-12-31'),您将获得4条记录的输出,假设null表示其处于活动状态。 如果是oracle使用NVL功能。
答案 2 :(得分:0)
on T1.ID = T2.ID and T1.START between T2.START and T2.END
本身不起作用,因为T1的结束可能落在T2的开始/结束之间。
尝试:
on T1.ID = T2.ID and (T1.START between T2.START and T2.END or T1.END between T2.START and T2.END)
要处理空值,请使用COALLESCE(date,CURRENT_DATE),以便我们将具有空日期的任何内容视为今天继续。在Oracle中,current_date是SYSDATE,在Sql Server中它是GETDATE()。
答案 3 :(得分:0)
我认为这可以满足您的需求:
select
t1.version_st as t1_start,
t1.version_end as t1_end,
t2.version_st as t2_start,
t2.version_end as t2_end,
t1.attr1,
t2.attr2
from
t1
full join t2
on T1.ID = T2.ID and
((T1.version_st between T2.version_st and T2.version_end )
or (T1.version_END between T2.version_st and T2.version_end))
我认为你需要完全加入,因为无法保证日期排成一行(如t2的最后一行)。
答案 4 :(得分:0)
这似乎有效:
SELECT * FROM t1 a
INNER JOIN t2 b
on a.id = b.id and a.VERSION_ST between b.VERSION_ST and b.VERSION_END
UNION
SELECT * FROM t2 a
INNER JOIN t1 b
on a.id = b.id and a.VERSION_ST between b.VERSION_ST and b.VERSION_END
答案 5 :(得分:0)
如果一行中的一列与另一行中的另一列之间存在依赖关系,则会创建我称之为行跨越依赖关系的行。在您的情况下,依赖关系介于一行的VERSION_END和序列中下一行的VERSION_ST之间。这使得非常困难的DML和许多数据完整性令人头痛。此外,即使在同一行中,也可以在不同的上下文中使用两个日期字段。也就是说,一行的有效持续时间从VERSION_ST('yyy-mm-dd 00:00:00')的早晨午夜开始到VERSION_END晚上的最后一刻('yyy-mm-dd' 23:59:59' )。如果不出意外,这会导致混淆。
幸运的是,只需删除VERSION_END即可解决所有问题。这不是必需的。行的有效持续时间从VERSION_ST中的日期开始,并一直有效,直到下一行的VERSION_ST。 “当前”行是没有下一行的行。
请注意,下面的查询为您提供了所需的输出,而根本不使用VERSION_END。第一个查询返回Attr1创建/更新的日期以及当时的Attr2值。第二个查询返回Attr2创建/更新的日期以及此时的Attr1值。 union
删除重复的行(Attr1和Attr2都是同时创建的,因此会出现在两个查询中)。
select t1.id, t1.VERSION_ST, t1.Attr1, t2.Attr2
from t1
left join t2
on t2.id = t1.id
and t2.VERSION_ST =(
select max( VERSION_ST )
from t2
where VERSION_ST <= t1.VERSION_ST )
union
select t2.id, t2.VERSION_ST, t1.Attr1, t2.Attr2
from t2
left join t1
on t1.id = t2.id
and t1.VERSION_ST =(
select max( VERSION_ST )
from t1
where VERSION_ST <= t2.VERSION_ST );