413

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?

Piskvor left the building
  • 87,797
  • 43
  • 170
  • 220
Rupert
  • 4,183
  • 3
  • 14
  • 6
  • 12
    All of the current answers to this or dupes assume that you can add a unique index. Sometimes the decision is based on business logic that can't be imposed on the whole table. For example, you allow multiple rows with a certain value in a column, but another value in the column will only be allowed to appear on one row. How do we accomplish that? – Oscar May 02 '14 at 23:37

16 Answers16

538

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  |
+----+---------+-----------+------+
SherylHohman
  • 12,507
  • 16
  • 70
  • 78
Mike
  • 20,127
  • 2
  • 38
  • 65
  • 19
    Thanks that helped. My actual problem is far more complex and the column just cannot be unique and I cannot depend on the primary key. But this is exactly what I was looking for. – Rupert Jul 02 '10 at 10:41
  • This would work, but I'm a bit worried about performance - unless there's an index on `name`, the innermost select will be horribly slow. If there is an index, then this should work OK. – Piskvor left the building Jul 02 '10 at 10:46
  • 2
    @Piskovar: Agreed. @Rupert: you should index the column referred to in the inner select statement (`name`, in this case), if at all possible. Note also that you can do `SELECT 'John', 'Doe', '022' FROM table_listnames`, instead of `SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp` - but that will only work if `table_listnames` already contains one or more rows. I doubt the speed is any different though, so it's probably not a concern. – Mike Jul 02 '10 at 10:54
  • @Mike, if we want to add a row with same name means, will it work? – Dhasneem May 13 '13 at 07:23
  • I thought `INSERT` queries can't take `WHERE` clauses? Can someone please explain to me why this works despite @Piskvor saying it shouldn't in his answer? – vijrox Jul 03 '13 at 21:20
  • 3
    @VijayRamamurthy: This works because you Insert the result of an Select statement. Read the query carefully -> The `WHERE` statement belongs to the `SELECT` query. The Select query either returns a single data-row (data is inserted) or no data at all (nothing is inserted) – Philipp Sep 27 '13 at 14:42
  • 14
    This does not seem to work if you want to insert the same value twice into different fields (e.g. `SELECT 'Humbert', 'Humbert', '1'` in the inner select). I receive a `ERROR 1060 (42S21): Duplicate column name` – cburgmer Oct 10 '13 at 12:25
  • 1
    @cburgmer I think you can use `INSERT IGNORE` or `REPLACE` or `INSERT ... ON DUPLICATE KEY UPDATE` for that – Timo Huovinen Dec 18 '13 at 15:10
  • 29
    @cburgmer I got the same problem `#1060 - Duplicate column name`. Have you found a solution? Edit: Found it. Add behind each value an AS: `SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp` – Avatar Feb 17 '14 at 15:14
  • @Mike can u explain your answer its really helpful for me i am not getting why u write tmp here – Ashish Chaturvedi May 31 '14 at 09:20
  • 1
    @user2223410: Because the derived table (subquery) needs a name (also called alias). `(SELECT 'Rupert', 'Somewhere', '022')` is the subquery, and append `AS tmp` (or just `tmp`) gives it that name. See http://stackoverflow.com/q/1888779/367456 – hakre Sep 28 '14 at 10:11
  • 1
    I'm using this as I need to insert where it does not exist in a different table. And it needs to not be subject to race conditions. This is perfect, thanks. – Luke Cousins Nov 30 '14 at 13:23
  • does this can work on concurrency situation ? tow session execute the same statement as you supplied ? – Chinaxing Nov 10 '15 at 09:45
  • Hi Mike your code helped me to solve a problem, but I added something to for better working, you can see it below – Hovhannes Babayan Dec 15 '15 at 14:54
  • Can I use the same syntax to return a message that says: "A record Exists"? I usually use a procedure to do that. – housamz Jan 04 '16 at 14:13
  • 1
    excellent answer. Now I have another question: is there any way to know if the register got inserted? – Allfarid Morales García Oct 11 '16 at 21:48
  • Can u explain the query ?? plzz @Mike – Tushar Rmesh Saindane Jun 26 '17 at 06:10
  • @Mike I want to use your solution to insert multiple rows _(array)_ of data using `PDO` but `$query->execute();` always returns **TRUE** which does make sense but I'd like to know which index of array didn't insert. Any ideas – Daniyal Nasir Jan 30 '18 at 13:35
  • 1
    Your suggestion is better than `INSERT IGNORE` with a unique key. Ignoring errors can lead to missed failures for reasons other than the unique key constraint. For example, if someone adds a new column and then your stored proc runs with an insert ignore. – Don Rhummy Jun 12 '18 at 00:20
  • @DaniyalNasir that's a great question. You may want to create a new SO question referencing this solution. – HPWD Aug 07 '18 at 20:03
  • You can refer to the table alias (tmp) in the WHERE clause like this: WHERE NOT EXISTS ( SELECT name FROM table_listnames WHERE name = tmp.name ) – boes Feb 12 '19 at 11:56
  • If you do not have a UNIQUE constraint, I believe that this approach could still lead to duplicate entries in the rare case of a race condition: a duplicate entry could have been inserted in a parallel query between your SELECT subquery and your INSERT query. Though, I haven't tested and I'd love to be proven wrong. – Boris Dalstein Feb 27 '19 at 14:33
  • This solution creates errors when two of the values you try to insert are the same such as SELECT '3', '1', '1'. To avoid this, give each value an alias such as SELECT '3' AS A, '1' AS B, '1' AS C. – Nosajimiki Mar 11 '19 at 20:56
  • What if we have two columns with the same values, eg SELECT * FROM (SELECT 'John', 'Doe', '022','022') ? – Rinto George Jun 03 '19 at 08:35
  • Any help if the two or more column having same value as i'm getting duplicate column error – dev Mar 11 '20 at 04:33
  • @MBG it was mentioned several times above: you must name the columns `SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp` – Radek Pech Mar 12 '20 at 08:23
  • I am combining this current method to insert new data coming in from parsed streamed data using: https://stackoverflow.com/questions/40535757/download-xlsx-from-s3-and-parse-it by David but my SQL uploads new info plus old one still. Is there a way to prevent this? – dhahn Apr 10 '20 at 04:28
  • You saved my day – Eray Alparslan Nov 02 '20 at 12:41
  • @Mike special thanks for mentioning how to handle duplicate column name – Gaurav Kansal May 07 '21 at 12:06
