MySQL: вставить запись, если не существует в таблице


384

Я пытаюсь выполнить следующий запрос:

INSERT INTO table_listnames (name, address, tele)
VALUES ('Rupert', 'Somewhere', '022')
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name='value'
);

Но это возвращает ошибку. По сути, я не хочу вставлять запись, если поле «имя» записи уже существует в другой записи - как проверить, является ли новое имя уникальным?



12
Все текущие ответы на это или дупс предполагают, что вы можете добавить уникальный индекс. Иногда решение основывается на бизнес-логике, которую нельзя наложить на всю таблицу. Например, вы можете разрешить несколько строк с определенным значением в столбце, но другое значение в столбце будет разрешено отображать только в одной строке. Как нам это сделать?
Оскар

Смотрите также: stackoverflow.com/q/1361340/4418
Уоррен

Ответы:


502

На самом деле я не предлагаю вам это делать, так как UNIQUEиндекс, предложенный Писквором и другими, - гораздо лучший способ сделать это, но вы можете фактически сделать то, что пытались:

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 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  |
+----+--------+-----------+------+

Попробуйте вставить ту же запись снова:

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 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  |
+----+--------+-----------+------+

И так далее...


Обновить:

Чтобы предотвратить #1060 - Duplicate column nameошибку, если два значения могут совпадать, вы должны назвать столбцы внутреннего 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  |
+----+---------+-----------+------+

18
Спасибо, что помогли. Моя настоящая проблема гораздо сложнее, и столбец не может быть уникальным, и я не могу зависеть от первичного ключа. Но это именно то, что я искал.
Руперт

2
@ Писковар: Согласен. @Rupert: вы должны индексировать столбец, на который ссылается внутренний оператор выбора ( nameв данном случае), если это вообще возможно. Обратите внимание, что вы можете сделать SELECT 'John', 'Doe', '022' FROM table_listnamesвместо SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp- но это будет работать, только если table_listnamesуже содержит одну или несколько строк. Я сомневаюсь, что скорость отличается, хотя, вероятно, это не проблема.
Майк

3
@VijayRamamurthy: это работает, потому что вы вставляете результат оператора Select. Внимательно прочитайте запрос -> WHEREЗаявление принадлежит SELECTзапросу. Запрос Select либо возвращает одну строку данных (данные вставлены), либо вообще не содержит данных (ничего не вставлено)
Филипп

13
Это не работает, если вы хотите вставить одно и то же значение дважды в разные поля (например, SELECT 'Humbert', 'Humbert', '1'во внутреннее выделение). Я получаюERROR 1060 (42S21): Duplicate column name
cburgmer

28
@cburgmer У меня такая же проблема #1060 - Duplicate column name. Вы нашли решение? Изменить: нашел его. Добавьте за каждым значением AS:SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp
Кай Ноак

292

INSERT не позволяет WHEREв синтаксисе .

Что вы можете сделать: создайте UNIQUE INDEXполе, которое должно быть уникальным ( name), затем используйте либо:

  • нормальный INSERT(и обработать ошибку, если имя уже существует)
  • INSERT IGNORE(который будет молча вызывать ошибку (вместо ошибки), если имя уже существует)
  • INSERT ... ON DUPLICATE KEY UPDATE(который будет выполнен UPDATEв конце, если имя уже существует, см. документацию )

1
если вам нужно получить предупреждающее сообщение, вы можете сделать это позжеmysql> show warnings\G
fiorentinoing

2
то есть, предупреждающее сообщение, в случае INSERT IGNORE, соответствует регулярному выражению^Duplicate entry '.+' for key '.+'$
user2426679

1
Если вы используете вставку игнорировать, будет ли она игнорировать другие ошибки, которые могут произойти не потому, что дублирует?
gepex

1
@gepex Да, будет. Это большая проблема с использованием INSERT IGNOREIMO.
шмосел

1
Примечание: этот ответ может быть не лучшим выбором, если один из столбцов в ограничении уникального ключа имеет значение NULL! Ограничение работает, но вы можете ожидать других результатов :) Вставка ("John Doe", NULL), ("John Doe", NULL) приводит к двум строкам, хотя оба поля вместе находятся в ограничении уникального ключа. Также см stackoverflow.com/questions/4081783/unique-key-with-nulls
Piemol

57

Работал :

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

Это работает только для баз данных Oracle, верно? en.wikipedia.org/wiki/DUAL_table Этот вопрос специально о MySQL!
Сильно нерегулярный

1
Я не тестирую в базе данных Oracle. На MySQL работает нормально и проверено.
Махбуб Тито

9
Я так узнал «двойной» доступен в MySQL , но не в MariaDB, с открытым исходным кодом филиала MySQL
искривленный

3
Из ссылки @ HighlyIrregular на википедию «Несколько других баз данных (включая Microsoft SQL Server, MySQL, PostgreSQL, SQLite и Teradata) позволяют полностью исключить предложение FROM, если таблица не нужна. Это исключает необходимость использования фиктивной таблицы». Похоже, что тот же запрос работает в MySql (5.7, если это имеет значение) без FROM DUAL.
Станний

1
Протестировано и отлично работает в MariaDB! Спасибо @MahbubTito
Джефф Монтейро

28

MySQL предлагает очень симпатичное решение:

REPLACE INTO `table` VALUES (5, 'John', 'Doe', SHA1('password'));

Очень прост в использовании, поскольку вы объявили уникальный первичный ключ (здесь со значением 5).


чтобы предотвратить это, вам нужно создать уникальный индекс, подробнее здесь: ссылка
Azmeer

Очень опасно. Не пытайтесь, если вы не знаете, что делаете. На самом деле, не пытайтесь вообще. Проверьте другие ответы.
Джурлад Эстрелла

