MySQL函数与LAST_INSERT_ID()给出意想不到的结果

时间:2015-05-16 10:21:09

标签: mysql last-insert-id

我花了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 ;

目标?当state表中已存在状态时,它应返回id列(这是自动增量键)。当它不存在时,它应该插入它并返回新的(自动)分配的id。

该功能正在运行,但做了两件我不想要的事情:

1)当记录已经存在时,它返回零而不是现有记录的id。我的代码旨在解决这个问题,但它不起作用请注意我在表上定义了一个索引,以确保state.name在表中是唯一的,以便INSERT IGNORE工作

2)即使记录存在,LAST_INSERT_ID()也会继续递增,因此没有插入任何内容。因此,例如,如果状态表只有一个记录开始(比如id ==> 1,名称==>'状态A'),如果我调用该函数2次尝试重新插入'状态A' (不会像上面第1点中提到的那样插入),然后再次调用它来插入“状态B”,表格现在将有第2条记录ID ==> 4,name ==> '状态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;

END $$

2 个答案:

答案 0 :(得分:1)

洛伦兹已经给你解决方案了:

 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)返回NULL时,它可能不适用于第一个状态。

答案 1 :(得分:0)

这是InnoDB的预期行为。 insert总是递增insert_id,即使插入失败也是如此。原因是数据库引擎在尝试插入之前保留了id。

您可以通过首先选择表中的行来解决问题,并仅在名称不存在时插入名称。

另外,您将函数声明为确定性函数,但事实并非如此。只是声明它不会使它具有确定性。