我有这样的表结构
当我向表中插入行时,我正在使用此查询:
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
触发并且我插入新行时,ID对我来说很奇怪:
即使auto increment
触发ON DUPLICATE KEY
,我如何保持{{1}}正确递增?
答案 0 :(得分:35)
记录了此行为:
如果指定ON DUPLICATE KEY UPDATE,则插入一行 会在UNIQUE索引或PRIMARY KEY,MySQL中导致重复值 执行旧行的更新。例如,如果列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 :(得分:1)
我有同样的挫败感,也有解决方案。
首先让我谈谈“开销”。当我第一次编写数据库更新代码时,它执行了许多单独的查询,因此花费了5个小时。一旦我进行了“ ON DUPLICATE KEY UPDATE”,我将其降低到大约50秒。惊人!无论如何,我解决它的方式意味着我必须执行2个查询,所以我将其扩展到2分钟而不是1分钟,我认为这是一个合理的成本。
首先,我对包含更新和插入数据的数据进行了标准的sql查询,但是我包含了“ IGNORE”,因此这只会绕过更新而仅插入新内容。 “将IGNORE插入mytablename(stuff,stuff2)VALUES中”-无需重复更新键。这意味着将放入所有新记录。这些新记录仍不会破坏auto_increment。
接下来,我执行了该sql语句的“ ON DUPLICATE KEY UPDATE”版本,现在这会破坏auto_increment值,但是我们输入的所有更新和插入内容都是完美的auto_incrememnt值。只是我们添加的下一条新记录是错误的...
因此,最后您只需通过此sql修补混乱: “ ALTER TABLE mytablename AUTO_INCREMENT =”。 ($ TableCount + 1);
请记住实际上将$ TableCount设置为表计数,然后我们加1,这样就可以在下一条记录写入时使用了。
这便宜又脏,但是我喜欢。确保不要同时使用另一个函数等向数据库写入数据,因为这可能会导致错误。我认为有一种方法可以锁定桌子?但我不需要这种情况。
答案 2 :(得分:1)
我经常通过创建一个临时表来处理这个问题,在临时表中记录记录是否是新的,仅对非新行执行 UPDATE,并对新行执行 INSERT。这是一个完整的示例:
## 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
方法更简单,但它忽略了任何 错误,这不是我想要的。我同意这是 MySQL 的一种奇怪行为,但这是我们必须处理的。
答案 3 :(得分:0)
这个问题是一个相当老的问题,但我回答可能对某人有帮助,要解决自动递增问题,请在插入/复制重复更新部分之前使用以下代码并将其全部执行:
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";
答案 4 :(得分:0)
我只是想补充一下,因为我试图找到解决问题的方法。 我无法停止重复的警告,发现这是因为我将其设置为TINYINT,它仅允许127个条目,更改为SMALL / MED / BIGINT则允许更多
答案 5 :(得分:0)
我不认为这是 MySQL 5.6 的问题。见this example。
答案 6 :(得分:0)
重复密钥更新 id=LAST_INSERT_ID(id)
答案 7 :(得分:0)
将数据库引擎从 InnoDB 更改为 MyIsam 将解决您的问题。
答案 8 :(得分:-1)
INSERT INTO table_blah ( material_item, ... hidden ) VALUES ( data, ... data ) ON DUPLICATE KEY UPDATE material_item = data, ... hidden = data
是删除ID = ID,因为它会自动添加PRIMARY KEY = PRIMARY KEY ...