[英]joining temporal tables in oracle
I am looking for better solutions to a fairly generic problem with temporal tables.我正在为临时表的一个相当普遍的问题寻找更好的解决方案。
say we have说我们有
table_a (some_value int, date_from date, date_to date)
and a series of similar tables table_b
, table_c
, ...和一系列类似的表
table_b
, table_c
,...
the actual system is an HR system that tracks persons, contracts, assignments, salaries, ... all they are all dated in the way described above.实际的系统是一个 HR 系统,用于跟踪人员、合同、任务、工资……所有这些都以上述方式注明日期。
I need to join such tables (eg imagine getting all rows with the same some_value
) and return the period for which such join is valid (that is all the rows overlap over such period).我需要加入这样的表(例如,想象得到所有具有相同
some_value
的行)并返回这种加入有效的时间段(即所有行在这段时间内重叠)。
with two tables it is easy (ignore NULL
values for the time being)有两个表很容易(暂时忽略
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
this become quadratically harder with more tables, because with three table (A, B, C) you need to check the overlap between A and B, B and C, C and A. with N tables this grows as N^2.随着表的增多,这将变得更加困难,因为对于三个表(A、B、C),您需要检查 A 和 B、B 和 C、C 和 A 之间的重叠。对于 N 个表,这将增长为 N^2。
so I have written a pl/sql pipelined function (call it dated_join
) that given two intervals it returns one row with the overlapping period or nothing if it doesn't overlap所以我写了一个 pl/sql 流水线 function (称为
dated_join
),给定两个间隔,它返回一行有重叠的周期,如果不重叠,则返回任何内容
so I can have for three tables所以我可以吃三张桌子
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
this scales linearly to N values, because each period is joined only with the previous one and it carries forward the overlapping period.这线性地缩放到 N 个值,因为每个周期只与前一个周期相连,并且它延续了重叠的周期。
question: is it possible to make this strategy work with OUTER JOIN
s?问题:是否可以使此策略与
OUTER JOIN
一起使用? I cannot find any half-decent solution.我找不到任何半体面的解决方案。
is there anything in the SQL:2011 temporal extensions that would help with this? SQL:2011 临时扩展中有什么可以帮助解决这个问题吗?
thanks for your help感谢您的帮助
If you want to join multiple table so that they all overlap the same period then you can use GREATEST
and LEAST
:如果您想加入多个表,以便它们都在同一时期重叠,那么您可以使用
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 );
Which, for the sample data:其中,对于样本数据:
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;
Outputs:输出:
T1_FROM |T1_FROM | T2_FROM |
T2_FROM | T3_FROM |
T3_FROM | T4_FROM |
T4_FROM | T1_TO |
T1_TO | T2_TO |
T2_TO | T3_TO |
T3_TO | T4_TO:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- 15-MAR-20 |
T4_TO:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- 15-MAR-20 | 16-MAR-20 |
20 年 3 月 16 日 | 15-MAR-20 |
20 年 3 月 15 日 | 16-MAR-20 |
20 年 3 月 16 日 | 18-MAR-20 |
20 年 3 月 18 日 | 18-MAR-20 |
20 年 3 月 18 日 | 17-MAR-20 |
20 年 3 月 17 日 | 19-MAR-20 10-FEB-20 |
20 年 3 月 19 日 20 年 2 月 10 日 | 09-FEB-20 |
20 年 2 月 9 日 | 08-FEB-20 |
20 年 2 月 8 日 | 08-FEB-20 |
20 年 2 月 8 日 | 15-FEB-20 |
20 年 2 月 15 日 | 16-FEB-20 |
20 年 2 月 16 日 | 17-FEB-20 |
20 年 2 月 17 日 | 17-FEB-20 01-JAN-20 |
20 年 2 月 17 日 20 年 1 月 1 日 | 05-JAN-20 |
20 年 1 月 5 日 | 09-JAN-20 |
20 年 1 月 9 日 | 02-JAN-20 |
20 年 1 月 2 日 | 10-JAN-20 |
20 年 1 月 10 日 | 15-JAN-20 |
20 年 1 月 15 日 | 16-JAN-20 |
20 年 1 月 16 日 | 12-JAN-20
20 年 1 月 12 日
and, if you want it so that there is an overlap with any part of the ranges, then swap the GREATEST
and LEAST
:并且,如果您希望它与范围的任何部分重叠,则交换
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 );
Which outputs:哪个输出:
T1_FROM |T1_FROM | T2_FROM |
T2_FROM | T3_FROM |
T3_FROM | T4_FROM |
T4_FROM | T1_TO |
T1_TO | T2_TO |
T2_TO | T3_TO |
T3_TO | T4_TO:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- 15-MAR-20 |
T4_TO:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- |:-------- 15-MAR-20 | 16-MAR-20 |
20 年 3 月 16 日 | 15-MAR-20 |
20 年 3 月 15 日 | 16-MAR-20 |
20 年 3 月 16 日 | 18-MAR-20 |
20 年 3 月 18 日 | 18-MAR-20 |
20 年 3 月 18 日 | 17-MAR-20 |
20 年 3 月 17 日 | 19-MAR-20 10-FEB-20 |
20 年 3 月 19 日 20 年 2 月 10 日 | 09-FEB-20 |
20 年 2 月 9 日 | 08-FEB-20 |
20 年 2 月 8 日 | 08-FEB-20 |
20 年 2 月 8 日 | 15-FEB-20 |
20 年 2 月 15 日 | 16-FEB-20 |
20 年 2 月 16 日 | 17-FEB-20 |
20 年 2 月 17 日 | 17-FEB-20 01-JAN-20 |
20 年 2 月 17 日 20 年 1 月 1 日 | 05-JAN-20 |
20 年 1 月 5 日 | 01-JAN-20 |
20 年 1 月 1 日 | 02-JAN-20 |
20 年 1 月 2 日 | 10-JAN-20 |
20 年 1 月 10 日 | 15-JAN-20 |
20 年 1 月 15 日 | 02-JAN-20 |
20 年 1 月 2 日 | 12-JAN-20 01-JAN-20 |
20 年 1 月 12 日 20 年 1 月 1 日 | 05-JAN-20 |
20 年 1 月 5 日 | 09-JAN-20 |
20 年 1 月 9 日 | 02-JAN-20 |
20 年 1 月 2 日 | 10-JAN-20 |
20 年 1 月 10 日 | 15-JAN-20 |
20 年 1 月 15 日 | 16-JAN-20 |
20 年 1 月 16 日 | 12-JAN-20
20 年 1 月 12 日
If you want all values from tables that overlap with the timeperiod, then you seem to want:如果您想要与时间段重叠的表中的所有值,那么您似乎想要:
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 = ?
I don't see any "quadratic" complexity.我没有看到任何“二次”复杂性。 Exactly the same join conditions are used for each new table.
每个新表都使用完全相同的连接条件。
improving on the solution given above by @MT0 we can use a different overlap test改进@MT0 上面给出的解决方案,我们可以使用不同的重叠测试
GREATEST(t1.date_from, t2.date_from) < LEAST(t1.date_to, t2.date_to)
and extend it to any number of tables并将其扩展到任意数量的表
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, ...)
the inner join can then be replaced by one single test然后可以用一个测试替换内部连接
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);
while the outer join becomes而外连接变成
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 https://dbfiddle.uk/?rdbms=oracle_18&fiddle=e43b7c58208b7ea85af24ec0ec0ca4a7
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.