3
Обратите внимание, что это УДАЛЯЕТ любые совпадающие строки, а затем повторно вставляет их, более безопасной версией этого же поведения является использование ON DUPLICTE KEY UPDATE, которое обновляет объединяющиеся строки вместо удаления и повторной вставки: dev.mysql.com/doc/ RefMan / 5.7 / о / replace.html
Omn

22
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;

Здесь запрос mysql, который вставляет записи, если они не существуют, и игнорирует существующие подобные записи.

----Untested----

4
у меня это не работает, INSERT IGNORE INTO emAssignedTagsForEvent SET eventId = '2', defaultTagId = '1';
Питу

8
Выглядит как смесь синтаксиса вставки и обновления. Вы имеете в виду INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);?
Hobo

@Hobo Из руководства MySQL dev.mysql.com/doc/refman/5.7/ru/insert.html Синтаксис INSERT: Это может быть INSERT [...] IGNORE INTO mytable VALUES ...илиINSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...
Ширкам,

Msgstr "Если вы используете модификатор IGNORE, ошибки, возникающие при выполнении оператора INSERT, игнорируются." В общем, вы ничего не решили.
Марсело Агимовель

18

Вы можете легко использовать следующий способ:

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

Таким образом, вы можете вставить любое новое сырье и, если у вас есть дублирующиеся данные, заменить определенный столбец (лучший столбец - метки времени).
Например :

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

13

Если вы действительно не можете получить уникальный индекс в таблице, вы можете попробовать ...

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;

12

Чтобы преодолеть подобную проблему, я изменил таблицу, чтобы иметь уникальный столбец. Используя ваш пример, при создании у меня было бы что-то вроде:

name VARCHAR(20),
UNIQUE (name)

и затем используйте следующий запрос при вставке в него:

INSERT IGNORE INTO train
set table_listnames='Rupert'

Я предпочитаю это решение.
Ильяс

9

Этот запрос работает хорошо:

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

И вы можете создать таблицу, используя следующий запрос:

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 ;

Примечание. Создайте таблицу, используя второй запрос, прежде чем пытаться использовать первый запрос.


хэш пароля md5 .... yikes!
Чад Грант

4

Брайан Хупер: Вы почти достигли цели, но у вас есть ошибка в вашем синтаксисе. Ваша вставка никогда не будет работать. Я проверил на моей базе данных и вот правильный ответ:

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'

Я даю вам мой пример таблицы. Вставка - почти то же самое, что написал Bian Hooper, за исключением того, что я поместил select FROM DUAL из другой таблицы. С наилучшими пожеланиями, Иван


2
Что это за "двойной"? Встроенное ключевое слово? Я просто не вижу разницы с тем, что сказал Брайан ... РЕДАКТИРОВАТЬ: Дальнейшее расследование получило смешанные и противоречивые (?) Результаты. В то время как на странице таблиц SQL DUAL говорится, что MySQL не поддерживает таблицы DUAL, в руководстве MySQL говорится, что это так. Мой собственный тест показывает, что это не так, поскольку он дает unknown table status: TABLE_TYPEсообщения, хотя запрос дал результат. Возможно, потому что MySQL не требует FROM DUALпредложения?
not2qubit


3

Вы вставляете не обновляя результат. Вы можете определить имя столбца в первичном столбце или установить его уникальность.


3

У меня возникла проблема, и метод, который Майк посоветовал, сработал частично, у меня была ошибка Dublicate Column name = '0', и я изменил синтаксис вашего запроса следующим образом`

     $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;";

Проблема была с именами столбцов. sum3 был равен sum4, а mysql выбрасывал дублирующие имена столбцов, и я написал код в этом синтаксисе, и он работал отлично,


1
Владельцем DUAL является SYS (SYS владеет словарем данных, поэтому DUAL является частью словаря данных.) Но доступ к DUAL может получить любой пользователь. В таблице есть один столбец VARCHAR2 (1) с именем DUMMY, который имеет значение «X». MySQL позволяет указывать DUAL в виде таблицы в запросах, которым не нужны данные из каких-либо таблиц. Таблица DUAL представляет собой специальную таблицу из одной строки и одной колонки, которая по умолчанию присутствует в Oracle и других установках базы данных. В Oracle таблица имеет один столбец VARCHAR2 (1) с именем DUMMY, который имеет значение «X». Это подходит для использования при выборе псевдостолбца, такого как SYSDATE или USER.
Аднан Хайдер

3

У меня была похожая проблема, и мне нужно было вставить несколько, если не существует. Итак, из приведенных выше примеров я пришел к этой комбинации ... она здесь на всякий случай, если кому-то понадобится.

Обратите внимание: мне приходилось определять имя везде, как того требовал MSSQL ... MySQL тоже работает с *.

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( OIDint (11) NOT NULL AUTO_INCREMENT, namevarchar (32) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY ( OID), UNIQUE KEY name_UNIQUE( name)) ENGINE = InnoDB AUTO_INCREMENT = 1;

или

MSSQL: СОЗДАТЬ ТАБЛИЦУ [names] ([OID] INT IDENTITY (1, 1) NOT NULL, [name] NVARCHAR (32) NOT NULL, первичный ключ кластеризован ([OID] ASC)); СОЗДАТЬ УНИКАЛЬНЫЙ НЕКЛАСТЕРНЫЙ ИНДЕКС [Index_Names_Name] ON [names] ([name] ASC);


2

Этот запрос может быть использован в коде PHP.

У меня есть столбец идентификатора в этой таблице, поэтому мне нужно проверить наличие дубликатов для всех столбцов, кроме этого столбца идентификатора:

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

и теперь новый элемент будет добавлен только в том случае, если не существует строки со значениями, заданными в SETстроке

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.