web-dev-qa-db-fra.com

Comment vérifier un nombre manquant dans une série de chiffres?

Je suis en train de créer un système d'admission pour un collège. les technologies sont Java et Oracle. 

Dans l'une des tables, les numéros de série pré-générés sont stockés. Par la suite, en fonction de ces numéros de série, les données de formulaire du demandeur seront entrées. Mon exigence est que lorsque le processus d’inscription soit terminé, je devrai générer un rapport détaillé. Si lors de l’alimentation des numéros de série pré-générés, des numéros de séquence ont disparu. 

Par exemple, disons dans un tableau, les numéros de séquence sont 7001, 7002, 7004, 7005, 7006, 7010 . Dans la série ci-dessus, il est clair que de 7001 à 7010 les numéros manquants sont 7003, 7007, 7008 et 7009

Existe-t-il une fonction SGBD disponible dans Oracle pour connaître ces numéros ou si une procédure stockée peut remplir mon objectif, veuillez suggérer un algorithme. 

Je peux trouver des techniques en Java, mais pour la rapidité, je veux trouver la solution dans Oracle.

22
Samcoder

Une solution sans coder en dur le 9:

select min_a - 1 + level
     from ( select min(a) min_a
                 , max(a) max_a
              from test1
          )
  connect by level <= max_a - min_a + 1
    minus
   select a
     from test1

Résultats:

MIN_A-1+LEVEL
-------------
         7003
         7007
         7008
         7009

4 rows selected.
40
Rob van Wijk

Essaye ça:

SELECT t1.SequenceNumber + 1 AS "From",
       MIN(t2.SequenceNumber) - 1 AS "To"
FROM MyTable t1
JOIN MyTable t2 ON t1.SequenceNumber < t2.SequenceNumber 
GROUP BY t1.SequenceNumber
HAVING t1.SequenceNumber + 1 < MIN(t2.SequenceNumber)

Voici le résultat pour la séquence 7001, 7002, 7004, 7005, 7006, 7010:

From  To
7003  7003
7007  7009
13
Patrick

Cela fonctionne sur postgres> = 8.4. Avec quelques légères modifications de la syntaxe CTE, il pourrait également être adapté à Oracle et Microsoft.

-- EXPLAIN ANALYZE
WITH missing AS (
    WITH RECURSIVE fullhouse AS (
        SELECT MIN(num)+1 as num
        FROM numbers n0
        UNION ALL SELECT 1+ fh0.num AS num
        FROM fullhouse fh0
        WHERE EXISTS (
                SELECT * FROM numbers ex
                WHERE ex.num > fh0.num
                )
        )
        SELECT * FROM fullhouse fh1
        EXCEPT ( SELECT num FROM numbers nx)
        )
SELECT * FROM missing;
2
wildplasser

Cela a fonctionné mais sélectionne la première séquence (valeur de départ) car il n’a pas de prédécesseur. Testé sous SQL Server mais devrait fonctionner sous Oracle

SELECT
    s.sequence  FROM seqs s
WHERE
    s.sequence - (SELECT sequence FROM seqs WHERE sequence = s.sequence-1) IS NULL

Voici un résultat de test

  Table
  -------------
  7000
  7001
  7004
  7005
  7007
  7008

  Result
  ----------
  7000
  7004
  7007

Pour obtenir une séquence non assignée, il suffit de faire value[i] - 1 où i est supérieur à la première ligne, par exemple. (7004 - 1 = 7003 and 7007 - 1 = 7006) qui sont des séquences disponibles

Je pense que vous pouvez améliorer cette requête simple

1
codingbiz

J'aurais suggéré connect by level comme Stefan l'a fait , cependant, vous ne pouvez pas utiliser de sous-requête dans cette instruction, ce qui signifie que cela ne vous convient pas vraiment, car vous devez savoir ce que le maximum et le minimum les valeurs de votre séquence sont.

