正如gelonsoft 所指出的,有很多方法可以做到这一点。我宁愿只打一次桌子。这是我目前最喜欢的,基于我第一次在这个网站上找到的方法,可能是在这个答案上(你可能会在这些问题中找到更多的方法:
select inventory_group_id, code, start_num,
case when end_num = start_num then null else end_num end as end_num
from (
select inventory_group_id, code, min(num) as start_num, max(num) as end_num
from (
select inventory_group_id, num, code,
row_number() over (order by num)
- row_number() over (partition by code order by num) as chain
from inventory_num
)
group by inventory_group_id, code, chain
)
order by 1,3;
INVENTORY_GROUP_ID C START_NUM END_NUM
------------------ - ---------- ----------
100003894 E 211 213
100003894 I 214
100003894 E 515 519
100003894 C 520 521
100003894 E 522
100003894 I 523
100003894 E 524 527
内部选择通过基于code
和num
值创建人工分组来完成所有工作 - 自行运行以查看它在做什么。下一个查询是折叠组以找到num
每个人工组的最低和最高。最后的外部查询纯粹是为了如果链只有一个链接 - 即和是相同的 - 你提到你想要的end
值。null
start
end
你没有说的是这些num
值是否必须是连续的。如果我用code='I'
and添加一条记录num=216
,我得到相同数量的输出,但214-216
被视为一个链,即使215
两者之间没有值:
INVENTORY_GROUP_ID C START_NUM END_NUM
------------------ - ---------- ----------
100003894 E 211 213
100003894 I 214 216
...
我还没有想出如何调整这种row_number()
方法以考虑到这一点并将它们视为单独的链 - 我很想看看它是否可以在保持简单的同时完成。给出的其他答案也会发生同样的事情;但我不确定这对你是否重要。
如果是这样,这是另一个版本,它只上桌一次;相当复杂,并且在这种形式下,与非连续num
值具有相同的潜在问题:
select distinct inventory_group_id, code,
case
when prev_code = code then lag(num) over (order by rn)
else num
end as start_num,
case
when next_code != code and prev_code != code then null
when next_code = code then lead(num) over (order by rn)
else num
end as end_num
from (
select inventory_group_id, num, code, prev_code, next_code,
rownum as rn
from (
select inventory_group_id, num, code,
lag(code) over (partition by inventory_group_id
order by num) as prev_code,
lead(code) over (partition by inventory_group_id
order by num) as next_code
from inventory_num
)
where (prev_code is null or code != prev_code)
or (next_code is null or code != next_code)
order by 1,2,3
)
order by 1,3,2;
INVENTORY_GROUP_ID C START_NUM END_NUM
------------------ - ---------- ----------
100003894 E 211 213
100003894 I 214
100003894 E 515 519
100003894 C 520 521
100003894 E 522
100003894 I 523
100003894 E 524 527
内部查询从表中进行选择,并使用lead
和lag
分析函数在每一行的任一侧查找代码。
下一个查询排除与下一行和上一行具有相同代码的任何行;也就是说,任何位于不间断链中间的东西。这意味着每个链最多折叠到两行,其中包含num
该链的开始值和结束值。
外部查询中的语句通过使用和再次case
使每个链的两行看起来相同;如果只有一行(例如 for ),则第二行的第一个子句构成最终值,你说你想要的。然后删除创建的重复项。lead
lag
214
when
case
null
distinct
case
我建议您分别运行每个级别的查询,以查看它在做什么,并了解它对上一个级别的操作。
code='I'
正如我所说,如果我用and引入一行,这会产生同样的潜在问题num=216
:
INVENTORY_GROUP_ID C START_NUM END_NUM
------------------ - ---------- ----------
100003894 E 211 213
100003894 I 214 216
...
通过采用这种方法可以将其分成两个链,但它有点复杂,因为您必须跟踪和比较num
值以及code
值:
select distinct inventory_group_id, code,
case
when prev_num is null then num
when prev_code = code then lag(num) over (order by rn)
else num
end as start_num,
case
when next_code != code and prev_code != code then null
when next_code is null then num
when next_num is null then null
when next_code = code then lead(num) over (order by rn)
else num
end as end_num
from (
select inventory_group_id, num, code, prev_code, next_code,
case
when prev_num != num - 1 then null
else prev_num
end as prev_num,
case
when next_num != num + 1 then null
else next_num
end as next_num,
rownum as rn
from (
select inventory_group_id, num, code,
lag(code) over (partition by inventory_group_id
order by num) as prev_code,
lead(code) over (partition by inventory_group_id
order by num) as next_code,
lag(num) over (partition by inventory_group_id
order by num) as prev_num,
lead(num) over (partition by inventory_group_id
order by num) as next_num
from inventory_num
)
where (prev_code is null or code != prev_code)
or (next_code is null or code != next_code)
or (prev_num is null or num != prev_num + 1)
or (next_num is null or num != next_num - 1)
order by 1,2,3
)
order by 1,3,2;
INVENTORY_GROUP_ID C START_NUM END_NUM
------------------ - ---------- ----------
100003894 E 211 213
100003894 I 214
100003894 I 216
100003894 E 515 519
100003894 C 520 521
100003894 E 522
100003894 I 523
100003894 E 524 527