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

时间:2016-03-28 18:22:41

标签: php mysql int

我需要在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;

4 个答案:

答案 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;