MySQL 带有 LAST_INSERT_ID() 的函数给出了意想不到的结果
MySQL Function with LAST_INSERT_ID() giving unexpected results
我已经为此花费了 24 小时,需要利用现有知识来加快速度。
我有这个 mysql 功能:
DELIMITER $$
USE `db`$$
DROP FUNCTION IF EXISTS `insertindb`$$
CREATE DEFINER=`root`@`localhost` FUNCTION `insertindb`(itemtype VARCHAR(10), dname VARCHAR (50), id INTEGER, fname VARCHAR(50), lname VARCHAR(50)) RETURNS INT(11)
DETERMINISTIC
BEGIN
DECLARE ret INT;
DECLARE ret2 INT;
IF itemtype = 'state' THEN
INSERT IGNORE INTO state (`name`) VALUES (dname);
SELECT LAST_INSERT_ID() INTO ret;
IF ret = 0 THEN
SELECT `id` INTO ret2 FROM state WHERE `name` LIKE dname;
SET ret = ret2;
END IF;
END IF;
RETURN ret;
END$$
DELIMITER ;
objective?当一个状态已经存在于state
table时,它应该returnid列(即自增键)。当它不存在时,它应该插入它和 return 新(自动)分配的 id。
该功能正在运行,但做了 2 件我不想做的事情:
1) 当记录已经存在时,它 return 为零而不是现有记录的 id。我的代码旨在解决这个问题,但它不起作用请注意,我在 table 上定义了一个索引,以确保 state.name 在 table 中是唯一的,以便 INSERT IGNORE 工作
2) LAST_INSERT_ID() 保持递增,即使记录存在因此没有插入任何内容。因此,例如,如果状态 table 只有一个记录开始(比如 id==>1,名称 ==> 'State A',)如果我调用该函数 2 次以尝试重新插入 'State A'(不会如上面第 1 点所述插入)然后再次调用它插入 'State B',table 现在将有第二条记录 id==>4,name == > 'State B' 跳过 id 值 2 和 3。有没有办法让 LAST_INSERT_ID 以不同的方式工作?
我现在的代码
BEGIN
DECLARE ret,ret2 INT;
IF itemtype = 'state' THEN
SELECT `id` INTO ret FROM state WHERE `name` LIKE dname;
#insert into debug (dump1,dump2) values ('ret will be',ret);
IF ret IS NULL OR ret = '' THEN
INSERT IGNORE INTO state (`name`) VALUES (dname);
SELECT LAST_INSERT_ID() INTO ret2;
#SELECT `id` INTO ret FROM state WHERE `name` LIKE dname;
RETURN ret2;
ELSE
RETURN ret;
END IF;
END IF;
结束$$
这是 InnoDB 的预期行为。 insert
总是递增 insert_id,即使插入失败。原因是数据库引擎在尝试插入之前保留了 id。
您可以通过先选择 table 中的行来解决问题,然后仅在该名称尚不存在时才插入该名称。
此外,您将函数声明为确定性的,但事实并非如此。仅仅声明它并不能使它具有确定性。
洛伦兹已经给了你答案:
SELECT `id` INTO ret FROM state WHERE `name` LIKE dname;
IF ret IS NULL THEN
INSERT IGNORE INTO state (`name`) VALUES (dname);
SELECT `id` INTO ret FROM state WHERE `name` LIKE dname;
END IF;
如果您同时多次调用同一个函数,您仍然有可能在 id 之间出现差距,那么您需要不同的方法:
1) 删除您 id 上的 auto_increment
2) 在 id 上创建唯一索引
3) 使用这种查询插入新值:
INSERT INTO state (name, id)
SELECT dname, MAX(id) + 1 FROM state
WHERE NOT EXISTS (SELECT * FROM state WHERE name=dname);
SELECT id INTO ret FROM state WHERE name=dname;
当 MAX(id) returns NULL 时,它可能不适用于第一个状态。
我已经为此花费了 24 小时,需要利用现有知识来加快速度。
我有这个 mysql 功能:
DELIMITER $$
USE `db`$$
DROP FUNCTION IF EXISTS `insertindb`$$
CREATE DEFINER=`root`@`localhost` FUNCTION `insertindb`(itemtype VARCHAR(10), dname VARCHAR (50), id INTEGER, fname VARCHAR(50), lname VARCHAR(50)) RETURNS INT(11)
DETERMINISTIC
BEGIN
DECLARE ret INT;
DECLARE ret2 INT;
IF itemtype = 'state' THEN
INSERT IGNORE INTO state (`name`) VALUES (dname);
SELECT LAST_INSERT_ID() INTO ret;
IF ret = 0 THEN
SELECT `id` INTO ret2 FROM state WHERE `name` LIKE dname;
SET ret = ret2;
END IF;
END IF;
RETURN ret;
END$$
DELIMITER ;
objective?当一个状态已经存在于state
table时,它应该returnid列(即自增键)。当它不存在时,它应该插入它和 return 新(自动)分配的 id。
该功能正在运行,但做了 2 件我不想做的事情:
1) 当记录已经存在时,它 return 为零而不是现有记录的 id。我的代码旨在解决这个问题,但它不起作用请注意,我在 table 上定义了一个索引,以确保 state.name 在 table 中是唯一的,以便 INSERT IGNORE 工作
2) LAST_INSERT_ID() 保持递增,即使记录存在因此没有插入任何内容。因此,例如,如果状态 table 只有一个记录开始(比如 id==>1,名称 ==> 'State A',)如果我调用该函数 2 次以尝试重新插入 'State A'(不会如上面第 1 点所述插入)然后再次调用它插入 'State B',table 现在将有第二条记录 id==>4,name == > 'State B' 跳过 id 值 2 和 3。有没有办法让 LAST_INSERT_ID 以不同的方式工作?
我现在的代码
BEGIN
DECLARE ret,ret2 INT;
IF itemtype = 'state' THEN
SELECT `id` INTO ret FROM state WHERE `name` LIKE dname;
#insert into debug (dump1,dump2) values ('ret will be',ret);
IF ret IS NULL OR ret = '' THEN
INSERT IGNORE INTO state (`name`) VALUES (dname);
SELECT LAST_INSERT_ID() INTO ret2;
#SELECT `id` INTO ret FROM state WHERE `name` LIKE dname;
RETURN ret2;
ELSE
RETURN ret;
END IF;
END IF;
结束$$
这是 InnoDB 的预期行为。 insert
总是递增 insert_id,即使插入失败。原因是数据库引擎在尝试插入之前保留了 id。
您可以通过先选择 table 中的行来解决问题,然后仅在该名称尚不存在时才插入该名称。
此外,您将函数声明为确定性的,但事实并非如此。仅仅声明它并不能使它具有确定性。
洛伦兹已经给了你答案:
SELECT `id` INTO ret FROM state WHERE `name` LIKE dname;
IF ret IS NULL THEN
INSERT IGNORE INTO state (`name`) VALUES (dname);
SELECT `id` INTO ret FROM state WHERE `name` LIKE dname;
END IF;
如果您同时多次调用同一个函数,您仍然有可能在 id 之间出现差距,那么您需要不同的方法:
1) 删除您 id 上的 auto_increment 2) 在 id 上创建唯一索引 3) 使用这种查询插入新值:
INSERT INTO state (name, id)
SELECT dname, MAX(id) + 1 FROM state
WHERE NOT EXISTS (SELECT * FROM state WHERE name=dname);
SELECT id INTO ret FROM state WHERE name=dname;
当 MAX(id) returns NULL 时,它可能不适用于第一个状态。