44

我在 MySQL 中有这个表,例如:

ID | Name
1  | Bob
4  | Adam
6  | Someguy

如果您注意到,没有 ID 号(2、3 和 5)。

如何编写查询以便 MySQL 只回答丢失的 ID,在这种情况下: "2,3,5" ?

4

8 回答 8

50
SELECT a.id+1 AS start, MIN(b.id) - 1 AS end
    FROM testtable AS a, testtable AS b
    WHERE a.id < b.id
    GROUP BY a.id
    HAVING start < MIN(b.id)

希望这个链接也有帮助 http://www.codediesel.com/mysql/sequence-gaps-in-mysql/

于 2012-09-07T20:50:19.917 回答
31

更有效的查询:

SELECT (t1.id + 1) as gap_starts_at, 
       (SELECT MIN(t3.id) -1 FROM my_table t3 WHERE t3.id > t1.id) as gap_ends_at
FROM my_table t1
WHERE NOT EXISTS (SELECT t2.id FROM my_table t2 WHERE t2.id = t1.id + 1)
HAVING gap_ends_at IS NOT NULL
于 2014-12-17T07:45:12.887 回答
4

为了给 Ivan 的回答添加一点内容,如果 1 不存在,此版本会在开头显示缺少的数字:

SELECT 1 as gap_starts_at,
       (SELECT MIN(t4.id) -1 FROM testtable t4 WHERE t4.id > 1) as gap_ends_at
FROM testtable t5
WHERE NOT EXISTS (SELECT t6.id FROM testtable t6 WHERE t6.id = 1)
HAVING gap_ends_at IS NOT NULL limit 1
UNION
SELECT (t1.id + 1) as gap_starts_at, 
       (SELECT MIN(t3.id) -1 FROM testtable t3 WHERE t3.id > t1.id) as gap_ends_at
FROM testtable t1
WHERE NOT EXISTS (SELECT t2.id FROM testtable t2 WHERE t2.id = t1.id + 1)
HAVING gap_ends_at IS NOT NULL;
于 2017-06-30T20:10:42.607 回答
4

如果您不想返回多个 ID 范围,而是要检索每个丢失的 ID 本身,每个 ID 都在自己的行中,您可以执行以下操作:

SELECT id+1 FROM table WHERE id NOT IN (SELECT id-1 FROM table) ORDER BY 1

查询非常有效。但是,它最后还包括一个额外的行,等于最高 ID 号加 1。可以在服务器脚本中忽略最后一行,方法是检查返回的行数 ( mysqli_num_rows ),然后使用for如果行数大于 1 则循环(查询将始终返回至少一行)。

编辑: 我最近发现我的原始解决方案没有返回所有丢失的 ID 号,在丢失的数字是连续的(即彼此相邻的)的情况下。但是,该查询仍然可以非常快速地确定是否有数字丢失,并且在与 hagensoft 的查询(最佳答案)结合使用时可以节省时间。换句话说,可以首先运行此查询来测试是否缺少 ID。如果找到任何东西,则可以在之后立即运行 hagensoft 的查询,以帮助识别丢失的确切 ID(没有节省时间,但一点也不慢)。如果什么也没找到,那么可能会节省大量时间,因为不需要运行 hagensoft 的查询。

于 2019-02-28T08:14:03.487 回答
3

在一个查询中获取间隙的开始和在一个查询中的间隙结束会更有效率。

我有 1800 万条记录,每条记录都用了不到一秒钟的时间来获得这两个结果。当我尝试将它们放在一起时,我的查询在一小时后超时。

获取差距的开始:

SELECT (t1.id + 1) as MissingID
FROM sequence t1
WHERE NOT EXISTS 
    (SELECT t2.id 
    FROM sequence t2 
    WHERE t2.id = t1.id + 1);

得到间隙的结束:

SELECT (t1.id - 1) as MissingID
FROM sequence t1
WHERE NOT EXISTS 
    (SELECT t2.id 
    FROM sequence t2 
    WHERE t2.id = t1.id - 1);    
于 2018-11-23T00:50:54.853 回答
2

上面的查询将提供两列,因此您可以尝试在单个列中获取缺失的数字

select start from 
(SELECT a.id+1 AS start, MIN(b.id) - 1 AS end
    FROM sequence AS a, sequence AS b
    WHERE a.id < b.id
    GROUP BY a.id
    HAVING start < MIN(b.id)) b
UNION
select c.end from (SELECT a.id+1 AS start, MIN(b.id) - 1 AS end
    FROM sequence AS a, sequence AS b
    WHERE a.id < b.id
    GROUP BY a.id
    HAVING start < MIN(b.id)) c order by start;
于 2015-08-26T12:48:30.013 回答
1

通过使用窗口函数(在 mysql 8 中可用)查找id列中的间隙可以表示为:

WITH gaps AS
(
    SELECT
        LAG(id, 1, 0) OVER(ORDER BY id) AS gap_begin,
        id AS gap_end,
        id - LAG(id, 1, 0) OVER(ORDER BY id) AS gap
    FROM test
)
SELECT
    gap_begin,
    gap_end
FROM gaps
WHERE gap > 1
;

如果您使用的是旧版本的 mysql,则必须依赖变量(所谓的穷人窗口函数习语)

SELECT
   gap_begin,
   gap_end
FROM (
     SELECT
         @id_previous AS gap_begin,
         id AS gap_end,
         id - @id_previous AS gap,
         @id_previous := id
     FROM (
         SELECT
             t.id
         FROM test t
         ORDER BY t.id
     ) AS sorted
     JOIN (
         SELECT
             @id_previous := 0
     ) AS init_vars
 ) AS gaps
WHERE gap > 1
;
于 2021-03-14T13:39:58.057 回答
0

如果您想要一种更轻松的方式来搜索数百万行数据,

SET @st=0,@diffSt=0,@diffEnd=0;
SELECT res.startID, res.endID, res.diff
  , CONCAT(
    "SELECT * FROM lost_consumer WHERE ID BETWEEN "
    ,res.startID+1, " AND ", res.endID-1) as `query`
FROM (
SELECT
  @diffSt:=(@st) `startID`
  , @diffEnd:=(a.ID) `endID`
  , @st:=a.ID `end`
  , @diffEnd-@diffSt-1 `diff`
  FROM consumer a 
ORDER BY a.ID
) res
WHERE res.diff>0;

看看这个http://sqlfiddle.com/#!9/3ea00c/9

于 2021-06-03T08:30:10.820 回答