Mysql
 sql >> Base de données >  >> RDS >> Mysql

PHP MySQL trouve le plus petit nombre manquant dans la colonne

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