【问题标题】:Finding the next available id in MySQL在 MySQL 中查找下一个可用的 id
【发布时间】:2010-11-27 04:25:12
【问题描述】:

我必须在 MySQL 数据库中找到下一个可用的 id(如果数据库中有 5 个数据,我必须获得下一个可用的插入位置 6)。我怎样才能做到这一点? 我使用了MAX(id),但是当我从数据库中删除一些行时,它仍然保留它没有更新的旧最大值。

【问题讨论】:

  • 所以你要问的是找到第一个可用的 id。如果您有 5 个,但删除了第三个,那么 id 3 应该可用于插入新行?
  • 这是个糟糕的建议……大多数数据库只是通过一个合成键递增。他们不会尝试重复使用旧值。它只是增加。
  • Burak:你想找到“下一个可用的 id”(即 max(id) + 1)还是“下一个从未使用过的 id”?

标签: sql mysql


【解决方案1】:

2014 年 12 月 5 日更新

由于 Simon 的(已接受)答案以及 Diego 的评论中列出的原因,我不推荐这种方法。请自行承担使用以下查询的风险。


原答案

我在 MySQL 开发者网站上找到的最短的一个:

SELECT Auto_increment
FROM information_schema.tables
WHERE table_name='the_table_you_want'

请注意,如果您有几个具有相同表的数据库,您也应该指定数据库名称,如下所示:

SELECT Auto_increment
FROM information_schema.tables
WHERE table_name = 'the_table_you_want'
      AND table_schema = 'the_database_you_want';

【讨论】:

  • 很好,但不要忘记!数据库中的其他用户可能会在您进行此查询之后和实际插入记录之前插入一条记录。在某些情况下它可以正常工作,但请记住!
  • 我试过了,auto_increment 为我返回了 null。我从中选择的表有 5 条记录,一个名为 id 的字段是主键。谁能提出为什么会这样?
  • @LeoKing - 该列必须是 AutoIncrement 列才能取回值。
  • 这个答案中的查询也可以用于测试目的(例如检查插入和删除操作是否真的成功)
【解决方案2】:

我认为您永远无法确定 下一个 id,因为有人可能会在您要求下一个 id 之后插入新行。您至少需要一个事务,如果我没记错的话,您只能在 插入它之后获取实际使用的 id,至少这是处理它的常用方法——参见 http://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html

【讨论】:

  • 我同意这个评论。为了数据完整性,我总是会生成并使用一个新的数字。如果您想使用“未使用”的 id,我建议您在 cron 作业上执行一个 php 文件,该作业在您的 web 应用程序中肯定没有用户并且您将行“折叠”到未使用的行时执行。您必须确保更新引用此表的任何其他表,并相应更新。
  • @Oliver M Grech:“你的 web 应用程序中肯定没有用户”——没有这样的事情,互联网没有工作时间。取决于“哦,现在是 GMT+1 凌晨 2 点,因此没有用户” 会回来咬你(去过那里,做到了)。
【解决方案3】:

除了 Lukasz Lysik 的回答 - LEFT-JOIN 类型的 SQL。
据我了解,如果有 id's: 1,2,4,5 它应该返回 3。

SELECT u.Id + 1 AS FirstAvailableId
FROM users u
LEFT JOIN users u1 ON u1.Id = u.Id + 1
WHERE u1.Id IS NULL
ORDER BY u.Id
LIMIT 0, 1

希望它对一些访问者有所帮助,虽然帖子已经相当老了。

【讨论】:

  • 另外,您可以使用 min(u.id)+1 来代替 limit 和 order by。不确定哪种方式更快 - 可能是你的。
  • 要显着加快速度,您应该添加一个分组依据。 " group by u1.id order by u1.id limit 1"
【解决方案4】:

鉴于你在评论中所说的:

我的 id 列是自动递增的,我必须获取 id 并将其转换为另一个基数。所以我需要在插入之前获取下一个 id,因为转换后的代码也将被插入。

有一种方法可以满足您的要求,即在实际插入之前询问表格下一个插入行的 id 是什么: p>

