Hvis Order
kolonne er indekseret, kan du få det første manglende tal med SQL, uden at læse hele tabellen med et ekskluderende LEFT JOIN:
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
eller (måske mere intuitivt)
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
Den anden forespørgsel vil blive konverteret af MySQL til den første. Så de er praktisk talt lige.
Opdater
Strawberry nævnte en god pointe:Det første manglende tal kan være 1
, som ikke er dækket af min forespørgsel. Men jeg var ikke i stand til at finde en løsning, som er både elegant og hurtig.
Vi kunne gå den modsatte vej og søge efter det første tal efter et hul. Men ville være nødt til at slutte sig til tabellen igen for at finde det sidste eksisterende tal før det hul.
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 (i mit tilfælde MariaDB 10.0.19) er ikke i stand til at optimere den forespørgsel korrekt. Det tager omkring et sekund på en indekseret (PK) 1M rækketabel, selvom det første manglende tal er 9. Jeg ville forvente, at serveren stopper med at søge efter t1.Order=10
, men det ser ud til ikke at gøre det.
En anden måde, som er hurtig, men ser grim ud (IMHO), er kun at bruge den originale forespørgsel i et undervalg, hvis Order=1
eksisterer. Ellers returner 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
Eller ved at bruge 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