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
各人工グループの最低値と最高値を見つけることです。最後の外部クエリは、チェーンにリンクが1つしかない場合、つまり、とが同じである場合にend
値を作成することです。null
start
end
あなたが言っていないのは、num
値が連続している必要があるかどうかです。とを使用してレコードを追加するcode='I'
とnum=216
、同じ数の出力が得られますが、間に値214-216
がない場合でも、1つのチェーンとして扱われます。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
分析関数を使用して、各行の両側のコードを検索します。
次のクエリ出力では、次の行と前の行の両方と同じコードを持つ行が除外されます。つまり、途切れないチェーンの真ん中にあるものすべてです。つまり、各チェーンは最大2行に折りたたまれ、num
そのチェーンの開始値と終了値が含まれます。
外側のクエリのステートメントは、を使用しcase
て、各チェーンの両方の行を同じように見せます。行が1つしかない場合(たとえば)、2番目の最初の句が最終値になります。これはあなたが望むと言ったものです。次に、作成された重複を削除します。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
...
この方法を採用することで、これを2つのチェーンに分割できますが、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