我需要在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;
答案 0 :(得分:3)
如果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转换为第一个查询。所以他们实际上是平等的。
<强>更新强>
草莓提到了一个好点:第一个缺失的数字可能是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)无法正确优化该查询。在索引(PK)1M行表上大约需要一秒钟,即使第一个丢失的数字是9.我希望服务器在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
答案 1 :(得分: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`Dorder` FROM tabla 强>
答案 2 :(得分:0)
从PHP方面:
我更多地围绕解决方案工作:
Fisrt call功能:
$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;
}
感谢您的帮助
答案 3 :(得分:0)
这是一个想法...
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;