Comme gelonsoft a noté, il y a plusieurs façons de faire ce. Je préférerais ne frapper qu'une seule fois la table. Ceci est mon favori actuel, basé sur une méthode j'ai trouvé sur ce site, probablement this answer (et vous pouvez trouver beaucoup d'autres approches 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
La sélection intérieure est fait tout le travail, en créant des regroupements artificiels basé sur les valeurs code
et num
- exécuter ce seul pour voir ce qu'il fait.La requête suivante est de regrouper les groupes pour trouver le plus bas et le plus élevé num
pour chacun des groupes artificiels.La dernière requête externe est purement pour faire la end
valeur null
si la chaîne n'a qu'un seul lien - ie start
et end
sont les mêmes - que vous exprimé vous vouliez.
Ce que vous n'avez pas dit, c'est si les valeurs num
doivent être contiguës. Si j'ajoute un enregistrement avec code='I'
et num=216
, je reçois le même nombre de sorties, mais 214-216
est traitée comme une chaîne, même si il n'y a pas de valeur 215
entre:
INVENTORY_GROUP_ID C START_NUM END_NUM
------------------ - ---------- ----------
100003894 E 211 213
100003894 I 214 216
...
Je n'ai pas compris comment pour adapter cette méthode row_number()
pour tenir compte de cela et les traiter comme des chaînes séparées - je serais intéressé de voir si cela peut être fait tout en restant simple. La même chose arrive avec les autres réponses données; mais je ne suis pas sûr que cela compte pour vous.
Si c'est le cas, voici une autre version qui ne frappe la table qu'une seule fois; un peu plus compliqué et sous cette forme a le même problème potentiel avec num
non contiguës valeurs:
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
la requête interne sélectionne à partir de la table, et utilise le lead
et lag
analytic functions pour trouver le code de chaque côté de chaque rangée .
La requête suivante exclut toutes les lignes ayant le code comme lignes suivantes et suivantes; c'est-à-dire tout ce qui se trouve au milieu d'une chaîne ininterrompue.Cela signifie que chaque chaîne est réduite à au plus deux lignes, avec les valeurs num
de début et de fin pour cette chaîne.
L'instruction case
dans la requête externe rend les deux lignes de chaque chaîne identiques, en utilisant à nouveau lead
et lag
; s'il n'y a qu'une seule ligne (par exemple pour 214
), la première clause when
de la seconde case
fait la valeur de fin null
, ce que vous avez dit que vous vouliez. Le distinct
supprime ensuite les doublons créés par le case
.
Je suggère que vous exécutez chaque niveau de la requête séparément pour voir ce qu'il fait, et comprendre ce qu'il fait à la précédente.
Comme je l'ai dit, cela a le même problème potentiel si je présente une ligne avec code='I'
et num=216
:
INVENTORY_GROUP_ID C START_NUM END_NUM
------------------ - ---------- ----------
100003894 E 211 213
100003894 I 214 216
...
Cela peut être divisé en deux chaînes en adaptant cette méthode, mais il est un peu plus compliqué que vous devez suivre et de comparer les valeurs num
ainsi que les valeurs 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
comment voulez-vous identifier le début et la fin? – Taryn
@bluefeet: Je pense que c'est quand le code change (ordonné par num). –
Exemples de résultats de ** quoi **? Qu'essayez-vous de faire? [Qu'avez-vous essayé?] (Http://whathaveyoutried.com) –