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
— warren

Відповіді:


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
@Piskovar: Погоджено. @Rupert: слід індексувати стовпчик, про який йдеться у внутрішньому операторі select ( nameу цьому випадку), якщо це взагалі можливо. Зауважте також, що ви можете робити це SELECT 'John', 'Doe', '022' FROM table_listnamesзамість SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp- але це буде працювати лише тоді, коли воно table_listnamesмістить один або кілька рядків. Я сумніваюся, що швидкість відрізняється, тому, мабуть, це не турбує.
— Майк

3
@VijayRamamurthy: Це працює, тому що ви вставляєте результат оператора Select. Прочитайте запит уважно -> WHEREЗаява належить до SELECTзапиту. Запит Select або повертає один рядок даних (дані вставлені), або взагалі відсутні дані (нічого не вставлено)
— Philipp

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
— Kai Noack

292

INSERT не дозволяє WHEREв синтаксисі .

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

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

1
якщо вам потрібно отримати попереджувальне повідомлення, яке ви зможете зробити після цьогоmysql> show warnings\G
— fiorentinoing

2
ime, попереджувальне повідомлення, у випадку з INSERT IGNORE, відповідає ^Duplicate entry '.+' for key '.+'$
— регексу

1
якщо ви використовуєте вставну ігнору, чи буде вона ігнорувати й інші помилки, які можуть статися не тому, що копії?
— gepex

1
@gepex Так, так і буде. Це велика проблема використання INSERT IGNOREІМО.
— шмосель

1
Примітка: ця відповідь може бути не найкращим вибором, якщо один із стовпців унікального обмеження ключа є нульовим! Обмеження працює, але ви можете очікувати інших результатів :) Вставлення ("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
— High Irregular

3
З посилання вікіпедії @ HighlyIrregular "Кілька інших баз даних (включаючи Microsoft SQL Server, MySQL, PostgreSQL, SQLite і Teradata) дозволяють повністю опустити пункт FROM, якщо не потрібна таблиця. Це дозволяє уникнути потреби в будь-якій макетній таблиці". Цей же запит, здається, працює в MySql (5.7, якщо це має значення) без ДУАЛЬНОГО.
— stannius

1
Випробуваний і працює добре в MariaDB! Дякую @MahbubTito
— Джефф Монтейро,

27

MySQL пропонує дуже миле рішення:

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

Дуже простий у використанні, оскільки ви оголосили унікальний первинний ключ (тут зі значенням 5).


Щоб запобігти цьому, вам потрібно створити унікальний індекс, більше інформації тут: посилання
— Azmeer

Дуже небезпечно. Не робіть спроб, якщо ви не знаєте, що робите. Насправді, не намагайтеся взагалі. Перевірте інші відповіді.
— Jhourlad Estrella

3
Зауважте, що ЦЕ ЗАБЕЗПЕЧУЄ будь-які відповідні рядки, а потім повторно вставляє їх, більш безпечною версією такої ж поведінки є використання ON DUPLICTE KEY UPDATE, яке оновлює матч-рядки, а не видаляє та повторно вставляє їх: dev.mysql.com/doc/ refman / 5.7 / en /
— substitu.html

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

Тут запит mysql, який вставляє записи, якщо їх немає, і ігнорує існуючі подібні записи.

----Untested----

4
це не працює для мене, ВСТАВИТИ IGNORE INTO emAssignedTagsForEvent SET eventId = '2', defaultTagId = '1';
— піту

8
Схоже, це суміш вставлених та оновлених синтаксисів. Ви маєте на увазі INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);?
— Хобо

@Hobo Із посібника з MySQL dev.mysql.com/doc/refman/5.7/uk/insert.html ВСТАВИТИ синтаксис: Це може бути INSERT [...] IGNORE INTO mytable VALUES ...абоINSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...
— Ширкам

"Якщо ви використовуєте модифікатор 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 .... вдається!
— Чад Грант

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'

Я наводжу вам свій приклад y таблиці. Вставка майже подібна до того, як писав Біан Хупер, за винятком того, що я поставив виділення ВІД ДУАЛЬНОГО ont з іншої таблиці. З повагою, Іване


2
Що це за «подвійний»? Вбудоване ключове слово? Я просто не бачу різниці від того, що сказав Брайан ... EDIT: Подальше розслідування отримало неоднозначні та суперечливі (?) Результати. Хоча на сторінці таблиці SQL DUAL йдеться про те, що MySQL не підтримує таблиці DUAL, у посібнику MySQL йдеться про це. Мій власний тест показує, що цього немає, оскільки він дає unknown table status: TABLE_TYPEповідомлення, хоча запит дав результат. Можливо, тому, що MySQL не вимагає цього FROM DUALпункту?
— not2qubit


3

Ви вставляєте не Оновлення результату. Ви можете визначити стовпчик імен у первинному стовпчику або встановити його унікальним.


3

У мене виникла проблема, і метод, який радив Майк, працював частково, у мене з’явилася помилка дублікату стовпця назва = '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: СТВОРИТИ ТАБЛИЦЮ [імена] ([OID] INT IDENTITY (1, 1) NOT NULL, [name] NVARCHAR (32) NOT NULL, PRIMARY KEY CLUSTERED ([OID] ASC)); СТВОРИТИ УНІКАЛЬНИЙ НЕКЛЮСТОВАНИЙ ІНДЕКС [Index_Names_Name] ON [імена] ([ім'я] 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.