Why does INSERT ... ON DUPLICATE KEY UPDATE affected rows reports 2? - mysql

If I execute the following statement:
INSERT INTO users(id, username) VALUES(102, 'test') ON DUPLICATE KEY UPDATE username='test';
And if the value for column id exists in DB, but the value for username is different, then the reported number of affected rows is 2. To be more precise:
If the new record is an exact duplicate, and nothing is updated/inserted then the affected rows is 0
If the id does not exist yet, then a new record is inserted, and affected rows is 1
If the id exists, but the existing record is updated the value for affected rows is set to 2
What is the reason for this? Is this to provide information back of what action took place? Are there actually 2 rows being affected?
Tested on MariaDB 10.3.7 with
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(355) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8mb4;
INSERT INTO `users` (`id`, `username`) VALUES (102, 'Some name');

INSERT... ON DUPLICATE KEY UPDATE executes two actions. You can demonstrate this with triggers that set session variables as a side-effect:
mysql> create trigger i before insert on users for each row set #i = true;
mysql> create trigger u before update on users for each row set #u = true;
mysql> INSERT INTO users(id, username) VALUES(102, 'test') ON DUPLICATE KEY UPDATE username='test';
Query OK, 2 rows affected (0.01 sec)
mysql> select #i, #u;
+------+------+
| #i | #u |
+------+------+
| 1 | 1 |
+------+------+

Related

If mail message doesn't exist, add to mysql database [duplicate]

