Как проверить, хорошо ли INSERT в сохраненной функции? - PullRequest
7 голосов
/ 09 мая 2011

Я создаю хранимую функцию, которая должна вставлять новую строку в таблицу. В этой таблице также один уникальный столбец.

Как я могу проверить, все ли хорошо и строка действительно вставлена?

Как я могу точно проверить, найден ли этот уникальный столбец (например, попытаться добавить повторяющееся значение)?

Ответы [ 2 ]

7 голосов
/ 09 мая 2011

Вы можете проверить функцию 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.

Недостатком этого подхода является то, что вы не можете вернуться и использовать идентификаторы 2 и 4 из-за неудачных попыток ВСТАВИТЬ 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>

В этом примере сохраненная функция возвращает 0, если INSERT была в порядке, и возвращает 1 с дублирующим ключом на имени. Преимущество? Нет потерянных идентификаторов для auto_increment. Недостаток? Выполнение инструкции SELECT каждый раз для проверки имени, уже присутствующего в таблице.

Вы можете выбрать способ обработки дубликатов ключей. Первый метод позволяет mysqld обрабатывать состояние INSERT IGNORE. У второго метода есть сохраненная функция, проверяющая дубликат ключа сначала перед INSERT.

0 голосов
/ 09 мая 2011

Хранимые процедуры - "все или ничего"

Поэтому, если вы включите INSERT в sproc, и если INSERT завершится неудачно из-за ошибки дублирующегося ключа, весь sproc будет свернутназад.

Если sproc выполняется без ошибок, вы можете быть уверены, что INSERT не было ошибки.Теперь, это не означает, что INSERT на самом деле происходит только потому, что sproc завершает, просто, что не было ошибок ... например, если у вас было какое-то WHEREпредложение, которое исключает INSERT, но не выдает ошибку, тогда может быть некоторая двусмысленность.

...