SHOW TABLE STATUS WHERE name = "myTable"

该结果集中将有一个名为“Auto_increment”的字段,它会告诉您下一个自动增量值。

【讨论】:

    【解决方案5】:

    据我了解,如果有 id's: 1,2,4,5 它应该返回 3。

    SELECT t1.id + 1
    FROM theTable t1
    WHERE NOT EXISTS (
        SELECT * 
        FROM theTable t2
        WHERE t2.id = t1.id + 1
    )
    LIMIT 1
    

    【讨论】:

    • 仅供参考,此代码有一个问题:如果表中还没有行,则不会返回任何内容。
    • 如果表中有行,那么这样安全吗?其他一些答案建议仅使用新的 ID,但如果我们可以使用它并具有数据完整性,那就太好了。
    【解决方案6】:

    你说:

    我的 id 列是我拥有的自动增量 获取 id 并将其转换为 另一个基地。所以我需要下一个 插入前的 id 导致转换后的代码 也会被插入。

    您要求的内容非常危险,会导致竞争状况。如果您的代码由不同的用户同时运行两次,他们都将得到 6,并且他们的更新或插入将相互叠加。

    我建议您改为将INSERT 放入表中,使用LAST_INSERT_ID() 获取auto_increment 值,然后使用UPDATE 该行设置取决于auto_increment 值的任何值。

    【讨论】:

      【解决方案7】:

      如果要选择第一个间隙,请使用:

      SELECT  @r
      FROM    (
              SELECT  @r := MIN(id) - 1
              FROM    t_source2
              ) vars,
              t_source2
      WHERE   (@r := @r + 1) <> id
      ORDER BY
              id
      LIMIT   1;
      

      同一查询有一个ANSI 语法版本:

      SELECT  id
      FROM    mytable mo
      WHERE   (
              SELECT  id + 1
              FROM    mytable mi
              WHERE   mi.id < mo.id
              ORDER BY
                      mi.id DESC
              LIMIT 1
              ) <> id
      ORDER BY
              id,
      LIMIT   1
      

      但是,由于MySQL 中的优化器错误,它会很慢。

      【讨论】:

      • 我在 MySQL 5.5 中运行了几个小时的变体测试。我对这种方法很着迷,但无论如何这都行不通。在非索引列中包含 1、2、3、5 的测试用例中,第一种方法返回 7。第二种 ANSI 替代方法的工作更少,返回冲突的 5。改进此设计的尝试以 @ 的不稳定径流告终r 值或空结果,包括尝试实现起始引用。我很想看到这个工作 - 创建一个没有函数的变量循环的想法是令人兴奋的。
      【解决方案8】:

      如果您真的想在插入行之前计算下一个插入的键(我认为这不是一个好主意),那么我建议您使用当前使用的最大 id 加一:

      SELECT MAX(id) + 1 FROM table
      

      但我建议您让 MySQL 自己创建 id(通过使用自动增量列)并使用 LAST_INSERT_ID() 从 DBMS 获取它。为此,请使用执行插入然后查询 id 的事务,例如:

      INSERT INTO table (col1) VALUES ("Text");
      SELECT LAST_INSERT_ID();
      

      returnset 现在只包含一列,其中包含新生成行的 id。

      【讨论】:

      • Max 不适用于已删除的 pk 行,我认为第二种方法不是最好的和有效的......
      【解决方案9】:

      现在回答这个问题为时已晚,但希望这对某人有所帮助。

      @Eimantas 已经给出了最佳答案,但是如果您在同一服务器下有两个或多个同名表,则该解决方案将不起作用。

      我稍微修改了@Eimantas 的回答来解决上述问题。

      select Auto_increment as id from information_schema.tables where table_name = 'table_name' and table_schema = 'database_name'
      

      【讨论】:

        【解决方案10】:

        一种方法是将索引设置为自动递增。然后您的 SQL 语句只需指定 NULL,然后 SQL 解析器会为您完成其余的工作。

        INSERT INTO foo VALUES (null);
        

        【讨论】:

        • 我的 id 列是自动递增的,我必须获取 id 并将其转换为另一个基数。所以我需要在插入之前获取下一个 id,因为转换后的代码也会被插入。
        【解决方案11】:

        如果这与插入新记录一起使用,您可以使用类似的东西。

        (您已在 cmets 中声明 id 是自动递增的,另一个表需要下一个 ID + 1)

        INSERT INTO TABLE2 (id, field1, field2, field3, etc) 
        VALUES(
           SELECT (MAX(id) + 1), field1, field2, field3, etc FROM TABLE1
           WHERE condition_here_if_needed
        )
        

        这是伪代码,但你明白了

        【讨论】:

          【解决方案12】:

          许多解决方案的问题是它们只找到下一个“GAP”,而忽略“1”是否可用,或者如果没有任何行,它们将返回 NULL 作为下一个“GAP”。

          以下不仅会找到下一个可用的间隙,还会考虑第一个可用数字是否为 1:

          SELECT CASE WHEN MIN(MyID) IS NULL OR MIN(MyID)>1
          -- return 1 if it's available or if there are no rows yet
          THEN
              1
          ELSE -- find next gap
              (SELECT MIN(t.MyID)+1
              FROM MyTable t (updlock)
              WHERE NOT EXISTS (SELECT NULL FROM MyTable n WHERE n.MyID=t.MyID+1))
          END AS NextID
          FROM MyTable
          

          【讨论】:

          • 我知道我来晚了,但它可能对即将到来的访问者有用,因为您的查询对我有帮助:):你有一个额外的括号:(SELECT MIN(t.MyID))+1
          【解决方案13】:

          这对我来说效果很好(MySQL 5.5),也解决了“起始”位置的问题。

          SELECT
              IF(res.nextID, res.nextID, @r) AS nextID
          FROM
              (SELECT @r := 30) AS vars,
              (
              SELECT MIN(t1.id + 1) AS nextID
              FROM test t1
              LEFT JOIN test t2
                ON t1.id + 1 = t2.id
              WHERE t1.id >= @r
                AND t2.id IS NULL
                AND EXISTS (
                    SELECT id
                    FROM test
                    WHERE id = @r
                )
            LIMIT 1
            ) AS res
          LIMIT 1
          

          如前所述,这些类型的查询非常慢,至少在 MySQL 中是这样。

          【讨论】:

            【解决方案14】:
            SELECT ID+1 "NEXTID" 
            FROM ( 
                SELECT ID from TABLE1 
                WHERE ID>100 order by ID
            ) "X" 
            WHERE not exists (
                SELECT 1 FROM TABLE1 t2
                WHERE t2.ID=X.ID+1
            ) 
            LIMIT 1
            

            【讨论】:

            • 最好解释一下为什么以及如何工作。
            【解决方案15】:
            <?php
            Class Database{
                public $db;
                public $host   = DB_HOST;
                public $user   = DB_USER;
                public $pass   = DB_PASS;
                public $dbname = DB_NAME;
            
                public $link;
                public $error;
            
                public function __construct(){
                    $this->connectDB();
                }
                private function connectDB(){
                $this->link = new mysqli($this->host, $this->user, $this->pass, $this->dbname);
                if(!$this->link){
                    $this->error ="Connection fail".$this->link->connect_error;
                    return false;
                }
             }
            
                // Select or Read data
            
                public function select($query){
                    $result = $this->link->query($query) or die($this->link->error.__LINE__);
                    if($result->num_rows > 0){
                        return $result;
                    } else {
                        return false;
                    }
                }
            
            }
             $db = new Database();
            $query = "SELECT * FROM table_name WHERE id > '$current_postid' ORDER BY ID ASC LIMIT 1";
            $postid = $db->select($query);
            if ($postid) {
              while ($result = $postid->fetch_assoc()) { 
                    echo $result['id'];
                } 
            
              } ?>
            

            【讨论】:

            • 您的建议可能适用于 PHP 或其他语言,但不适用于直接 SQL 或 MySQL。
            猜你喜欢
            • 2011-11-10
            • 2013-08-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-10-31
            • 1970-01-01
            相关资源
            最近更新 更多