给出的数据:
inventory_num_id inventory_group_id num code
9681066 100003894 211 E
9679839 100003894 212 E
9687165 100003894 213 E
9680883 100003894 214 I
9710863 100003894 515 E
9681246 100003894 516 E
9682695 100003894 517 E
9681239 100003894 518 E
9685409 100003894 519 E
9679843 100003894 520 C
9679844 100003894 521 C
9714882 100003894 522 E
9679845 100003894 523 I
9681211 100003894 524 E
9681216 100003894 525 E
9682696 100003894 526 E
9681227 100003894 527 E
结果示例应该是:
inventory_group_id code start end
------------------ ---- ----- ----
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
我应该使用什么算子来使最小值开始并以最大值结束?你能解释一下,当最终(最大)不应该出现时我应该做些什么吗?
我能以某种方式使用GROUP BY子句吗?
答案 0 :(得分:1)
安,小心sql的黑暗面。 有不止一种方法可以做到这一点。 这是答案:
SELECT a.inventory_group_id,
a.code,
a.num AS "start",
decode(b.num,a.num,null,b.num) AS "end" FROM
( SELECT inventory_num_id,inventory_group_id,code,num
, ROW_NUMBER() OVER (PARTITION BY inventory_group_id,code ORDER BY num) AS rn
FROM inventory_num a
WHERE NOT EXISTS
( SELECT *
FROM inventory_num prev
WHERE prev.inventory_group_id = a.inventory_group_id
and PREV.CODE = a.code
AND prev.num = a.num - 1
)
) a
JOIN
( SELECT inventory_num_id,inventory_group_id,code, num
, ROW_NUMBER() OVER (PARTITION BY inventory_group_id,code ORDER BY num) AS rn
FROM inventory_num a
WHERE NOT EXISTS
( SELECT *
FROM inventory_num next
WHERE next.inventory_group_id = a.inventory_group_id
and next.CODE = a.code
AND next.num = a.num + 1
)
) b
ON b.inventory_group_id = a.inventory_group_id and b.code = a.code
AND b.rn = a.rn
order by 3;
答案 1 :(得分:1)
我在MS Sql server下测试了这个,我打赌它也可以在oracle下工作:
select max(inventory_group_id) inventory_group_id,max(code) Code ,min(num) minNum,max(num) maxNum
from
(
select inventory_group_id,inventory_num_id,code,num,
(select min(num) from DATA where num>
(select max(num) from DATA where DATA.num<a.num and code<>a.code)
) as minNum
from DATA a
) A
group by minNum
order by 3
答案 2 :(得分:1)
正如gelonsoft所说,有很多方法可以做到这一点。我宁愿只打一次桌子。这是我目前最喜欢的,基于我首次在此网站上找到的方法,可能在this answer上(您可能会发现更多方法among these questions:
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'
和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
analytic functions查找每行的任意一侧的代码。
下一个查询排除了与下一行和前一行具有相同代码的任何行;也就是说,任何处于不间断链条中间的东西。这意味着每个链最多折叠为两行,其中包含该链的起始和结束num
值。
外部查询中的case
语句使每个链的两行看起来都相同,再次使用lead
和lag
;如果只有一行(例如214
),则第二行when
的第一个case
子句会生成您想要的结束值null
。 distinct
然后移除case
创建的重复项。
我建议您分别运行查询的每个级别以查看它正在执行的操作,并了解它对前一个查询执行的操作。
正如我所说,如果我引入一行code='I'
和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