【问题标题】:How to check if INSERT went well in stored function?如何检查 INSERT 在存储功能中是否顺利?
【发布时间】:2011-08-21 19:02:39
【问题描述】:

我正在创建一个应该向表中插入新行的存储函数。在这个表中也是一个唯一的列。

如何检查是否一切顺利并且是否真的插入了行?

如何准确检查是否找到了这个唯一列(例如 - 尝试添加重复值)?

【问题讨论】:

    标签: mysql sql stored-functions


    【解决方案1】:

    您可以检查 LAST_INSERT_ID() 函数和 INSERT IGNORE。

    如果 INSERT IGNORE 成功,则返回主键。让我们创建一个具有自增主键和名称唯一键的表。

    use test
    DROP TABLE IF EXISTS nametable;
    CREATE TABLE nametable
    (
      id int not null auto_increment,
      name varchar(20) not null,
      primary key (id),
      unique key (name)
    );
    DELIMITER $$
    DROP FUNCTION IF EXISTS `test`.`InsertName` $$
    CREATE FUNCTION `test`.`InsertName` (newname VARCHAR(20)) RETURNS INT
    BEGIN
      INSERT IGNORE INTO test.nametable (name) VALUES (newname);
      RETURN LAST_INSERT_ID();
    END $$
    DELIMITER ;
    SELECT InsertName('rolando');
    SELECT InsertName('rolando');
    SELECT InsertName('pamela');
    SELECT InsertName('pamela');
    SHOW CREATE TABLE test.nametable\G
    SELECT * FROM test.nametable;
    

    这是正在运行的示例:

    mysql> use test
    Database changed
    mysql> DROP TABLE IF EXISTS nametable;
    Query OK, 0 rows affected (0.04 sec)
    
    mysql> CREATE TABLE nametable
        -> (
        ->   id int not null auto_increment,
        ->   name varchar(20) not null,
        ->   primary key (id),
        ->   unique key (name)
        -> );
    Query OK, 0 rows affected (0.07 sec)
    
    mysql> DELIMITER $$
    mysql> DROP FUNCTION IF EXISTS `test`.`InsertName` $$
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> CREATE FUNCTION `test`.`InsertName` (newname VARCHAR(20)) RETURNS INT
        -> BEGIN
        ->   INSERT IGNORE INTO test.nametable (name) VALUES (newname);
        ->   RETURN LAST_INSERT_ID();
        -> END $$
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> DELIMITER ;
    mysql> SELECT InsertName('rolando');
    +-----------------------+
    | InsertName('rolando') |
    +-----------------------+
    |                     1 |
    +-----------------------+
    1 row in set (0.03 sec)
    
    mysql> SELECT InsertName('rolando');
    +-----------------------+
    | InsertName('rolando') |
    +-----------------------+
    |                     0 |
    +-----------------------+
    1 row in set (0.02 sec)
    
    mysql> SELECT InsertName('pamela');
    +----------------------+
    | InsertName('pamela') |
    +----------------------+
    |                    3 |
    +----------------------+
    1 row in set (0.02 sec)
    
    mysql> SELECT InsertName('pamela');
    +----------------------+
    | InsertName('pamela') |
    +----------------------+
    |                    0 |
    +----------------------+
    1 row in set (0.03 sec)
    
    mysql> SHOW CREATE TABLE test.nametable\G
    *************************** 1. row ***************************
           Table: nametable
    Create Table: CREATE TABLE `nametable` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `name` (`name`)
    ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1
    1 row in set (0.00 sec)
    
    mysql> SELECT * FROM test.nametable;
    +----+---------+
    | id | name    |
    +----+---------+
    |  3 | pamela  |
    |  1 | rolando |
    +----+---------+
    2 rows in set (0.00 sec)
    
    mysql>
    

    如上例所示,您可以检查函数的返回值。非零返回值意味着 INSERT IGNORE 顺利进行。零返回值表示重复键,没有向 mysqld 引入错误号。

    这种方法的缺点是您无法返回并使用 id 2 和 4,因为在重复键的情况下尝试 INSERT IGNORE 失败。

    让我们尝试另一个使用 INSERT 和不使用 LAST_INSERT_ID() 的不同存储函数设置的示例:

    use test
    DROP TABLE IF EXISTS nametable;
    CREATE TABLE nametable
    (
      id int not null auto_increment,
      name varchar(20) not null,
      primary key (id),
      unique key (name)
    );
    DELIMITER $$
    DROP FUNCTION IF EXISTS `test`.`InsertName` $$
    CREATE FUNCTION `test`.`InsertName` (newname VARCHAR(20)) RETURNS INT
    BEGIN
      DECLARE rv INT;
      SELECT COUNT(1) INTO rv FROM test.nametable WHERE name = newname;
      IF rv = 0 THEN
        INSERT INTO test.nametable (name) VALUES (newname);
      END IF;
      RETURN rv;
    END $$
    DELIMITER ;
    SELECT InsertName('rolando');
    SELECT InsertName('rolando');
    SELECT InsertName('pamela');
    SELECT InsertName('pamela');
    SHOW CREATE TABLE test.nametable\G
    SELECT * FROM test.nametable;
    

    结果如下:

    mysql> use test
    Database changed
    mysql> DROP TABLE IF EXISTS nametable;
    Query OK, 0 rows affected, 1 warning (0.00 sec)
    
    mysql> CREATE TABLE nametable
        -> (
        ->   id int not null auto_increment,
        ->   name varchar(20) not null,
        ->   primary key (id),
        ->   unique key (name)
        -> );
    Query OK, 0 rows affected (0.10 sec)
    
    mysql> DELIMITER $$
    mysql> DROP FUNCTION IF EXISTS `test`.`InsertName` $$
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> CREATE FUNCTION `test`.`InsertName` (newname VARCHAR(20)) RETURNS INT
        -> BEGIN
        ->   DECLARE rv INT;
        ->   SELECT COUNT(1) INTO rv FROM test.nametable WHERE name = newname;
        ->   IF rv = 0 THEN
        ->     INSERT INTO test.nametable (name) VALUES (newname);
        ->   END IF;
        ->   RETURN rv;
        -> END $$
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> DELIMITER ;
    mysql> SELECT InsertName('rolando');
    +-----------------------+
    | InsertName('rolando') |
    +-----------------------+
    |                     0 |
    +-----------------------+
    1 row in set (0.04 sec)
    
    mysql> SELECT InsertName('rolando');
    +-----------------------+
    | InsertName('rolando') |
    +-----------------------+
    |                     1 |
    +-----------------------+
    1 row in set (0.00 sec)
    
    mysql> SELECT InsertName('pamela');
    +----------------------+
    | InsertName('pamela') |
    +----------------------+
    |                    0 |
    +----------------------+
    1 row in set (0.03 sec)
    
    mysql> SELECT InsertName('pamela');
    +----------------------+
    | InsertName('pamela') |
    +----------------------+
    |                    1 |
    +----------------------+
    1 row in set (0.00 sec)
    
    mysql> SHOW CREATE TABLE test.nametable\G
    *************************** 1. row ***************************
           Table: nametable
    Create Table: CREATE TABLE `nametable` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `name` (`name`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
    1 row in set (0.00 sec)
    
    mysql> SELECT * FROM test.nametable;
    +----+---------+
    | id | name    |
    +----+---------+
    |  2 | pamela  |
    |  1 | rolando |
    +----+---------+
    2 rows in set (0.00 sec)
    
    mysql>
    

    在此示例中,如果 INSERT 正确,则存储的函数返回 0,并在名称上带有重复键的情况下返回 1。优势? auto_increment 不会浪费 id 编号。劣势?每次执行一个 SELECT 语句来检查表中已经存在的名称。

    您可以选择处理重复键的方式。第一种方法让 mysqld 处理 INSERT IGNORE 的条件。第二种方法是在 INSERT 之前首先检查重复键的存储函数。

    【讨论】:

    【解决方案2】:

    存储过程是“全有或全无”

    因此,如果您在 sproc 中包含 INSERT,并且 INSERT 因重复键错误而失败,则整个 sproc 将被回滚。

    如果存储过程没有错误地执行,您可以确信INSERT 没有错误。现在,这 意味着 INSERT 实际上 发生 只是因为存储过程完成,只是没有错误......例如,如果你有一些 @ 987654325@ 子句排除了INSERT 但不会引发错误,则可能存在一些歧义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-22
      • 2021-12-07
      • 1970-01-01
      • 2017-11-07
      • 1970-01-01
      • 2021-06-11
      • 1970-01-01
      相关资源
      最近更新 更多