2012-12-27 2 views
1

Je cherche un moyen de trouver des nombres manquants dans une gamme. J'ai une colonne de numéro de début et une colonne de numéro de fin dans la même table. J'essaie d'obtenir les numéros sautés. Je peux obtenir le prochain numéro ignoré, mais je ne sais pas comment obtenir une liste des numéros qui n'étaient pas dans la gamme. J'ai une table de nombres si cela serait utile.Comment trouver les numéros manquants entre 2 colonnes?

Voici mon exemple:

doc_num_begin doc_num_end 
------------- ----------- 
20000007  20000008 
20000011  20000015 
20000016  20000017 

Je voudrais obtenir 20000009,20000010. J'ai cherché mais je n'arrive pas à trouver comment faire cela en utilisant les colonnes de début et de fin.

Merci

+0

Combien de nombres plus grands peuvent être manqués? –

+0

L'utilisateur fournirait la gamme telle que 20000007-20000100. –

Répondre

4

Si vous avez une table de nombres, alors c'est assez facile:

select n.num 
from Numbers n left outer join 
    RangeTable rt 
    on n.number between rt.doc_num_begin and doc_num_end 
where rt.doc_num_begin is null 

Cela fait une jointure externe gauche des numéros à la table de plage, puis garder ceux qui ne correspond pas.

Bien que très facile à exprimer, la performance sera probablement plutôt mauvaise en raison de la non-équivalence. Vous pouvez également mettre des conditions dans la table des nombres, donc vous ne commencez pas à 0, 1,. . ., Lorsque les plages commencent à 20000007. Vous feriez cela comme:

select n.num 
from Numbers n join 
    (select MIN(doc_num_begin) as MinVal, MAX(doc_num_end) as MaxVal from RangeTable) const 
    on n.number between const.MinVal and const.MaxVal left outer join 
    RangeTable rt 
    on n.number between rt.doc_num_begin and doc_num_end 
where rt.doc_num_begin is null 
+0

Merci Gordon. J'ai essayé ça et ça a l'air bien. –

+0

Les plages de la table peuvent commencer à un point assez large, non couvert par la table des nombres. Mais cela devrait être facile à contourner, en changeant les conditions de jointure dans la deuxième requête comme ceci: 'sur n.number entre 0 et const.MaxVal - const.MinVal' et, la deuxième condition,' sur n.number entre rt .doc_num_begin - const.MinVal et rt.doc_num_end - const.MinVal'. Cela exigerait toujours que la différence entre const.MinVal et const.MaxVal ne dépasse pas le nombre maximum dans le tableau des nombres, bien sûr. –

0

Vous pouvez utiliser le numéro de séquence connue dans une base de données de table ou de l'échantillon à cette fin pour filtrer avec cet ID. Croix En rejoignant cet identifiant, vous étendez la limite que vous recherchez.

SELECT i from (select (w2.WorkOrderID-1)+(w1.WorkOrderID-1)*10000 as i 
from AdventureWorks.Production.WorkOrder w1 
cross join AdventureWorks.Production.WorkOrder w2 
where w1.WorkOrderID<10000 and w2.WorkOrderID<10000) as MyNumbers 
WHERE i BETWEEN @StartRange and @EndRange 
and not exists (SELECT 1 FROM MyTable 
WHERE i BETWEEN doc_num_begin doc_num_end) 
1

Si vous avez juste besoin de trouver les gammes manquantes, vous pouvez utiliser cette requête:

SELECT 
    t1.doc_num_end + 1 as start_missing_range, 
    MIN(t2.doc_num_begin) - 1 as end_missing_range 
FROM 
    your_table t1 INNER JOIN your_table t2 
    ON t1.doc_num_end < t2.doc_num_begin 
GROUP BY 
    t1.doc_num_end 
HAVING 
    MIN(t2.doc_num_begin) - t1.doc_num_end > 1 

EDIT: Et cette requête pourrait être utilisée pour développer une gamme:

SELECT num+start_missing_range 
FROM 
    (select 0 as num 
    union all select 1 as num 
    union all select 2 as num 
    union all select 3 as num 
    union all select 4 as num 
    union all select 5 as num 
    union all select 6 as num 
    union all select 7 as num 
    union all select 8 as num 
    union all select 9 as num) numbers inner join 
    (SELECT 
    t1.doc_num_end + 1 as start_missing_range, 
    MIN(t2.doc_num_begin) - 1 as end_missing_range 
    FROM 
    your_table t1 INNER JOIN your_table t2 
     ON t1.doc_num_end < t2.doc_num_begin 
    GROUP BY 
    t1.doc_num_end 
    HAVING 
    MIN(t2.doc_num_begin) - t1.doc_num_end > 1) rg 
    on end_missing_range-start_missing_range>=numbers.num 

(cela ne fonctionnera que si une plage contient au maximum 10 chiffres, elle pourrait facilement être étendue à d'autres ... bien sûr, il y aura toujours une limite, mais au moins vous ne devez pas d une table avec tous les nombres)

+0

Cela peut être étendu en utilisant une table numérique pour "déplier" les plages. –

+0

@AndriyM j'ai étendu un peu ma réponse, pour déplier les plages (limité à 10 chiffres) – fthiella

+0

Je suis d'accord que vous pourriez faire sans une table de nombres permanents. Pourtant, une table de nombres peut avoir de nombreuses utilisations, et donc la persistance d'un ensemble de nombres serait probablement justifiée de toute façon. Même s'il s'agissait d'une seule requête, mais souvent appelée, qui en avait besoin, vous pouvez toujours trouver utile d'utiliser un tableau de nombres * indexé *.Quoi qu'il en soit, votre approche m'a paru valoir un upvote même avant votre mise à jour, donc, bon travail! :) –

Questions connexes