[英]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.