英文:
intersection across date ranges from multiple rows in oracle
问题
在Oracle中跨行查找日期范围的交集。
我有一张包含以下记录的表格:
Item_no | item_type | active_from | active_to | rule_id |
---|---|---|---|---|
10001 | SAR | 2020-01-01 | 2023-01-01 | rule1 |
10001 | SAR. | 2024-01-01 | 9999-12-31 | rule1 |
10001 | SAR | 2020-05-01 | 2021-06-01 | rule2 |
10001 | SAR | 2021-01-01 | 2021-02-01 | rule2 |
我们需要找到规则ID之间的共同日期。
输出将是:
Item_no | item_type | active_from | active_to |
---|---|---|---|
10001 | SAR | 2020-05-01 | 2021-06-01 |
我尝试使用connect by level
生成日期,然后取交集,但由于9999-12-31,运行时间很长。
英文:
Intersection of date ranges across rows in oracle.
I have a table which contains following records
Item_no | item_type | active_from | active_to | rule_id |
---|---|---|---|---|
10001 | SAR | 2020-01-01 | 2023-01-01 | rule1 |
10001 | SAR. | 2024-01-01 | 9999-12-31 | rule1 |
10001 | SAR | 2020-05-01 | 2021-06-01 | rule2 |
10001 | SAR | 2021-01-01 | 2021-02-01 | rule2 |
We need to find common dates between rule ids
Output will be
Item_no | item_type | active_from | active_to |
---|---|---|---|
10001 | SAR | 2020-05-01 | 2021-06-01 |
I tried with connect by level to generate dates and then take intersection, but it is running for long time due to 9999-12-31
答案1
得分: 1
从Oracle 12开始,您可以使用UNPIVOT将日期转换,然后使用分析函数和MATCH_RECOGNIZE逐行处理结果集,以找到同时满足两个规则的连续行:
SELECT *
FROM (
SELECT item_no,
item_type,
rule_id,
dt,
SUM(CASE rule_id WHEN 'rule1' THEN active END) OVER (
PARTITION BY item_no, item_type ORDER BY dt, ACTIVE DESC
) AS rule1,
SUM(CASE rule_id WHEN 'rule2' THEN active END) OVER (
PARTITION BY item_no, item_type ORDER BY dt, ACTIVE DESC
) AS rule2
FROM table_name
UNPIVOT (
dt FOR active IN ( active_from AS 1, active_to AS -1 )
)
)
MATCH_RECOGNIZE(
PARTITION BY item_no, item_type
ORDER BY dt, rule1 DESC, rule2 DESC
MEASURES
FIRST(dt) AS active_from,
NEXT(dt) AS active_to
PATTERN ( active_rules+ )
DEFINE active_rules AS rule1 > 0 AND rule2 > 0
)
对于样本数据:
CREATE TABLE table_name (Item_no, item_type, active_from, active_to, rule_id) AS
SELECT 10001, 'SAR', DATE '2020-01-01', DATE '2023-01-01', 'rule1' FROM DUAL UNION ALL
SELECT 10001, 'SAR', DATE '2024-01-01', DATE '9999-12-31', 'rule1' FROM DUAL UNION ALL
SELECT 10001, 'SAR', DATE '2020-05-01', DATE '2021-06-01', 'rule2' FROM DUAL UNION ALL
SELECT 10001, 'SAR', DATE '2021-01-01', DATE '2021-02-01', 'rule2' FROM DUAL;
输出结果为:
ITEM_NO | ITEM_TYPE | ACTIVE_FROM | ACTIVE_TO |
---|---|---|---|
10001 | SAR | 2020-05-01 00:00:00 | 2021-06-01 00:00:00 |
对于:
CREATE TABLE table_name (Item_no, item_type, active_from, active_to, rule_id) AS
SELECT 10001, 'SPR', DATE '2023-01-01', DATE '2023-01-31', 'rule1' FROM DUAL UNION ALL
SELECT 10001, 'SPR', DATE '2023-01-31', DATE '2023-02-27', 'rule2' FROM DUAL;
输出结果为:
ITEM_NO | ITEM_TYPE | ACTIVE_FROM | ACTIVE_TO |
---|---|---|---|
10001 | SPR | 2023-01-31 00:00:00 | 2023-01-31 00:00:00 |
英文:
From Oracle 12, you can UNPIVOT
the dates and then use analytic functions and MATCH_RECOGNIZE
to process the result set row-by-row to find the consecutive rows where both rules are active:
SELECT *
FROM (
SELECT item_no,
item_type,
rule_id,
dt,
SUM(CASE rule_id WHEN 'rule1' THEN active END) OVER (
PARTITION BY item_no, item_type ORDER BY dt, ACTIVE DESC
) AS rule1,
SUM(CASE rule_id WHEN 'rule2' THEN active END) OVER (
PARTITION BY item_no, item_type ORDER BY dt, ACTIVE DESC
) AS rule2
FROM table_name
UNPIVOT (
dt FOR active IN ( active_from AS 1, active_to AS -1 )
)
)
MATCH_RECOGNIZE(
PARTITION BY item_no, item_type
ORDER BY dt, rule1 DESC, rule2 DESC
MEASURES
FIRST(dt) AS active_from,
NEXT(dt) AS active_to
PATTERN ( active_rules+ )
DEFINE active_rules AS rule1 > 0 AND rule2 > 0
)
Which, for the sample data:
CREATE TABLE table_name (Item_no, item_type, active_from, active_to, rule_id) AS
SELECT 10001, 'SAR', DATE '2020-01-01', DATE '2023-01-01', 'rule1' FROM DUAL UNION ALL
SELECT 10001, 'SAR', DATE '2024-01-01', DATE '9999-12-31', 'rule1' FROM DUAL UNION ALL
SELECT 10001, 'SAR', DATE '2020-05-01', DATE '2021-06-01', 'rule2' FROM DUAL UNION ALL
SELECT 10001, 'SAR', DATE '2021-01-01', DATE '2021-02-01', 'rule2' FROM DUAL;
Outputs:
ITEM_NO | ITEM_TYPE | ACTIVE_FROM | ACTIVE_TO |
---|---|---|---|
10001 | SAR | 2020-05-01 00:00:00 | 2021-06-01 00:00:00 |
and for:
CREATE TABLE table_name (Item_no, item_type, active_from, active_to, rule_id) AS
SELECT 10001, 'SPR', DATE '2023-01-01', DATE '2023-01-31', 'rule1' FROM DUAL UNION ALL
SELECT 10001, 'SPR', DATE '2023-01-31', DATE '2023-02-27', 'rule2' FROM DUAL;
The output is:
ITEM_NO | ITEM_TYPE | ACTIVE_FROM | ACTIVE_TO |
---|---|---|---|
10001 | SPR | 2023-01-31 00:00:00 | 2023-01-31 00:00:00 |
答案2
得分: 0
当寻找相同物品的重叠日期范围,但使用不同规则时,请使用以下代码:
select
a.item_no,
a.item_type,
greatest(a.active_from, b.active_from) as active_from,
least(a.active_to, b.active_to) as active_to
from mytable a
join mytable b on b.item_no = a.item_no
and b.item_type = a.item_type
and b.rule <> a.rule
and b.active_from <= a.active_to
and b.active_to >= a.active_from;
这是用于查找重叠日期范围的SQL查询。
英文:
You are looking for overlapping date ranges for the same item, but a different rule.
When do two date ranges A and B overlap? Answer: when the start(B) <= end(A) AND end(B) >= start(A). With this knowledge we can join. The overlapping range is just the greater start date until the lesser end date.
select
a.item_no,
a.item_type,
greatest(a.active_from, b.active_from) as active_from,
least(a.active_to, b.active_to) as active_to
from mytable a
join mytable b on b.item_no = a.item_no
and b.item_type = a.item_type
and b.rule <> a.rule
and b.active_from <= a.active_to
and b.active_to >= a.active_from;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论