sql >> Database teknologi >  >> RDS >> Mysql

PHP MySQL finde det mindste manglende tal i kolonnen

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


  1. Mysql, Muligt indlejret kategoriproblem

  2. MySQL-replikeringshastighed

  3. SQL Hvordan erstatter man værdier for select return?

  4. PHP/Mysql-søgning - Der skelnes mellem store og små bogstaver