301

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)
Piskvor left the building
  • 87,797
  • 43
  • 170
  • 220
  • INSERT IGNORE worked for me. It is best option, esp. for large data set. – Mugoma J. Okomba Sep 19 '16 at 21:02
  • 1
    if you need to get warning message you can afterwards do `mysql> show warnings\G` – fiorentinoing Oct 20 '16 at 18:59
  • 2
    ime, the warning message, in the case of `INSERT IGNORE`, matches the regex of `^Duplicate entry '.+' for key '.+'$` – user2426679 Nov 15 '17 at 23:10
  • 1
    if you use insert ignore, will it ignore other errors that may occur that are not because duplicates? – gepex May 09 '18 at 20:28
  • @gepex: I don't know off the top of my head - but this should not affect *unrelated* errors. – Piskvor left the building May 09 '18 at 20:30
  • 1
    @gepex Yes, it will. That's a big problem with using `INSERT IGNORE` IMO. – shmosel May 31 '18 at 19:52
  • 1
    Note: this answer might not be the best choice if one of the columns in the unique key constraint is nullable! The constraint works, but you might expect other results :) Inserting ("John Doe", NULL), ("John Doe", NULL) results in two rows although both fields together are in a unique key constraint. Also see https://stackoverflow.com/questions/4081783/unique-key-with-nulls – Piemol Jan 17 '19 at 14:36
68

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');
Mahbub Tito
  • 921
  • 7
  • 7
33

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).