This question already has answers here:
How can I do 'insert if not exists' in MySQL?
(11 answers)
Closed 2 years ago.
I am trying to execute the following query:
INSERT INTO table_listnames (name, address, tele)
VALUES ('Rupert', 'Somewhere', '022')
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name='value'
);
But this returns an error. Basically I don't want to insert a record if the 'name' field of the record already exists in another record - how to check if the new name is unique?
I'm not actually suggesting that you do this, as the UNIQUE index as suggested by Piskvor and others is a far better way to do it, but you can actually do what you were attempting:
CREATE TABLE `table_listnames` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`address` varchar(255) NOT NULL,
`tele` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Insert a record:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
+----+--------+-----------+------+
Try to insert the same record again:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
+----+--------+-----------+------+
Insert a different record:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'John'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
| 2 | John | Doe | 022 |
+----+--------+-----------+------+
And so on...
Update:
To prevent #1060 - Duplicate column name error in case two values may equal, you must name the columns of the inner SELECT:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Unknown' AS name, 'Unknown' AS address, '022' AS tele) AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+---------+-----------+------+
| id | name | address | tele |
+----+---------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
| 2 | John | Doe | 022 |
| 3 | Unknown | Unknown | 022 |
+----+---------+-----------+------+
INSERT doesn't allow WHERE in the syntax.
What you can do: create a UNIQUE INDEX on the field which should be unique (name), then use either:
normal INSERT (and handle the error if the name already exists)
INSERT IGNORE (which will fail silently cause a warning (instead of error) if name already exists)
INSERT ... ON DUPLICATE KEY UPDATE (which will execute the UPDATE at the end if name already exists, see documentation)
Worked :
INSERT INTO users (full_name, login, password)
SELECT 'Mahbub Tito','tito',SHA1('12345') FROM DUAL
WHERE NOT EXISTS
(SELECT login FROM users WHERE login='tito');
MySQL provides a very cute solution :
REPLACE INTO `table` VALUES (5, 'John', 'Doe', SHA1('password'));
Very easy to use since you have declared a unique primary key (here with value 5).
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;
Here the mysql query, that insert records if not exist and will ignore existing similar records.
----Untested----
You can easily use the following way :
INSERT INTO ... ON DUPLICATE KEY UPDATE ...
In this way, you can insert any new raw and if you have duplicate data, replace a specific column ( The best columns are timestamps ).
For example :
CREATE TABLE IF NOT EXISTS Devices (
id INT(6) NOT NULL AUTO_INCREMENT,
unique_id VARCHAR(100) NOT NULL PRIMARY KEY,
created_at VARCHAR(100) NOT NULL,
UNIQUE KEY unique_id (unique_id),
UNIQUE KEY id (id)
)
CHARACTER SET utf8
COLLATE utf8_unicode_ci;
INSERT INTO Devices(unique_id, time)
VALUES('$device_id', '$current_time')
ON DUPLICATE KEY UPDATE time = '$current_time';
To overcome a similar problem, I have modified the table to have a unique column. Using your example, on creation I would have something like:
name VARCHAR(20),
UNIQUE (name)
and then use the following query when inserting into it:
INSERT IGNORE INTO train
set table_listnames='Rupert'
If you really can't get a unique index on the table, you could try...
INSERT INTO table_listnames (name, address, tele)
SELECT 'Rupert', 'Somewhere', '022'
FROM some_other_table
WHERE NOT EXISTS (SELECT name
FROM table_listnames
WHERE name='Rupert')
LIMIT 1;
This query works well:
INSERT INTO `user` ( `username` , `password` )
SELECT * FROM (SELECT 'ersks', md5( 'Nepal' )) AS tmp
WHERE NOT EXISTS (SELECT `username` FROM `user` WHERE `username` = 'ersks'
AND `password` = md5( 'Nepal' )) LIMIT 1
And you can create the table using following query:
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`password` varchar(32) NOT NULL,
`status` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Note: Create table using second query before trying to use first query.
Brian Hooper :
You almost hit the point but you have an error in your synatx. Your insert will never work. I tested on my database and here is the right answer:
INSERT INTO podatki (datum,ura,meritev1,meritev1_sunki,impulzi1,meritev2,meritev2_sunki,impulzi2)
SELECT '$datum', '$ura', '$meritve1','$sunki1','$impulzi1','$meritve2','$sunki2','$impulzi2'
FROM dual
WHERE NOT EXISTS (SELECT datum,ura
FROM podatki
WHERE datum='$datum' and ura='$ura'
I'm giving you my example of y table. Insert is almost the same like Bian Hooper wrote, except that I put the select FROM DUAL ont from other table.
Cind regards, Ivan
This is not an answer, it's just a note. The query like the one in the accepted answer does not work if the inserted values are duplicates, like here:
INSERT INTO `addr` (`email`, `name`)
SELECT * FROM (SELECT 'user#domain.tld', 'user#domain.tld') AS tmp
WHERE NOT EXISTS (
SELECT `email` FROM `addr` WHERE `email` LIKE 'user#domain.tld'
);
Error
SQL query: Copy Documentation
MySQL said: Documentation
#1060 - Duplicate column name 'user#domain.tld'
In the contrary, the query like the one from Mahbub Tito's answer works fine:
INSERT INTO `addr` (`email`, `name`)
SELECT 'user#domain.tld', 'user#domain.tld'
WHERE NOT EXISTS (
SELECT `email` FROM `addr` WHERE `email` LIKE 'user#domain.tld'
);
1 row inserted.
Tested in MariaDB
insert into customer_keyskill(customerID, keySkillID)
select 2,1 from dual
where not exists (
select customerID from customer_keyskill
where customerID = 2
and keySkillID = 1 )
You are inserting not Updating the result.
You can define the name column in primary column or set it is unique.
I had a problem, and the method Mike advised worked partly, I had an error Dublicate Column name = '0', and changed the syntax of your query as this`
$tQ = "INSERT INTO names (name_id, surname_id, sum, sum2, sum3,sum4,sum5)
SELECT '$name', '$surname', '$sum', '$sum2', '$sum3','$sum4','$sum5'
FROM DUAL
WHERE NOT EXISTS (
SELECT sum FROM names WHERE name_id = '$name'
AND surname_id = '$surname') LIMIT 1;";
The problem was with column names. sum3 was equal to sum4 and mysql throwed dublicate column names, and I wrote the code in this syntax and it worked perfectly,
I had a similar problem and I needed to insert multiple if not existing. So from the examples above I came to this combination... it's here just in case somebody would need it.
Notice:
I had to define name everywhere as MSSQL required it... MySQL works with * too.
INSERT INTO names (name)
SELECT name
FROM
(
SELECT name
FROM
(
SELECT 'Test 4' as name
) AS tmp_single
WHERE NOT EXISTS
(
SELECT name FROM names WHERE name = 'Test 4'
)
UNION ALL
SELECT name
FROM
(
SELECT 'Test 5' as name
) AS tmp_single
WHERE NOT EXISTS
(
SELECT name FROM names WHERE name = 'Test 5'
)
) tmp_all;
MySQL:
CREATE TABLE names (
OID int(11) NOT NULL AUTO_INCREMENT,
name varchar(32) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (OID),
UNIQUE KEY name_UNIQUE (name)
) ENGINE=InnoDB AUTO_INCREMENT=1;
or
MSSQL:
CREATE TABLE [names] (
[OID] INT IDENTITY (1, 1) NOT NULL,
[name] NVARCHAR (32) NOT NULL,
PRIMARY KEY CLUSTERED ([OID] ASC)
);
CREATE UNIQUE NONCLUSTERED INDEX [Index_Names_Name] ON [names]([name] ASC);
This query can be used in PHP code.
I have an ID column in this table, so I need check for duplication for all columns except this ID column:
#need to change values
SET #goodsType = 1, #sybType=5, #deviceId = asdf12345SDFasdf2345;
INSERT INTO `devices` (`goodsTypeId`, `goodsId`, `deviceId`) #need to change tablename and columnsnames
SELECT * FROM (SELECT #goodsType, #sybType, #deviceId) AS tmp
WHERE NOT EXISTS (
SELECT 'goodsTypeId' FROM `devices` #need to change tablename and columns names
WHERE `goodsTypeId` = #goodsType
AND `goodsId` = #sybType
AND `deviceId` = #deviceId
) LIMIT 1;
and now new item will be added only in case of there is not exist row with values configured in SET string

How to use INSERT... ON DUPLICATE?

Structure table:
CREATE TABLE IF NOT EXISTS `table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rows_id` int(11) NOT NULL,
`url_id` int(11) NOT NULL,
`keyword_id` int(11) NOT NULL,
`date` datetime NOT NULL,
`seet` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
STRUCTURE TABLE WITH TEST ROWS ON SQLFIDDLE
query insert:
INSERT INTO `table` (`id`, `rows_id`, `url_id`, `keyword_id`, `date`, `seet`)
VALUES
(1, 1, 2, 1, '2014-05-01 00:00:00', 1);
I would like insert this row or update row if table already have row with date = '2014-05-01 00:00:00' and rows_id = '1' and keyword_id = '1'.
In dev.mysql.com i see query INSERT ... ON DUPLICATE KEY UPDATE but how make insert or update in my case ?
INSERT...ON DUPLICATE KEY UPDATE (IODKU) does an update only if the values you insert conflict with an existing row based on a PRIMARY KEY or UNIQUE KEY.
So you need to define a UNIQUE KEY over the three columns or else IODKU can't tell if the row conflicts with an existing one.
ALTER TABLE `table` ADD UNIQUE KEY (date, rows_id, keyword_id);
You said in a comment above that you can have more than one row with the same rows_id. That's fine -- the unique constraint says that you can't have two rows that duplicate the same combination of three values in date, rows_id, and keyword_id. But you can have multiple rows that have the same value in any one of those columns, as long as the combination of the three is unique.
Here's a quick demo:
mysql> create table t (
id int auto_increment primary key,
d int,
r int,
k int,
unique key(d,r,k)
);
mysql> insert into t values (1,1,1,1);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t values (1,1,1,1) on duplicate key update d = values(d)+1;
Query OK, 2 rows affected (0.00 sec)
mysql> select * from t;
+----+------+------+------+
| id | d | r | k |
+----+------+------+------+
| 1 | 2 | 1 | 1 |
+----+------+------+------+
From what I'm understanding from your question, this might help. This SQL Query will test if a row exists and update the row or create a new one based on whether or not that row already exists.
IF EXISTS (SELECT * FROM table WHERE date = 2014-05-01 00:00:00 AND rows_id = 1 AND keyword_id = 1)
BEGIN
UPDATE table SET Params=values
END
ELSE
BEGIN
INSERT INTO `table` (`id`, `rows_id`, `url_id`, `keyword_id`, `date`, `seet`) VALUES (1, 1, 2, 1, '2014-05-01 00:00:00', 1)
END
If the row exists (if the SELECT statement returns data) the UPDATE query will run. If the row does not exist (SELECT statement returns no data) then the INSERT INTO statement will run.

Set PK in Mysql database with autoincrement(string,int)

I want to have a composite primarykey with (string,int) format where the string is a default value and int is set to autoincrement. Is it possible to achieve this. I need it in the below format
TC_01
TC_02
TC_03
.
.
.
so on
As the auto incremented value is identified just before insertion into the table, you should better depend on a before insert trigger on your table.
To set the concatenated string as in the form TC_002 etc, you need following steps in the trigger body.
Let us say if your str_field is defined as default 'TC', then
FOR EACH ROW BEGIN
SET NEW.str_field = concat( NEW.str_field, '_', NEW.auto_int_field );
END;
Note:
In case you have input a specific value, that is used for
concatenation.
In case if str_field allows null and you have not defined a default
value and you have not input any value for the same for insertion, the
concat( NEW.str_field, ... results a NULL.
You can define composite unique key on str_field and auto_int_field primary key field.
Constraint UK_CMP unique key ( str_field, auto_int_field )
EDIT:
when i use this trigger am getting str_field as TC_0 in all rows. But i should get TC_1,TC_2 etc.
You need an example like this:
mysql> drop table if exists cmp_pk;
Query OK, 0 rows affected (0.22 sec)
mysql> create table cmp_pk(
-> str varchar(10) not null unique default 'TC_',
-> id int not null auto_increment primary key,
-> constraint uk_cmp unique key( str, id )
-> );
Query OK, 0 rows affected (0.65 sec)
mysql>
mysql> drop trigger if exists bi_cmp_pk;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> delimiter //
mysql> create trigger bi_cmp_pk before insert on cmp_pk
-> for each row begin
-> SET #NEW_ID := ( SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES
-> WHERE TABLE_NAME='cmp_pk' AND TABLE_SCHEMA=DATABASE() );
-> SET NEW.str = concat( NEW.str, #NEW_ID );
-> end;
-> //
Query OK, 0 rows affected (0.07 sec)
mysql> delimiter ;
mysql>
mysql> insert into cmp_pk values();
Query OK, 1 row affected (0.06 sec)
mysql> select * from cmp_pk;
+------+----+
| str | id |
+------+----+
| TC_1 | 1 |
+------+----+
1 row in set (0.00 sec)
mysql>
mysql> insert into cmp_pk values();
Query OK, 1 row affected (0.06 sec)
mysql> select * from cmp_pk;
+------+----+
| str | id |
+------+----+
| TC_1 | 1 |
| TC_2 | 2 |
+------+----+
DELIMITER $$
CREATE TRIGGER bi_cmp_pk
BEFORE INSERT ON cmp_pk
FOR EACH ROW BEGIN
SET #NEW_ID := ( SELECT last_insert_id()+1 from cmp_pk limit 1 );
SET NEW.str = concat( NEW.str, #NEW_ID );
end;
$$ DELIMITER ;
this trigger should solve your problem
Actually i dont know what actually you want but may this code will help you
CREATE TABLE tbl_name (
col1 varchar(255) DEFAULT 'TC_',
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (col1,id));

datetime as unique key in mysql database

I have a datetime (name:lud) variable in my database table. It registers in this format 2011-04-11 07:08:02. Now i want to make an unique key in combination with an article_id: article_id + lud.
But for the unique key lud part only 2011-04-11 07:08 is needed.
eg lud 2011-04-11 07:08 + article_id 45
As a variant - you could change type of the lud field to VARCHAR and use length option of index.
For example:
CREATE TABLE mytable(
article_id INT(11) DEFAULT NULL,
lud VARCHAR(19) DEFAULT NULL,
UNIQUE INDEX UK_mytable (article_id, lud (16))
)
ENGINE = INNODB;
INSERT INTO mytable VALUES
(1, '2011-04-11 07:08:11');
Query OK, 1 row affected (0.01 sec)
INSERT INTO mytable VALUES
(1, '2011-04-11 07:09:11');
Query OK, 1 row affected (0.01 sec)
INSERT INTO mytable VALUES
(1, '2011-04-11 07:09:30');
ERROR 1062 (23000): Duplicate entry '1-2011-04-11 07:09' for key 'UK_mytable'
Try this
CONCAT(DATE_FORMAT(lud,'%Y-%m-%d %H:%i'),article_id)

How to check if INSERT went well in stored function?

I'm creating a stored function which should insert new row to table. In this table is also one unique column.
How can I check if everything goes well and row really was inserted?
How can I check exactly that it's this unique column found (for example - try to add duplicate value)?
You can check the LAST_INSERT_ID() function and INSERT IGNORE.
If the INSERT IGNORE was successful, you get the primary key returned. Let's create a table with an auto increment primary key and a unique key on a name.
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;
Here is the example being run:
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>
As shown in the preceding example, you can check the return value of the function. A nonzero return value means the INSERT IGNORE went well. A zero return value indicates a duplicate key without introducing an error number to the mysqld.
The drawback to this approach is that you cannot go back and use id 2 and 4 because of failed attempts to INSERT IGNORE in the event of a duplicate key.
Let's try another example with a different stored function setup using INSERT and without using 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;
Here is the result:
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 this example, the stored function returns 0 if the INSERT was OK, and returns 1 with a duplicate key on the name. The advantage? No wasted id numbers for auto_increment. The disadvantage? Doing a SELECT statement each time to check for the name already being present in the table.
You have a choice as to which way you want to handle duplicate keys. The first method lets mysqld handle the condition of the INSERT IGNORE. The second method has the stored function checking for the duplicate key first before the INSERT.
Stored Procedures are "all-or-nothing"
Therefore, if you include an INSERT in the sproc, and that INSERT fails on a duplicate key error, the entire sproc will be rolled back.
If the sproc executes without error, you can be confident that the INSERT did not have an error. Now, this does not mean the INSERT actually happens just because the sproc completes, just that there were no errors.... for example, if you had some WHERE clause which excludes the INSERT but doesn't throw an error then there may be some ambiguity.