【问题标题】:ON DUPLICATE KEY + AUTO INCREMENT issue mysqlON DUPLICATE KEY + AUTO INCREMENT 问题 mysql
【发布时间】:2014-05-07 11:46:49
【问题描述】:

我有这样的表结构

当我向表中插入行时,我正在使用此查询:

INSERT INTO table_blah ( material_item, ... hidden ) VALUES ( data, ... data ) ON DUPLICATE KEY UPDATE id = id, material_item = data, ... hidden = data;

当我第一次插入数据而不触发ON DUPLICATE KEY 时,id 递增:

但是当ON DUPLICATE KEY 触发并且我INSERT A NEW ROW id 对我来说看起来很奇怪:

如何保持auto increment,即使在触发ON DUPLICATE KEY 时也能正确递增?

【问题讨论】:

  • 这里关于AUTO_INCREMENT 序列中的空白的大多数问题都是由于将其用于错误任务的结果。这只是好奇,那就没关系:)
  • @ÁlvaroG.Vicario 我错过了使用AUTO_INCREMENT吗?
  • 我不知道。差距有问题吗?
  • @ÁlvaroG.Vicario 不是真的,但我只是希望它适当增加,因为它可能在未来的发展中有用

标签: mysql


【解决方案1】:

这种行为is documented(括号中的段落):

如果您指定 ON DUPLICATE KEY UPDATE,并插入一行 将导致 UNIQUE 索引或 PRIMARY KEY、MySQL 中的重复值 对旧行执行 UPDATE。例如,如果 a 列是 声明为 UNIQUE 并包含值 1,以下两个 语句具有类似的效果:

    INSERT INTO table (a,b,c) VALUES (1,2,3)   ON DUPLICATE KEY UPDATE c=c+1;

    UPDATE table SET c=c+1 WHERE a=1;

(效果不一样 一个 InnoDB 表,其中 a 是一个自增列。带着 自增列,INSERT 语句增加 自动递增值,但 UPDATE 不会。)

这里是一个简单的解释。 MySQL 尝试先进行插入。这是 id 自动递增的时候。一旦增加,它就会保持不变。然后检测到重复并进行更新。但是价值被遗漏了。

您不应该依赖auto_increment 没有间隙。如果这是一个要求,那么更新和插入的开销就会大得多。本质上,您需要锁定整个表,并对需要重新编号的所有内容重新编号,通常使用触发器。更好的解决方案是计算输出的增量值。

【讨论】:

  • +1。说得好。感谢您提供实际文档。 ;-)
  • 如果 auto_increment 不是必需的,您认为差距可以吗?
  • 这是 mysql 的愚蠢问题。好的,我不会说它没有间隙,但是今天我只有大约 500000 条经常更新的记录时,我达到了 16000000+ 行(MEDIUMINT)的限制。问题是我有另一个超过 250Gb 大小的表链接到这个表 - 并将其更新为 INT(并且在完全不需要时松散存储空间)需要几天时间。
  • 我不明白这个设计背后的原因。虽然我不介意这些差距,但我认为没有理由发生自动增量,因为 ID 从未存在过,所以它只会创建很多垃圾 ID。
  • 我定期使用ON DUPLICATE KEY UPDATE 更新多条记录。我不在乎这些差距,但我害怕达到最大列值。每次批量更新后定期使用ALTER TABLE table_name AUTO_INCREMENT=1(将 auto_increment 重置为最后一行 id + 1)是一种不好的做法吗?
【解决方案2】:

这个问题是一个相当老的问题,但我回答它可能对某人有所帮助,解决自动递增问题,在插入/重复更新部分之前使用以下代码并一起执行它们:

SET @NEW_AI = (SELECT MAX(`the_id`)+1 FROM `table_blah`);
SET @ALTER_SQL = CONCAT('ALTER TABLE `table_blah` AUTO_INCREMENT =', @NEW_AI);
PREPARE NEWSQL FROM @ALTER_SQL;
EXECUTE NEWSQL; 

在一个语句中,它应该如下所示:

SET @NEW_AI = (SELECT MAX(`the_id`)+1 FROM `table_blah`);
SET @ALTER_SQL = CONCAT('ALTER TABLE `table_blah` AUTO_INCREMENT =', @NEW_AI);
PREPARE NEWSQL FROM @ALTER_SQL;
EXECUTE NEWSQL; 
INSERT INTO `table_blah` (`the_col`) VALUES("the_value")
ON DUPLICATE KEY UPDATE `the_col` = "the_value";

【讨论】:

  • 所有像“不要担心差距”这样的答案都是垃圾,这应该被接受。我的 ID 数字用完了,我使用上面的代码进行 INSERT,一次添加了许多值,而且效果很好。谢谢;)
  • id 保持不变,但操作仍以正常bug 方式返回受影响的行数。
【解决方案3】:

我对自动增量中的间隙有同样的挫败感,但我找到了避免它的方法。

就先前讨论的“开销”而言。当我第一次编写我的数据库查询代码时,它做了很多单独的查询,花了 5 个小时。一旦我穿上 “重复密钥更新” 它把它降到了大约50秒。惊人的!无论如何,我解决它的方法是使用 2 个查询。这将花费的时间加倍到 2 分钟,这仍然可以。

