我是DB世界的初学者,我正在使用ORACLE DB,有一个具体的问题,我在网上找不到任何充分的解释。
我有一个表,它有列(VALID_FROM(date)VALID_UNTIL(date)和valid(number))VALID FROM是强制性的,而VALID_UNTIL是可空的(比如说无穷大),我想获取所有的间隔(它们可以重叠,也不需要重叠),并将它们变成各自的非重叠间隔(并将非重叠间隔中的所有计数相加)
这是正确的输入和输出的例子:输入:
| 有效期为|有效期至|计数|
| --|--|--|
| 2022年01月01日|2022年10月1日星期一| 11 |
| 2023年01月01日|NULL| 1 |
| 2023年10月1日星期一|2023年10月19日| 2 |
| 2023年10月03日|2023年10月12日| 5 |
输出量:
| 有效期为|有效期至|计数|
| --|--|--|
| 2022年01月01日|2022年10月1日星期一| 11 |
| 1.1.23| 30.9.23| 1 |
| 1.10.23| 2.10.23| 3 |
| 3.10.23| 12.10.23| 8 |
| 13.10.23| 19.10.23| 3 |
| 20.10.23| NULL| 1 |
什么是最有效的查询来实现这一点,这是迄今为止最困难的事情,我试图学习。时期是噩梦。谢谢
我试过递归,cte,没有得到任何结果
4条答案
按热度按时间t98cgbkg1#
您可以在不需要行生成器、自联接或聚合的情况下完成此操作。
将日期反透视,以便将它们放在一列中,然后使用分析函数生成计数的运行总数,当您到达“from”日期时,将值相加,当您到达“until”日期时,将值相减。然后您可以使用
LEAD
分析函数查找下一个日期,如果新的valid_from
是一个“直到”值或者如果valid_until
是一个“从”值,则它将成为范围的valid_until
值,并按一天进行调整:字符串
其中,对于样本数据:
型
产出:
| VALID_FROM|有效期至|计数|
| --|--|--|
| 2022-01-01 00:00:00| 2022-09-30 23:59:59| 11 |
| 2019 -10-01 00:00:00| 2022-10-01 23:59:59| 22 |
| 2019 -02 - 22 00:00:00| 2022-10-02 23:59:59| 11 |
| 2019 - 09 - 22 00:00:00| 2022-10-04 23:59:59| 11 |
| 2023-01-01 00:00:00| 2023-09-30 23:59:59| 1 |
| 2023-10-01 00:00:00| 2023-10-02 23:59:59| 3 |
| 2019 -10-03 00:00:00| 2023-10-12 23:59:59| 8 |
| 2023-10-13 00:00:00| 2023-10-19 23:59:59| 3 |
| 2019 -10-20 00:00:00| * 空 *| 1 |
SELECT *
更改为SELECT valid_from, TRUNC(valid_until) AS valid_until, count
。如果你想合并具有相同计数的连续范围,那么你可以将输出传递给
MATCH_RECOGNIZE
来执行逐行模式匹配:型
产出:
| VALID_FROM|有效期至|计数|
| --|--|--|
| 2022-01-01 00:00:00| 2022-09-30 23:59:59| 11 |
| 2019 -10-01 00:00:00| 2022-10-01 23:59:59| 22 |
| 2019 -02 - 22 00:00:00| 2022-10-04 23:59:59| 11 |
| 2023-01-01 00:00:00| 2023-09-30 23:59:59| 1 |
| 2023-10-01 00:00:00| 2023-10-02 23:59:59| 3 |
| 2019 -10-03 00:00:00| 2023-10-12 23:59:59| 8 |
| 2023-10-13 00:00:00| 2023-10-19 23:59:59| 3 |
| 2019 -10-20 00:00:00| * 空 *| 1 |
fiddle
oymdgrw72#
这可能不是最有效的方法-你是对的,重叠的时间段很难!-但它应该工作:
字符串
首先,我们计算出数据集中最低可用valid_from日期(假设valid_to日期永远不会早于valid_from日期)和最高日期之间的日期列表,我们将其加1(以说明valid_to为空的行),然后我们将原始数据集内部连接到这个日期列表。
接下来,我们使用一种名为
tabibitosan
的技术来为每一行生成一个组标识符。这是通过从行的对应dt中减去按dt排序的cnt组内的行号来实现的。连续且具有相同cnt值的行将以相同的差值结束,否则差值将改变。最后,我们使用组标识符对最终输出进行分组,如果原始数据集中有NULL valid_to date,则沿着一些小技巧输出NULL。
huwehgph3#
这可能也不是非常有效;它多次命中表,但不会扩展日期范围,因此可能比@boneist的方法更好或更差。
首先,根据重叠的截止日期中的真实的行和隐含的行,计算出所有的期间开始日期:
字符串
| VALID_FROM|有效期至|
| --|--|
| 2022-01-01 2022-01-01| 2022-10-01 2022-10-01|
| 2023年1月1日| * 空 *|
| 2023-10-01 2023-10-01| 2023年10月19日|
| 2023-10-03 - 01| 2023年10月12日|
| 2023-10-13 - 01 - 01 - 01| 2023年10月12日|
| 2023年10月20日| * 空 *|
我在你的表中添加了一个ID列,以避免行在
exist
子句中匹配自己。如果你没有唯一的标识符,那么你可以使用valid_from
和valid_to
,只要它们的组合是唯一的。然后根据周围的行,使用前面的查询作为CTE,计算出每个起始日期的有效截止日期:
型
| VALID_FROM|有效期至|
| --|--|
| 2022-01-01 2022-01-01| 2022-10-01 2022-10-01|
| 2023年1月1日|2023-09-30 -09- 09 - 09|
| 2023-10-01 2023-10-01| 2023-10-02 2023-10-02|
| 2023-10-03 - 01| 2023年10月12日|
| 2023-10-13 - 01 - 01 - 01| 2023年10月19日|
| 2023年10月20日| * 空 *|
然后使用它作为另一个CTE,并连接回真实的数据,以获取匹配计数并求和:
型
或全文:
型
| VALID_FROM|有效期至|CNT|
| --|--|--|
| 2022-01-01 2022-01-01| 2022-10-01 2022-10-01| 11 |
| 2023年1月1日|2023-09-30 -09- 09 - 09| 1 |
| 2023-10-01 2023-10-01| 2023-10-02 2023-10-02| 3 |
| 2023-10-03 - 01| 2023年10月12日| 8 |
| 2023-10-13 - 01 - 01 - 01| 2023年10月19日| 3 |
| 2023年10月20日| * 空 *| 1 |
fiddle带有一些额外的查询,显示了一些工作。
tpgth1q74#
如果你准备好可能的间隔列表并测试交叉点,我可能会更容易:
字符串