Je suggérerais une fonction de tableau bordée de tubes pourrait être le meilleur moyen de générer les nombres dont vous avez besoin pour effectuer la jointure. Pour que cela fonctionne, vous avez besoin d'un objet dans votre base de données pour renvoyer les valeurs à:

create or replace type t_num_array as table of number;

Puis la fonction:

create or replace function generate_serial_nos return t_num_array pipelined is

   l_first number;
   l_last number;

begin

   select min(serial_no), max_serial_no)
     into l_first, l_last 
     from my_table
          ;

   for i in l_first .. l_last loop
      pipe row(i);
   end loop;

   return;

end generate_serial_nos;
/

En utilisant cette fonction, ce qui suit renverrait une liste de numéros de série, entre le minimum et le maximum.

select * from table(generate_serial_nos);

Ce qui signifie que votre requête pour savoir quels numéros de série sont manquants devient:

select serial_no
  from ( select * 
           from table(generate_serial_nos) 
                ) generator 
  left outer join my_table actual
    on generator.column_value = actual.serial_no
 where actual.serial_no is null
1
Ben

Voici une solution qui:

  • S'appuie sur la fonction LAG d'Oracle
  • Ne nécessite pas la connaissance de la séquence complète (mais ne détecte donc pas si le premier ou le dernier numéro de la séquence a été oublié)
  • Répertorie les valeurs entourant les listes de nombres manquantes
  • Répertorie les listes de numéros manquantes en tant que groupes contigus (peut-être pratique pour la génération de rapports)
  • Échec tragique pour les très grandes listes de numéros manquants, en raison des limitations de listagg

SQL: 

WITH MentionedValues /*this would just be your actual table, only defined here to provide data for this example */
        AS (SELECT *
              FROM (    SELECT LEVEL + 7000 seqnum
                          FROM DUAL
                    CONNECT BY LEVEL <= 10000)
             WHERE seqnum NOT IN (7003,7007,7008,7009)--omit those four per example
                                       ),
     Ranges /*identifies all ranges between adjacent rows*/
        AS (SELECT seqnum AS seqnum_curr,
                   LAG (seqnum, 1) OVER (ORDER BY seqnum) AS seqnum_prev,
                   seqnum - (LAG (seqnum, 1) OVER (ORDER BY seqnum)) AS diff
              FROM MentionedValues)
SELECT Ranges.*,
       (    SELECT LISTAGG (Ranges.seqnum_prev + LEVEL, ',') WITHIN GROUP (ORDER BY 1)
              FROM DUAL
        CONNECT BY LEVEL < Ranges.diff) "MissingValues" /*count from lower seqnum+1 up to lower_seqnum+(diff-1)*/
  FROM Ranges
 WHERE diff != 1 /*ignore when diff=1 because that means the numers are sequential without skipping any*/
;

Sortie:

SEQNUM_CURR SEQNUM_PREV DIFF MissingValues
7004        7002        2    "7003" 
7010        7006        4    "7007,7008,7009"                  
1
James Daily

Voici un moyen simple d'obtenir votre réponse à votre scénario:

create table test1 ( a number(9,0));

insert into test1 values (7001);
insert into test1 values (7002);
insert into test1 values (7004);
insert into test1 values (7005);
insert into test1 values (7006);
insert into test1 values (7010);
commit;

select n.n from (select ROWNUM + 7001 as n from dual connect by level <= 9) n 
   left join test1 t on n.n = t.a where t.a is null;

La sélection vous donnera la réponse de votre exemple. Cela n'a de sens que si vous savez à l'avance dans quelle fourchette se trouvent vos chiffres et que la fourchette ne devrait pas être trop grande. Le premier nombre doit être le décalage dans la partie ROWNUM et la longueur de la séquence est la limite du niveau dans la partie connect by.

1
Stefan
 SELECT ROWNUM "Missing_Numbers" FROM dual CONNECT BY LEVEL <= (SELECT MAX(a) FROM test1)
 MINUS
 SELECT a FROM test1 ;
0
Rajat Srivastava