it-swarm.com.de

Wie kann ich eine fehlende Nummer aus einer Reihe von Nummern prüfen?

Ich mache ein Projekt, das ein Aufnahmesystem für ein College schafft. Die Technologien sind Java und Oracle. 

In einer der Tabellen werden vorgenerierte Seriennummern gespeichert. Gegen diese Seriennummern werden später die Formulardaten des Antragstellers eingegeben. Meine Forderung ist, dass nach Abschluss des Einreichungsprozesses ein lotweiser Bericht erstellt werden muss. Wenn während der Zuführung vorgenerierter Seriennummern keine Sequenznummern fehlen. 

Zum Beispiel, in einer Tabelle, sind die Folgenummern 7001, 7002, 7004, 7005, 7006, 7010 . Aus der obigen Serie ist klar, dass von 7001 bis 7010 die fehlenden Nummern 7003, 7007, 7008 und 7009 sind

Gibt es in Oracle eine DBMS-Funktion, um diese Zahlen herauszufinden, oder falls eine gespeicherte Prozedur meinen Zweck erfüllt, schlagen Sie bitte einen Algorithmus vor. 

Ich kann einige Techniken in Java finden, aber aus Gründen der Schnelligkeit möchte ich die Lösung in Oracle finden.

22
Samcoder

Eine Lösung ohne Hardcoding der 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

Ergebnisse:

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

4 rows selected.
40
Rob van Wijk

Versuche dies:

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)

Hier ist das Ergebnis für die Sequenz 7001, 7002, 7004, 7005, 7006, 7010:

From  To
7003  7003
7007  7009
13
Patrick

Das funktioniert bei Postgres> = 8.4. Mit einigen geringfügigen Änderungen an der CTE-Syntax könnte es auch für Oracle und Microsoft funktionieren.

-- 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

Dies hat funktioniert, aber die erste Sequenz (Startwert) wird ausgewählt, da sie keinen Vorgänger hat. In SQL Server getestet, sollte jedoch in Oracle funktionieren

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

Hier ist ein Testergebnis

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

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

Um eine nicht zugewiesene Sequenz zu erhalten, machen Sie einfach value[i] - 1, wobei i die erste Zeile größer ist, z. (7004 - 1 = 7003 and 7007 - 1 = 7006) welche Sequenzen verfügbar sind

Ich denke, Sie können diese einfache Abfrage verbessern

1
codingbiz

Ich hätte connect by level vorgeschlagen, da Stefan dies getan hat Sie können jedoch keine Unterabfrage in dieser Anweisung verwenden, was bedeutet, dass sie nicht wirklich für Sie geeignet ist, da Sie wissen müssen, was das Maximum und das Minimum ist Werte Ihrer Sequenz sind.

Ich würde vorschlagen, dass eine Pipeline-Tabellenfunktion der beste Weg ist, um die Zahlen zu erzeugen, die Sie für den Join benötigen. Damit dies funktioniert, benötigen Sie ein Objekt in Ihrer Datenbank, in das die Werte zurückgegeben werden:

create or replace type t_num_array as table of number;

Dann die Funktion:

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;
/

Bei Verwendung dieser Funktion würde das Folgende eine Liste von Seriennummern zwischen dem Minimum und dem Maximum zurückgeben.

select * from table(generate_serial_nos);

Was bedeutet, dass Ihre Abfrage, um herauszufinden, welche Seriennummern fehlen, zu werden:

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

Hier ist eine Lösung, die:

  • Stützt sich auf die LAG-Funktion von Oracle
  • Erfordert keine Kenntnis der gesamten Sequenz (erkennt jedoch nicht, ob die erste oder letzte Nummer in der Sequenz fehlt).
  • Listet die Werte auf, die die fehlenden Nummernlisten umgeben
  • Listet die fehlenden Nummernlisten als zusammenhängende Gruppen auf (möglicherweise für die Berichterstellung geeignet).
  • Bei sehr großen Listen fehlender Zahlen aufgrund von Listagg-Einschränkungen tragisch fehlgeschlagen

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*/
;

Ausgabe:

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

Eine einfache Möglichkeit, um eine Antwort auf Ihr Szenario zu erhalten, ist folgende:

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;

Die Auswahl gibt Ihnen die Antwort aus Ihrem Beispiel. Das macht nur Sinn, wenn Sie vorher wissen, in welchem ​​Bereich Ihre Zahlen liegen und der Bereich nicht zu groß sein sollte. Die erste Zahl muss der Versatz im ROWNUM-Teil sein und die Länge der Sequenz ist die Begrenzung der Ebene im connect by-Teil.

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