2012-08-02 1 views
3

Compte tenu des données:Oracle SQL. Quelle déclaration dois-je utiliser

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 

exemples de résultat devrait être comme:

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 

Quel opérateur dois-je utiliser pour faire démarrer comme minimum et mettre fin à valeur max? Et pourriez-vous s'il vous plaît expliquer ce que je devrais faire quand la fin (maximum) n'est pas censé se présenter? Est-ce que je peux utiliser la clause GROUP BY d'une certaine manière?

+0

comment voulez-vous identifier le début et la fin? – Taryn

+2

@bluefeet: Je pense que c'est quand le code change (ordonné par num). –

+0

Exemples de résultats de ** quoi **? Qu'essayez-vous de faire? [Qu'avez-vous essayé?] (Http://whathaveyoutried.com) –

Répondre

1

Ann, soyez prudent sur le côté sombre de sql. Il y a plusieurs façons de le faire. Voici la réponse:

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

Je l'ai testé sous ce serveur MS Sql et je parie que cela fonctionnera sous Oracle aussi:

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 
1

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 laganalytic 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 
Questions connexes