2012-06-10 15 views
13

Ich mache ein Projekt, das ein Zulassungssystem für ein College schafft; Die Technologien sind Java und Oracle.Wie überprüft man fehlende Nummern aus einer Reihe von Nummern?

In einer der Tabellen sind vorgenerierte Seriennummern gespeichert. Später werden mit diesen Seriennummern die Formulardaten des Antragstellers eingegeben. Meine Anforderung ist, dass nach Abschluss des Eintrags ein Lot-Bericht erstellt werden muss. Wenn während der Einspeisung vorgenerierter Seriennummern keine Sequenznummern verloren gingen. Zum Beispiel, sagen wir in einer Tabelle, sind die Sequenznummern 7001, 7002, 7004, 7005, 7006, 7010. Aus der obigen Reihe ist klar, dass von 7001 bis 7010 die fehlenden Nummern 7003, 7007, 7008 sind und 7009

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

Ich kann einige Techniken in Java finden, aber für Geschwindigkeit möchte ich die Lösung in Oracle finden.

+0

ich hinzugefügt, um die Lücken-und-Inseln-Tag. das Suchen nach diesem wird wahrscheinlich eine ausreichende Menge des Stands der Technik ergeben, einschließlich rekursiver Abfragen. – wildplasser

+0

Siehe [Suche nach fehlenden Werten in einer Folge von Zahlen oder Datumsangaben] (http://lalitkumarb.wordpress.com/2015/07/22/find-range-of-missing-values-in-a-sequence-of -numbers-or-dates /) –

Antwort

33

Eine Lösung ohne hartzucodieren die 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. 
+5

Das macht meine Antwort lächerlich zu kompliziert! +1 – Ben

+0

Ich erforschte die Logik für eine Weile selbst und entschied, dass es unnötig war, Zeit wie diese zu verschwenden. Ich denke, ich hätte Google zu einer guten Übung machen sollen. Daher +1 für diese Antwort. –

0

Eine einfache Möglichkeit, Ihre Antwort für Ihr Szenario zu erhalten, ist dies:

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 select geben Ihnen die Antwort aus Ihrem Beispiel. Das macht nur Sinn, wenn Sie im Voraus wissen, in welchem ​​Bereich Ihre Nummern liegen und die Reichweite nicht zu groß sein sollte. Die erste Nummer muss der Offset im ROWNUM Teil sein und die Länge der Sequenz ist die Grenze zum connect by Teil.

+0

Sie müssen _know_ wissen, dass der Wert 9 ist. Woher wissen Sie das? – Ben

+0

Das habe ich geschrieben: Sie müssen die Reichweite Ihrer Sequenz kennen. Wenn ich die Aufgabe richtig verstehe, ist das wahrscheinlich bekannt. Oder habe ich dich missverstanden? – Stefan

1

Ich hätte connect by level als Stefan has done vorgeschlagen, jedoch können Sie in dieser Anweisung keine Unterabfrage verwenden, was bedeutet, dass sie für Sie nicht wirklich geeignet ist, da Sie wissen müssen, wie die Maximal- und Minimalwerte von deine Sequenz ist.

Ich würde vorschlagen, eine könnte der beste Weg, um die Zahlen zu generieren, die Sie benötigen, um die Join zu machen. Damit dies funktioniert, müssen Sie ein Objekt in Ihrer Datenbank bräuchten die Werte zurück:

create or replace type t_num_array as table of number; 

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

Mit Hilfe dieser Funktion die folgenden würde eine Liste von Serien Zahlen zwischen dem Minimum und Maximum.

select * from table(generate_serial_nos); 

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

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 
0

Dies funktionierte aber wählt die erste Sequenz (Startwert), da es keinen Vorgänger hat.Getestet in Server SQL sollte aber in Oracle arbeiten

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 

nicht zugewiesene Sequenz zu erhalten, tun nur value[i] - 1, wo ich mehr erste Reihe ist z.B. (7004 - 1 = 7003 and 7007 - 1 = 7006) die

verfügbaren Sequenzen sind glaube ich Sie auf diese einfache Abfrage auf Postgres> = 8.4 Dies funktioniert

+0

Dies setzt voraus, dass eine Tabelle mit allen gespeicherten Folgennummern vorhanden ist. Dies ist in Oracle nicht erforderlich. – Ben

+0

würde Oracles "connect by" besser funktionieren als das? – codingbiz

+0

die Antwort mit den höchsten Stimmen verwendet hier 2 Aggregatfunktionen - was ist mit Leistung? – codingbiz

-1

verbessern können. Mit ein paar leichten Ä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; 
+0

Zum Downvoter: bitte erklären. Diese Frage ist markiert mit 'sql', das ist (sollte) Standard sql. CTEs sind ein Teil davon. – wildplasser

+2

Es war nicht ich, der herabgestimmt hat, aber um fair zu sein, ist es auch mit Oracle getaggt und diese Syntax ist falsch. – Ben

+0

Nun wurde mir gesagt, dass der CTE in Orakel implementiert ist: http: //stackoverflow.com/questions/6064970/oracle-cte-merge. Natürlich gibt es das connect-by/prior-Konstrukt schon seit einigen Jahren, aber die CTE-Syntax ist zumindest ein Teil eines Standards, und es gibt immer einen Grund für einige Unterschiede, selbst wenn es Standard ist. Wie ich in meiner Antwort sagte: geringfügige Unterschiede in der Syntax (wie das Auslassen des Schlüsselwortes RECURSIVE) können existieren. Und zum Schluss: Zumindest funktioniert die Abfrage für mich (mit ein paar Änderungen auch für andere). Es gab andere Antworten hier, die * nicht * funktionieren. – wildplasser

11

Try this:

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 
-2
select A.ID + 1 As ID 
From [Missing] As A 
Where A.ID + 1 Not IN (Select ID from [Missing]) 
And A.ID < n 

Data: ID 
1 
2 
5 
7 
Result: ID 
3 
4 
6 
+0

Fehlende Sequenznummern. –

+0

Bitte bearbeiten Sie Ihren Beitrag, um einen Kontext für Ihre Antwort bereitzustellen. Code-only-Antworten sind nur teilweise hilfreich: http://stackoverflow.com/help/how-to-answer –

0

Hier ist eine Lösung, die :

  • setzt auf Oracle LAG Funktion
  • nicht die Kenntnis der vollständigen Sequenz erfordern (aber damit nicht, wenn erste oder letzte Zahlen in Folge verpasst wurden nicht erfasst)
  • sind die Werte, die fehlenden Listen von Zahlen rund um
  • Führt die fehlenden Listen von Zahlen als zusammenhängende Gruppen (vielleicht bequemer für die Berichterstattung)
  • nicht Tragisch für sehr große Listen der fehlenden Zahlen, aufgrund listagg Einschränkungen

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

Ausgang:

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