【问题标题】:MySQL get missing IDs from tableMySQL 从表中获取缺少的 ID
【发布时间】:2012-09-01 18:03:01
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: mysql


    【解决方案1】:
    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/

    【讨论】:

    • 这真是天才!谢谢
    【解决方案2】:

    更有效的查询:

    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
    

    【讨论】:

    • 谢谢伊万。这运行得更快!
    • 这对我有用,只是它错过了从 id=1 开始的初始间隙
    • MySQL 返回一个空结果集(即零行)。 (查询耗时 60.3183 秒。) -- 检查超过 200 万条记录只用了一分钟。
    • 你能调整这个查询让它返回 N 个缺失的 ID 吗? IE。 - 我想检索前 10 个丢失的主键,按它们的值排序。无需报告所有差距。
    【解决方案3】:

    为了给 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;
    

    【讨论】:

      【解决方案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),并且如果行数大于 1,则使用 for 循环(查询将始终返回至少一行)。

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

      【讨论】:

      • 非常简单、非常聪明、非常快速。事实上,如果表太大,接受的版本在合理的时间内是不可行的(大意味着 200,000 条记录)。
      【解决方案5】:

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

      我有 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);    
      

      【讨论】:

      • 只是澄清一下,我应该有 1800 万条记录。事实证明,我的数据库中只有 300 万条记录,并且错过了 1500 万条记录。
      【解决方案6】:

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

      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;
      

      【讨论】:

      • 有了这个单列版本,我得到(例如)475477506508513,但是对于两列版本,它得到我的[475,475][477,506][508,513] 告诉我我缺少数字 475、477-506 和 508-513。
      【解决方案7】:

      通过使用window functions(在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
      ;
      

      【讨论】:

      • 效果很好(只尝试过旧版本)
      【解决方案8】:

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

      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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-07
        • 1970-01-01
        相关资源
        最近更新 更多