簡體   English   中英

PHP MySQL在列中找到最小的缺失數

[英]PHP MySQL find smallest missing number in column

我需要在MySQL中讀取帶有INT Order的列,並從該列中獲取缺少的較低數字:

+--------+---------+
| ID     | Order   |
+--------+---------+
| 1      | 1       |
| 3      | 5       |
| 4      | 3       |
| 5      | 4       |
| 6      | 2       |
| 7      | 6       |
| 8      | 11      |
+--------+---------+

我需要的結果是數字7,因為存在1到6,而其他缺失數字大於7。

$stmtpre    =   "SELECT Order FROM tabla ORDER BY Order DESC";
$data       =   $this   ->  DBMANAGER   ->  BDquery($stmtpre);
        $count      =   0;
        while ($row =   mysqli_fetch_assoc($data)){
            $count++;
            if($row['Order']!==$count){
                $result= $count; #store first lower get
                break;
            }
        }
return $result;

如果將Order列編入索引,則可以使用SQL獲取第一個缺少的數字,而無需使用排除的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

或(也許更直觀)

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

MySQL將第二個查詢轉換為第一個查詢。 因此,它們實際上是相等的。

更新

Strawberry提到了一個要點:第一個丟失的數字可能是1 ,我的查詢中沒有涵蓋這個數字。 但是我找不到一個既優雅又快速的解決方案。

我們可以采取相反的方法,並在出現間隔后搜索第一個數字。 但是需要再次加入表以查找該間隔之前的最后一個現有數字。

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(在我的情況下為MariaDB 10.0.19)無法正確優化該查詢。 即使第一個缺失的數字為9,在索引(PK)1M行表上也要花費大約一秒鍾的時間。我希望服務器在t1.Order=10之后停止搜索,但它似乎不這樣做。

另一種快速但看起來很丑陋的方法(IMHO)是僅在Order=1存在時才在子選擇中使用原始查詢。 否則返回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

或使用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

可能還有很長的路要走,但這是一種方法:

while ($row = mysqli_fetch_assoc($data)) {
    $orders[] = $row['Order'];
}

$result = min(array_diff(range(min($orders), max($orders)), $orders));
  • 創建一個范圍,從找到的最小訂單到找到的最大訂單
  • 計算找到的訂單的差額以獲取丟失的訂單
  • 查找缺少的最低訂單號

假設您要使用查詢返回的最低和最高數字作為范圍。 如果要始終從1開始,請使用1而不是min($orders)

另外,正如Strawberry指出的那樣, Order是MySQL中的保留字,因此請考慮對其進行更改或使用反引號SELECT`Order` FROM tabla對其進行定界。

從PHP方面:

我在解決方案上做更多的工作:

Fisrt調用功能:

$stmtpre    =   "SELECT Order FROM tabla ORDER BY Order ASC";
$data       =   $this   ->  DBMANAGER   ->  BDqueryFirstMissingINT($stmtpre, DATABASE);
echo    $data;

在第二

function BDqueryFirstMissingINT($stmtpre,$dbUsing){
    $data       =   $this   ->  BDquery($stmtpre, $dbUsing); #run the query
    $count      =   0;
    while ($row =   mysqli_fetch_array($data)){
        $count++;
        $value  =   (int)$row[0];
        if($value!==$count){
            $result = $count;
            break;
        }
    }
    return $result;
}

謝謝你的幫助

這是一個主意...

SELECT x.my_order + 1 missing 
  FROM 
     ( SELECT my_order FROM my_table 
        UNION 
       SELECT 0
     ) x 
  LEFT 
  JOIN my_table y 
    ON y.my_order = x.my_order + 1 
 WHERE y.my_order IS NULL 
 ORDER 
    BY missing 
 LIMIT 1;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM