Se l'Order
colonna è indicizzata, è possibile ottenere il primo numero mancante con SQL, senza leggere la tabella completa utilizzando un LEFT JOIN escluso:
SELECT t1.`Order` + 1 AS firstMissingOrder
FROM tabla t1
LEFT JOIN tabla t2 ON t2.`Order` = t1.`Order` + 1
WHERE t2.`Order` IS NULL
AND t1.`Order` <> (SELECT MAX(`Order`) FROM tabla)
ORDER BY t1.`Order`
LIMIT 1
o (forse più intuitivo)
SELECT t1.`Order` + 1 AS firstMissingOrder
FROM tabla t1
WHERE NOT EXISTS (
SELECT 1
FROM tabla t2
WHERE t2.`Order` = t1.`Order` + 1
)
AND t1.`Order` <> (SELECT MAX(`Order`) FROM tabla)
ORDER BY t1.`Order`
LIMIT 1
La seconda query verrà convertita da MySQL nella prima. Quindi sono praticamente uguali.
Aggiorna
Strawberry ha menzionato un buon punto:il primo numero mancante potrebbe essere 1
, che non è coperto dalla mia query. Ma non sono riuscito a trovare una soluzione, che sia allo stesso tempo elegante e veloce.
Potremmo andare nella direzione opposta e cercare il primo numero dopo uno spazio vuoto. Ma sarebbe necessario unirmi di nuovo al tavolo per trovare l'ultimo numero esistente prima di quel divario.
SELECT IFNULL(MAX(t3.`Order`) + 1, 1) AS firstMissingOrder
FROM tabla t1
LEFT JOIN tabla t2 ON t2.`Order` = t1.`Order` - 1
LEFT JOIN tabla t3 ON t3.`Order` < t1.`Order`
WHERE t1.`Order` <> 1
AND t2.`Order` IS NULL
GROUP BY t1.`Order`
ORDER BY t1.`Order`
LIMIT 1
MySQL (nel mio caso MariaDB 10.0.19) non è in grado di ottimizzare correttamente quella query. Ci vuole circa un secondo su una tabella di righe indicizzate (PK) da 1 milione, anche se il primo numero mancante è 9. Mi aspetto che il server smetta di cercare dopo t1.Order=10
, ma sembra non farlo.
Un altro modo, che è veloce ma sembra brutto (IMHO), è utilizzare la query originale in una sottoselezione solo se Order=1
esiste. Altrimenti restituisci 1
.
SELECT CASE
WHEN NOT EXISTS (SELECT 1 FROM tabla WHERE `Order` = 1) THEN 1
ELSE (
SELECT t1.`Order` + 1 AS firstMissingOrder
FROM tabla t1
LEFT JOIN tabla t2 ON t2.`Order` = t1.`Order` + 1
WHERE t2.`Order` IS NULL
AND t1.`Order` <> (SELECT MAX(`Order`) FROM tabla)
ORDER BY t1.`Order`
LIMIT 1
)
END AS firstMissingOrder
Oppure usando UNION
SELECT 1 AS firstMissingOrder FROM (SELECT 1) dummy WHERE NOT EXISTS (SELECT 1 FROM tabla WHERE `Order` = 1)
UNION ALL
SELECT firstMissingOrder FROM (
SELECT t1.`Order` + 1 AS firstMissingOrder
FROM tabla t1
LEFT JOIN tabla t2 ON t2.`Order` = t1.`Order` + 1
WHERE t2.`Order` IS NULL
AND t1.`Order` <> (SELECT MAX(`Order`) FROM tabla)
ORDER BY t1.`Order`
LIMIT 1
) sub
LIMIT 1