sql >> Databasteknik >  >> RDS >> Mysql

PHP MySQL hitta minsta saknade nummer i kolumn

Om Order kolumnen är indexerad, kan du få det första saknade numret med SQL, utan att läsa hela tabellen med ett exkluderande 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 (kanske mer 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 andra frågan kommer att konverteras av MySQL till den första. Så de är praktiskt taget lika.

Uppdatera

Strawberry nämnde en bra poäng:Det första saknade numret kan vara 1 , som inte täcks av min fråga. Men jag kunde inte hitta en lösning, som är både elegant och snabb.

Vi kunde gå motsatt väg och söka efter den första siffran efter en lucka. Men skulle behöva gå med i tabellen igen för att hitta det sista befintliga numret före det gapet.

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 mitt fall MariaDB 10.0.19) kan inte optimera den frågan ordentligt. Det tar ungefär en sekund på en indexerad (PK) 1M radtabell, även om det första saknade talet är 9. Jag förväntar mig att servern slutar söka efter t1.Order=10 , men det verkar inte göra det.

Ett annat sätt, som är snabbt men ser fult ut (IMHO), är att använda den ursprungliga frågan i ett underval endast om Order=1 existerar. Annars returnerar du 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 med 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. Två olika leverantörer på samma konfigurationsfil

  2. 2 sätt att lista alla funktioner i MariaDB

  3. Är en vy snabbare än en enkel fråga?

  4. Använda rekursion för att bygga navigering