2012-04-11 3 views
0

Ich habe eine Tabelle in einer MySQL DB mit einer UNIQUE INT (10) Spalte. Die Tabelle ist ziemlich ausgefüllt und die Zeile enthält nicht aufeinanderfolgende Einträge von Integer-Zahlen in dieser Spalte. Ich würde gerne eine Abfrage machen, die mir die kleinste Zahl (oder die kleinste Zahl n) liefert, die in keiner Zeile steht.So erhalten Sie die kleinsten Ganzzahlen, die noch nicht in einer Datenbankspalte enthalten sind

Beispiel: Die Tabelle enthält Zeilen mit Werten (1, 2, 3, 5, 7, 8, 10, 12, 15) für die Spalte. Die SQL-Anweisung sollte zurückgeben, d. H. Die fünf niedrigsten nicht enthaltenen Werte, die in diesem Fall 4, 6, 9, 11, 13 sind.

Ist das mit MySQL möglich?

+2

Haftungsausschluss: Ich habe nicht diese benötigen Autoinkrement PK Werte aufzufüllen fehlt ... – Thomas

Antwort

4

Sie können eine "Zahlen" Tabelle verwenden (es für verschiedene Operationen praktisch ist):

CREATE TABLE num 
(i UNSIGNED INT NOT NULL 
, PRIMARY KEY (i) 
) ; 

INSERT INTO num (i) 
VALUES 
    (1), (2), ..., (1000000) ; 

Dann:

SELECT 
    num.i 
FROM 
     num 
    LEFT JOIN 
     tableX AS t 
      ON num.i = t.columnX 
WHERE 
    t.columnX IS NULL 
ORDER BY 
    num.i 
LIMIT 5 

oder:

SELECT 
    num.i 
FROM 
    num 
WHERE 
    NOT EXISTS 
    (SELECT * 
     FROM tableX AS t 
     WHERE num.i = t.columnX 
    ) 
ORDER BY 
    num.i 
LIMIT 5 

Ein anderer Ansatz , ohne eine Hilfstabelle zu verwenden, wäre um MySQL-Variablen zu verwenden. Sie können es in SQL-Fiddle, test-2 testen. Der Ausgang ist nicht das gleiche wie zuvor (nur um zu zeigen, dass es getan werden kann):

SELECT start_id, end_id 
FROM 
    (SELECT 
     IF(t.columnX <> @id, @id, NULL)  AS start_id 
     , IF(t.columnX <> @id, t.columnX-1, NULL) AS end_id 
     , @rows := @rows + (t.columnX - @id)  AS r 
     , @id := t.columnX + 1     AS running_id 
    FROM 
      tableX AS t 
     CROSS JOIN 
      (SELECT @rows := 0 
        , @id := 1 
      ) AS dummy 
    WHERE 
     @rows < 5 
    ORDER BY 
     t.columnX 
    ) AS tmp 
WHERE 
    start_id IS NOT NULL 
+0

ich diese Idee gefällt, aber Füllung und die num Tabelle Beitritt würde eher ineffizient sein, wenn die möglichen Zahlen in die Millionen gehen (oder sogar Milliarden). Gibt es eine Lösung ohne diesen Trick? – Thomas

+0

Nicht, wenn Sie es einmal tun und Sie diesen Tisch behalten. Eine Milliarde Zeilen Tabelle wäre 4GB (und möglicherweise ein bisschen mehr mit dem Index). Es gibt rekursive Wege, um dasselbe zu erreichen, aber MySQL hat keine CTEs oder eine ähnliche Struktur. –

+0

Eine Lösung mit Variablen wäre jedoch möglich. –

2

Dies funktioniert, aber ich denke, es ist ziemlich ineffizient ist. Sie werden jedoch keine zusätzliche Tabelle benötigen (eine Tabelle, die für alle positiven Zahlen in INT (2^31-1)*4/1024^3 = 8GB wäre). Ich rate Ihnen auch zu schauen, warum Sie das brauchen, weil es vielleicht nicht notwendig ist.

Es gibt auch den Anfang und das Ende eines Bereichs zurück, aber nicht alle Nummern in diesem Bereich. (Zum Beispiel, wenn Sie Nummern 1 und 5 wird zurückkehren {0,2,4,6})

SELECT (t.num-1) AS bound FROM t 
    WHERE t.num-1 NOT IN (SELECT t.num FROM t) 
UNION 
SELECT (t.num+1) AS bound FROM t 
    WHERE t.num+1 NOT IN (SELECT t.num FROM t) 

Wie gesagt das ist ziemlich ineffizient sein wird, schließt sich schneller sein könnte, aber sie würde es brauchen Benchmark.

SELECT (t.num-1) AS bound FROM t 
    LEFT JOIN t AS u ON t.num-1 = u.num 
    WHERE u.num IS NULL 
UNION 
SELECT (t.num+1) AS bound FROM t 
    LEFT JOIN t AS u ON t.num+1 = u.num 
    WHERE u.num IS NULL 
+0

Danke, ich werde das mit der anderen Lösung leistungsmäßig vergleichen. – Thomas

Verwandte Themen