sdesvergez
  • 757
  • 7
  • 3
  • to prevent it you need to create a unique index, more info here:[link](http://stackoverflow.com/questions/5835978/how-to-properly-create-composite-primary-keys-mysql) – Azmeer Nov 05 '15 at 05:34
  • Very dangerous. Do not attempt unless you know what you are doing. Actually, do not attempt at all. Check other answers. – Jhourlad Estrella Jun 21 '19 at 10:27
  • 4
    Note, this DELETES any matching rows and then re-inserts them, a safer version of this same behavior is to use ON DUPLICTE KEY UPDATE, which updates mathcing rows rather than deleting and re-inserting them: https://dev.mysql.com/doc/refman/5.7/en/replace.html – Omn Jul 13 '19 at 22:31
26
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----
Montaser El-sawy
  • 620
  • 1
  • 7
  • 13
  • 4
    it doesnt worked for me, INSERT IGNORE INTO emAssignedTagsForEvent SET eventId='2',defaultTagId='1'; – pitu Jun 07 '14 at 05:29
  • 8
    Looks like a mixture of insert and update syntaxes. Do you mean ``INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);``? – Hobo Oct 22 '14 at 12:33
  • @Hobo From MySQL Manual https://dev.mysql.com/doc/refman/5.7/en/insert.html INSERT syntax: It can be `INSERT [...] IGNORE INTO mytable VALUES ...` Or `INSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...` – Shirkam Jul 26 '17 at 06:43
  • "If you use the IGNORE modifier, errors that occur while executing the INSERT statement are ignored.". So basically you didn't solved anything. – Marcelo Agimóvel Dec 26 '17 at 12:25
21

You can easily use the following way :

INSERT INTO ... ON DUPLICATE KEY UPDATE ...

By this way you can insert any new raw and if you have duplicate data, replace specific column ( best columns is 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';
Arash Hatami
  • 4,313
  • 4
  • 31
  • 50
14

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'
Mohit Gupta
  • 33
  • 1
  • 5
obsessiveCookie
  • 1,040
  • 1
  • 16
  • 31
13

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;
Brian Hooper
  • 20,414
  • 23
  • 82
  • 132
11

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.

dakab
  • 4,576
  • 8
  • 38
  • 56
4

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

Sgoettschkes
  • 12,643
  • 4
  • 55
  • 72
  • 2
    What is that "dual"? A built in keyword? I just don't see the difference from what Brian said... EDIT: Further investigation got mixed and contradicting (?) results. While [SQL DUAL table](http://www.w3resource.com/sql/sql-dual-table.php) page says that MySQL does not support DUAL tables, the [MySQL manual](http://dev.mysql.com/doc/refman/5.0/en/select.html) says that it does. My own test show that it doesn't, since it gives `unknown table status: TABLE_TYPE` messages, although the query yielded a result. Probably because MySQL doesn't require the `FROM DUAL` clause? – not2qubit Oct 29 '13 at 18:55
4
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 )
Greg the Incredulous
  • 1,370
  • 4
  • 23
  • 32
Oms G
  • 41
  • 1
3

You are inserting not Updating the result. You can define the name column in primary column or set it is unique.

User 1034
  • 8,145
  • 25
  • 67
  • 88
3

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,

  • 1
    The owner of DUAL is SYS (SYS owns the data dictionary, therefore DUAL is part of the data dictionary.) but DUAL can be accessed by any user. The table has a single VARCHAR2(1) column called DUMMY that has a value of 'X'. MySQL allows DUAL to be specified as a table in queries that do not need data from any tables.The DUAL table is a special one-row, one-column table present by default in Oracle and other database installations. In Oracle, the table has a single VARCHAR2(1) column called DUMMY that has a value of 'X'. It is suitable for use in selecting a pseudo column such as SYSDATE or USER. – Adnan haider Feb 08 '19 at 06:02
3

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);

Waldemar
  • 143
  • 7
2

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

Andrew
  • 5,048
  • 4
  • 35
  • 58
1

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

quicktrick
  • 68
  • 6