Mysql
 sql >> Database >  >> RDS >> Mysql

Come verificare se INSERT è andato bene nella funzione memorizzata?

Puoi controllare la funzione LAST_INSERT_ID() e INSERT IGNORE.

Se INSERT IGNORE ha esito positivo, viene restituita la chiave primaria. Creiamo una tabella con una chiave primaria a incremento automatico e una chiave univoca su un nome.

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;

Ecco l'esempio in esecuzione:

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>

Come mostrato nell'esempio precedente, puoi controllare il valore di ritorno della funzione. Un valore di ritorno diverso da zero significa che INSERT IGNORE è andato bene. Un valore di ritorno zero indica una chiave duplicata senza introdurre un numero di errore in mysqld.

Lo svantaggio di questo approccio è che non puoi tornare indietro e utilizzare id 2 e 4 a causa di tentativi falliti di INSERT IGNORE in caso di chiave duplicata.

Proviamo un altro esempio con una diversa configurazione della funzione memorizzata utilizzando INSERT e senza utilizzare 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;

Ecco il risultato:

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>

In questo esempio, la funzione memorizzata restituisce 0 se INSERT è OK e restituisce 1 con una chiave duplicata sul nome. Il vantaggio? Nessun numero ID sprecato per auto_increment. Lo svantaggio? Eseguire ogni volta un'istruzione SELECT per verificare che il nome sia già presente nella tabella.

Puoi scegliere in che modo vuoi gestire le chiavi duplicate. Il primo metodo consente a mysqld di gestire la condizione di INSERT IGNORE. Il secondo metodo ha la funzione memorizzata che controlla la chiave duplicata prima di INSERT.