首先我做了一个 sql 查询来写入所有数据(更新和插入),但是我在第一个查询中包含了“IGNORE”,所以这只是绕过了更新,只插入了新的东西。因此,假设您的 auto_increment 以前没有间隙,那么它仍然没有间隙,因为它只有新记录。我相信是更新导致了差距。所以对于更新: "INSERT IGNORE INTO mytablename(stuff,stuff2) VALUES"

接下来我做了那个 sql 查询的“ON DUPLICATE KEY UPDATE”变体。它将保持 ID 完好无损,因为所有正在更新的记录都已经有了 ID。它唯一破坏的是 auto_increment 值,当添加新记录时它会增加。因此解决方案是在添加任何新记录后修补此 auto_increment 值。

要修补自动增量值,请在您的 php 中使用此 sql: “ALTER TABLE mytablename AUTO_INCREMENT =”。 ($TableCount + 1);

这很有效,因为当您进行更新时,您不会增加记录的数量。因此我们可以使用 tablecount 来知道下一个 ID 应该是什么。您将 $TableCount 设置为表计数,然后我们加 1,这就是下一个增量数。

这既便宜又脏,但似乎很管用。不过,当其他东西写入数据库时​​,使用它可能会很糟糕。

【讨论】:

    【解决方案4】:

    我经常通过创建一个临时表来处理这个问题,在临时表中记录记录是否是新的,只对不是新的行进行更新,并对新行进行插入。这是一个完整的例子:

    ## THE SETUP
    
    # This is the table we're trying to insert into
    DROP TABLE IF EXISTS items;
    CREATE TABLE items (
      id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
      name VARCHAR(100) UNIQUE,
      price INT
    );
    
    # Put a few rows into the table
    INSERT INTO items (name, price) VALUES
      ("Bike", 200),
      ("Basketball", 10),
      ("Fishing rod", 25)
    ;
    
    ## THE INSERT/UPDATE
    
    # Create a temporary table to help with the update
    DROP TEMPORARY TABLE IF EXISTS itemUpdates;
    CREATE TEMPORARY TABLE itemUpdates (
      name VARCHAR(100) UNIQUE,
      price INT,
      isNew BOOLEAN DEFAULT(true)
    );
    
    # Change the price of the Bike and Basketball and add a new Tent item
    INSERT INTO itemUpdates (name, price) VALUES
      ("Bike", 150),
      ("Basketball", 8),
      ("Tent", 100)
    ;
    
    # For items that already exist, set isNew false
    UPDATE itemUpdates
    JOIN items
    ON items.name = itemUpdates.name
    SET isNew = false;
    
    # UPDATE the already-existing items
    UPDATE items
    JOIN itemUpdates
    ON items.name = itemUpdates.name
    SET items.price = itemUpdates.price
    WHERE itemUpdates.isNew = false;
    
    # INSERT the new items
    INSERT INTO items (name, price)
    SELECT name, price
    FROM itemUpdates
    WHERE itemUpdates.isNew = true;
    
    # Check the results
    SELECT * FROM items;
    # Results:
    # ID | Name        | Price
    # 1  | Bike        | 150
    # 2  | Basketball  | 8
    # 3  | Fishing rod | 25
    # 4  | Tent        | 100
            
    

    INSERT IGNORE INTO 方法更简单,但它忽略了 any 错误,这不是我想要的。我同意这是 MySQL 的奇怪行为,但这是我们必须处理的问题。

    【讨论】:

      【解决方案5】:

      我只是想我会添加,因为我试图找到我的问题的答案。 我无法停止重复警告,发现这是因为我将其设置为仅允许 127 个条目的 TINYINT,更改为 SMALL/MED/BIGINT 允许更多

      【讨论】:

        【解决方案6】:

        我认为这不是 MySQL 5.6 的问题。见this example

        【讨论】:

          【解决方案7】:

          在重复密钥更新 id=LAST_INSERT_ID(id)

          【讨论】:

            【解决方案8】:

            将数据库引擎从 InnoDB 更改为 MyIsam 将解决您的问题。

            【讨论】:

              【解决方案9】:

              为最终结果添加更少的直接答案和更多的修复。

              如果您不使用自动增量作为应用程序中的标识字段(而且您确实不应该这样做。UUID 或类似性质的东西是更好的做法),当然,如果您没有 multi - 数十亿行,你可以很容易地重置你的自动增量字段。

              SET SQL_SAFE_UPDATES = 0;
              SET  @num := 0;
              UPDATE my_table SET id = @num := (@num+1);
              ALTER TABLE my_table  AUTO_INCREMENT =1;
              

              我有点讨厌在 MySQL 中执行 INSERT UPDATE 时会发生这种情况。

              这不是我的代码。我在 SO 的某个地方找到了它,但那是很久以前的事了......

              补充说明,这并不是这个问题的真正答案。它更多的是帮助修复失控的自动增量字段。

              【讨论】:

                【解决方案10】:

                INSERT INTO table_blah ( material_item, ... hidden ) VALUES ( data, ... data ) ON DUPLICATE KEY UPDATE material_item = data, ... hidden = data

                是的,删除 ID=ID,因为它会自动添加 where PRIMARY KEY = PRIMARY KEY...

                【讨论】:

                • 但是如果它有重复的键会增加吗?
                • 如果你的表是 MyISSAM,它不会,在 InnoDB 上它会。还有其他方法可以解决 InnoDB 上的问题
                猜你喜欢
                • 2011-06-29
                • 2010-12-15
                • 2019-02-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多