[英]joining temporal tables in oracle
我正在为临时表的一个相当普遍的问题寻找更好的解决方案。
说我们有
table_a (some_value int, date_from date, date_to date)
和一系列类似的表table_b
, table_c
,...
实际的系统是一个 HR 系统,用于跟踪人员、合同、任务、工资……所有这些都以上述方式注明日期。
我需要加入这样的表(例如,想象得到所有具有相同some_value
的行)并返回这种加入有效的时间段(即所有行在这段时间内重叠)。
有两个表很容易(暂时忽略NULL
值)
select a.some_value, greatest(a.date_from, b.date_from) date_from, least(a.date_to, b.date_to) date_to
from table_a a join table_b b on a.some_value = b.some_value
where a.date_from < b.date_to and b.date_from < a.date_to
随着表的增多,这将变得更加困难,因为对于三个表(A、B、C),您需要检查 A 和 B、B 和 C、C 和 A 之间的重叠。对于 N 个表,这将增长为 N^2。
所以我写了一个 pl/sql 流水线 function (称为dated_join
),给定两个间隔,它返回一行有重叠的周期,如果不重叠,则返回任何内容
所以我可以吃三张桌子
select a.some_value, period_b.date_from, period_b.date_to
from table_a a
join table_b b on a.some_value = b.some_value
join table(dated_join(a.date_from, a.date_to, b.date_from, b.date_to)) period_a
join table_c c on a.some_value = c.some_value
join table(dated_join(period_a.date_from, period_a.date_to, c.date_from, c.date_to)) period_b
这线性地缩放到 N 个值,因为每个周期只与前一个周期相连,并且它延续了重叠的周期。
问题:是否可以使此策略与OUTER JOIN
一起使用? 我找不到任何半体面的解决方案。
SQL:2011 临时扩展中有什么可以帮助解决这个问题吗?
感谢您的帮助
如果您想加入多个表,以便它们都在同一时期重叠,那么您可以使用GREATEST
和LEAST
:
SELECT t1.date_from AS t1_from,
t2.date_from AS t2_from,
t3.date_from AS t3_from,
t4.date_from AS t4_from,
t1.date_to AS t1_to,
t2.date_to AS t2_to,
t3.date_to AS t3_to,
t4.date_to AS t4_to
FROM table1 t1
INNER JOIN table2 t2
ON ( t1.date_to > t2.date_from
AND t1.date_from < t2.date_to )
INNER JOIN table3 t3
ON ( LEAST( t1.date_to, t2.date_to ) > t3.date_from
AND GREATEST( t1.date_from, t2.date_from ) < t3.date_to )
INNER JOIN table4 t4
ON ( LEAST( t1.date_to, t2.date_to, t3.date_to ) > t4.date_from
AND GREATEST( t1.date_from, t2.date_from, t3.date_from ) < t4.date_to );
其中,对于样本数据:
CREATE TABLE table1 ( date_from, date_to ) AS
SELECT DATE '2020-01-01', DATE '2020-01-10' FROM DUAL UNION ALL
SELECT DATE '2020-02-10', DATE '2020-02-15' FROM DUAL UNION ALL
SELECT DATE '2020-03-15', DATE '2020-03-18' FROM DUAL;
CREATE TABLE table2 ( date_from, date_to ) AS
SELECT DATE '2020-01-05', DATE '2020-01-15' FROM DUAL UNION ALL
SELECT DATE '2020-02-09', DATE '2020-02-16' FROM DUAL UNION ALL
SELECT DATE '2020-03-16', DATE '2020-03-18' FROM DUAL;
CREATE TABLE table3 ( date_from, date_to ) AS
SELECT DATE '2020-01-01', DATE '2020-01-02' FROM DUAL UNION ALL
SELECT DATE '2020-01-09', DATE '2020-01-16' FROM DUAL UNION ALL
SELECT DATE '2020-02-08', DATE '2020-02-17' FROM DUAL UNION ALL
SELECT DATE '2020-03-15', DATE '2020-03-17' FROM DUAL;
CREATE TABLE table4 ( date_from, date_to ) AS
SELECT DATE '2020-01-02', DATE '2020-01-12' FROM DUAL UNION ALL
SELECT DATE '2020-02-08', DATE '2020-02-17' FROM DUAL UNION ALL
SELECT DATE '2020-03-16', DATE '2020-03-19' FROM DUAL;
输出:
T1_FROM | T2_FROM | T3_FROM | T4_FROM | T1_TO | T2_TO | T3_TO | T4_TO:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- 15-MAR-20 | 20 年 3 月 16 日 | 20 年 3 月 15 日 | 20 年 3 月 16 日 | 20 年 3 月 18 日 | 20 年 3 月 18 日 | 20 年 3 月 17 日 | 20 年 3 月 19 日 20 年 2 月 10 日 | 20 年 2 月 9 日 | 20 年 2 月 8 日 | 20 年 2 月 8 日 | 20 年 2 月 15 日 | 20 年 2 月 16 日 | 20 年 2 月 17 日 | 20 年 2 月 17 日 20 年 1 月 1 日 | 20 年 1 月 5 日 | 20 年 1 月 9 日 | 20 年 1 月 2 日 | 20 年 1 月 10 日 | 20 年 1 月 15 日 | 20 年 1 月 16 日 | 20 年 1 月 12 日
并且,如果您希望它与范围的任何部分重叠,则交换GREATEST
和LEAST
:
SELECT t1.date_from AS t1_from,
t2.date_from AS t2_from,
t3.date_from AS t3_from,
t4.date_from AS t4_from,
t1.date_to AS t1_to,
t2.date_to AS t2_to,
t3.date_to AS t3_to,
t4.date_to AS t4_to
FROM table1 t1
INNER JOIN table2 t2
ON ( t1.date_to > t2.date_from
AND t1.date_from < t2.date_to )
INNER JOIN table3 t3
ON ( GREATEST( t1.date_to, t2.date_to ) > t3.date_from
AND LEAST( t1.date_from, t2.date_from ) < t3.date_to )
INNER JOIN table4 t4
ON ( GREATEST( t1.date_to, t2.date_to, t3.date_to ) > t4.date_from
AND LEAST( t1.date_from, t2.date_from, t3.date_from ) < t4.date_to );
哪个输出:
T1_FROM | T2_FROM | T3_FROM | T4_FROM | T1_TO | T2_TO | T3_TO | T4_TO:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- 15-MAR-20 | 20 年 3 月 16 日 | 20 年 3 月 15 日 | 20 年 3 月 16 日 | 20 年 3 月 18 日 | 20 年 3 月 18 日 | 20 年 3 月 17 日 | 20 年 3 月 19 日 20 年 2 月 10 日 | 20 年 2 月 9 日 | 20 年 2 月 8 日 | 20 年 2 月 8 日 | 20 年 2 月 15 日 | 20 年 2 月 16 日 | 20 年 2 月 17 日 | 20 年 2 月 17 日 20 年 1 月 1 日 | 20 年 1 月 5 日 | 20 年 1 月 1 日 | 20 年 1 月 2 日 | 20 年 1 月 10 日 | 20 年 1 月 15 日 | 20 年 1 月 2 日 | 20 年 1 月 12 日 20 年 1 月 1 日 | 20 年 1 月 5 日 | 20 年 1 月 9 日 | 20 年 1 月 2 日 | 20 年 1 月 10 日 | 20 年 1 月 15 日 | 20 年 1 月 16 日 | 20 年 1 月 12 日
db<> 在这里摆弄
如果您想要与时间段重叠的表中的所有值,那么您似乎想要:
select *
from table_a a left join
table_b b
on b.date_from < a.date_to and
b.date_to > a.date_from left join
table_c c
on c.date_from < a.date_to and
c.date_to > a.date_from
where a.value = ?
我没有看到任何“二次”复杂性。 每个新表都使用完全相同的连接条件。
改进@MT0 上面给出的解决方案,我们可以使用不同的重叠测试
GREATEST(t1.date_from, t2.date_from) < LEAST(t1.date_to, t2.date_to)
并将其扩展到任意数量的表
GREATEST(t1.date_from, t2.date_from, t3.date_from, t4.date_from, ...)
< LEAST(t1.date_to, t2.date_to, t3.date_to, t4.date_to, ...)
然后可以用一个测试替换内部连接
SELECT t1.date_from AS t1_from,
t2.date_from AS t2_from,
t3.date_from AS t3_from,
t4.date_from AS t4_from,
t1.date_to AS t1_to,
t2.date_to AS t2_to,
t3.date_to AS t3_to,
t4.date_to AS t4_to
FROM table1 t1
CROSS JOIN table2 t2
CROSS JOIN table3 t3
CROSS JOIN table4 t4
WHERE
GREATEST(t1.date_from, t2.date_from, t3.date_from, t4.date_from)
< LEAST(t1.date_to, t2.date_to, t3.date_to, t4.date_to);
而外连接变成
SELECT t1.date_from AS t1_from,
t2.date_from AS t2_from,
t3.date_from AS t3_from,
t4.date_from AS t4_from,
t1.date_to AS t1_to,
t2.date_to AS t2_to,
t3.date_to AS t3_to,
t4.date_to AS t4_to
FROM table1 t1
left outer join table2 t2 on
greatest(t1.date_from, t2.date_from) < least(t1.date_to, t2.date_to)
left outer join table3 t3 on
greatest(t1.date_from, t2.date_from, t3.date_from) < least(t1.date_to, t2.date_to, t3.date_to)
left outer join table4 t4 on
greatest(t1.date_from, t2.date_from, t3.date_from, t4.date_from) < least(t1.date_to, t2.date_to, t3.date_to, t4.date_to);
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=e43b7c58208b7ea85af24ec0ec0ca4a7
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.