Si la Order
colonne est indexée, vous pouvez obtenir le premier nombre manquant avec SQL, sans lire la table complète en utilisant un LEFT JOIN exclu :
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
ou (peut-être plus intuitif)
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 deuxième requête sera convertie par MySQL en la première. Ils sont donc pratiquement égaux.
Mettre à jour
Strawberry a mentionné un bon point :le premier nombre manquant pourrait être 1
, qui n'est pas couvert dans ma requête. Mais je n'ai pas trouvé de solution à la fois élégante et rapide.
Nous pourrions aller dans le sens inverse et rechercher le premier numéro après un écart. Mais il faudrait rejoindre à nouveau la table pour trouver le dernier numéro existant avant cet écart.
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 (dans mon cas, MariaDB 10.0.19) n'est pas en mesure d'optimiser correctement cette requête. Cela prend environ une seconde sur une table de lignes indexée (PK) 1M, même si le premier nombre manquant est 9. Je m'attendrais à ce que le serveur arrête la recherche après t1.Order=10
, mais il semble que ce ne soit pas le cas.
Une autre façon, qui est rapide mais qui a l'air moche (à mon humble avis), consiste à utiliser la requête d'origine dans une sous-sélection uniquement si Order=1
existe. Sinon retourner 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
Ou en utilisant 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