2011-12-09 22 views
5

Il problema qui è correlato a un'altra domanda che avevo ...Come si possono trovare le lacune ID in un recordset MySQL?

Ho milioni di record e l'ID di ciascuno di questi record è auto-incrementato, purtroppo a volte l'ID che viene generato a volte viene buttato via così ci sono molte lacune tra gli ID.

Voglio trovare gli spazi vuoti e riutilizzare gli ID che sono stati abbandonati.

Qual è un modo efficiente per farlo in MySQL?

+0

Correlati: http://stackoverflow.com/questions/3718229/stop-mysql-reusing-auto-increment-ids –

+2

Se stai usando un INT per la tua chiave primaria, puoi avere come 2 miliardi + record. Perché preoccuparsi di cercare di colmare le lacune? Stai finendo i numeri? Trovo che ci sia un vantaggio nel sapere che i numeri corrispondono all'ordine in cui sono stati aggiunti i record. – minboost

+1

Forse avrai meno problemi di prestazioni modificando il tuo tipo di chiave primaria in BIGINT (se i valori di 4 billon forniti da INT sono troppo brevi) rispetto al tentativo di riutilizzare gli ID su una tabella molto grande. –

risposta

17

Prima di tutto, quale vantaggio stai cercando di ottenere riutilizzando i valori ignorati? Un ordinario INT UNSIGNED ti consente di contare fino a 4.294.967.295. Con "milioni di record" il tuo database dovrebbe crescere mille volte prima di esaurire gli ID validi. (E quindi utilizzando un BIGINT UNSIGNED si imbatterà fino a 18,446,744,073,709,551,615 valori.)

Cercando di riciclare i valori MySQL ha saltato rischia di consumare un sacco di tempo a cercare di compensare qualcosa che in realtà non si preoccupa di MySQL nel primo posto.

Detto questo, è possibile trovare gli ID mancante con qualcosa di simile:

SELECT id + 1 
FROM the_table 
WHERE NOT EXISTS (SELECT 1 FROM the_table t2 WHERE t2.id = the_table.id + 1); 

Questo troverà solo il primo numero di manca in ogni sequenza (ad esempio, se si dispone di {1, 2, 3, 8, 10} troverà {4,9}), ma è probabile che sia efficiente e, naturalmente, una volta che hai inserito un ID, puoi sempre eseguirlo di nuovo.

+1

+1 per una risposta semplice e premurosa – qodeninja

+0

se 1 è il primo divario non verrà restituito – morandi3

+0

Nel mio caso ogni numero mancante è importante, così è l'ultimo paragrafo di risposta :) +1 Upvote – AamirR

2

Di seguito restituirà una riga per ogni lacuna nel campo intero "n" in mytab:

/* cs will contain 1 row for each contiguous sequence of integers in mytab.n 
    and will have the start of that chain. 
    ce will contain the end of that chain */ 
create temporary table cs (row int auto_increment primary key, n int); 
create temporary table ce like cs; 
insert into cs (n) select n from mytab where n-1 not in (select n from mytab) order by n; 
insert into ce (n) select n from mytab where n+1 not in (select n from mytab) order by n; 
select ce.n + 1 as bgap, cs.n - 1 as egap 
    from cs, ce where cs.row = ce.row + 1; 

Se invece delle lacune si desidera che le catene contigui poi select finale dovrebbe essere:

select cs.n as bchain, ce.n as echain from cs,ce where cs.row=ce.row; 
+0

la seconda query '' selezionare cs.n come bchain, ce.n come echain da cs, ce dove cs.row = ce.row; '' il join mostra un gap maggiore che esiste realmente ma il primo funziona bene. – magdmartin

1

Questa soluzione è migliore, nel caso in cui è necessario includere il primo elemento come 1:

SELECT 
    1 AS gap_start, 
    MIN(e.id) - 1 AS gap_end 
FROM 
    factura_entrada e 
WHERE 
    NOT EXISTS(
     SELECT 
      1 
     FROM 
      factura_entrada 
     WHERE 
      id = 1 
    ) 
LIMIT 1 
UNION 
    SELECT 
     a.id + 1 AS gap_start, 
     MIN(b.id)- 1 AS gap_end 
    FROM 
     factura_entrada AS a, 
     factura_entrada AS b 
    WHERE 
     a.id < b.id 
    GROUP BY 
     a.id 
    HAVING 
     gap_start < MIN(b.id);