Умовна вставка MySQL


98

Мені важко формувати умовний ВСТУП

У мене є x_table із стовпцями (екземпляр, користувач, елемент), де ідентифікатор екземпляра унікальний. Я хочу вставити новий рядок лише в тому випадку, якщо користувач вже не має заданого елемента.

Наприклад, намагаючись вставити екземпляр = 919191 user = 123 item = 456

Insert into x_table (instance, user, item) values (919191, 123, 456) 
    ONLY IF there are no rows where user=123 and item=456 

Будь-яка допомога чи вказівки в правильному напрямку були б вдячні.

Відповіді:


115

Якщо ваша СУБД не накладає обмежень на таблицю, яку ви вибираєте під час виконання вставки, спробуйте:

INSERT INTO x_table(instance, user, item) 
    SELECT 919191, 123, 456
        FROM dual
        WHERE NOT EXISTS (SELECT * FROM x_table
                             WHERE user = 123 
                               AND item = 456)

У цьому, dual таблиці лише один рядок (знайдений спочатку в Oracle, зараз також у mysql). Логіка полягає в тому, що оператор SELECT генерує єдиний ряд даних з необхідними значеннями, але лише тоді, коли значення вже не знайдені.

Як варіант, подивіться на заяву MERGE.


1
Чи dualпотрібно в цьому випадку створювати перед цим чи це якась спеціальна "тимчасова" таблиця, створена запитом для використання запиту поодинці?
itsmequinn

8
Якщо у вас цього ще немає dual, вам потрібно створити його. CREATE TABLE dual ( dummy CHAR(1) DEFAULT 'x' NOT NULL CHECK (dummy = 'x') PRIMARY KEY ); INSERT INTO dual VALUES('x'); REVOKE ALL ON dual FROM PUBLIC; GRANT SELECT ON dual TO PUBLIC;
Джонатан Леффлер

Дякую, що дивовижно знати
itsmequinn

11
Зверніть увагу, що в MySQL вам не потрібно існувати таблиці під назвою "подвійний", щоб існувати: це спеціальне ім'я таблиці, яке можна використовувати для вибору з нього будь-чого.
Крістофер Шульц

3
MySQL «поважає» поняття «подвійний», але насправді воно не існує. Наприклад, якщо ти SELECT COUNT(*) FROM dualзавжди отримуєш 1, і якщо SELECT 'foo' FROM dualти завжди отримуєш foo. Але ти не можеш SELECT * FROM dualі не можеш DESCRIBE dualчи щось подібне. Я не перевіряв, але також не думаю, що ви також можете відкликати дозволи dual. Тож варто зазначити, що він працює так, як ви очікували ... за винятком випадків, коли цього немає;)
Крістофер Шульц,

52

Ви також можете використовувати те, INSERT IGNOREщо мовчки ігнорує вставку замість того, щоб оновлювати чи вставляти рядок, коли у вас є унікальний індекс (користувач, елемент).

Запит буде виглядати приблизно так:

INSERT IGNORE INTO x_table(instance, user, item) VALUES (919191, 123, 456)

Ви можете додати унікальний індекс за допомогою CREATE UNIQUE INDEX user_item ON x_table (user, item).


2
За документацією на MySQL INSERT IGNORE є аналогом ЗАМІНИ ... INSERT IGNORE: зберігати старі рядки. ЗАМІНА: зберігайте нові рядки. Обидва працюють на унікальних клавішах.
Пол Кеньора

Найкраща відповідь колись
jmojico

30

Ви коли-небудь пробували щось подібне?

INSERT INTO x_table
SELECT 919191 as instance, 123 as user, 456 as item
FROM x_table
WHERE (user=123 and item=456)
HAVING COUNT(*) = 0;

о, так ! розумне та приємне рішення із лише одним твердженням SELECT
java acm

Приємно. Це працює для postgresql з першої спроби, де прийнята відповідь мені не здавалася.
могучий

10

З а UNIQUE(user, item), зробіть:

Insert into x_table (instance, user, item) values (919191, 123, 456) 
  ON DUPLICATE KEY UPDATE user=123

user=123біт не є "не-оп» , щоб відповідати синтаксису ON DUPLICATEпункту фактично не робити нічого , коли є дублікати.


1
Я не можу налаштувати унікальний (користувач, елемент), оскільки можуть бути випадки, коли кілька екземплярів з одним і тим же користувачем і елементом ... просто не тоді, коли я роблю цю вставку.
Невідомий

2

Якщо ви не хочете встановлювати унікальне обмеження, це працює як принадність:

INSERT INTO `table` (`column1`, `column2`) SELECT 'value1', 'value2' FROM `table` WHERE `column1` = 'value1' AND `column2` = 'value2' HAVING COUNT(`column1`) = 0

Сподіваюся, це допомагає!


2

Те, що ти хочеш, так і є INSERT INTO table (...) SELECT ... WHERE .... з посібника MySQL 5.6 .

У вашому випадку це:

INSERT INTO x_table (instance, user, item) SELECT 919191, 123, 456 
WHERE (SELECT COUNT(*) FROM x_table WHERE user=123 AND item=456) = 0

А може, оскільки ви не використовуєте жодної складної логіки, щоб визначити, запускати INSERTчи ні, ви можете просто встановити UNIQUEключ на комбінації цих двох стовпців, а потім використовувати INSERT IGNORE.


Ви запустили свою відповідь? Я продовжую отримувати ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where...(працює 5.6.34)
hlin117

Я думаю, що вам потрібно сказати from dualраніше where.
hlin117

@ hlin117 У MariaDB FROM DUALза замовчуванням, якщо не було посилань на таблиці ( див. цю документацію ).
Єті

1

Якщо ви додасте обмеження, яке (x_table.user, x_table.item) є унікальним, то вставити іншу рядок з тим самим користувачем і елемент не вдасться.

наприклад:

mysql> create table x_table ( instance integer primary key auto_increment, user integer, item integer, unique (user, item));
Query OK, 0 rows affected (0.00 sec)

mysql> insert into x_table (user, item) values (1,2),(3,4);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into x_table (user, item) values (1,6);
Query OK, 1 row affected (0.00 sec)

mysql> insert into x_table (user, item) values (1,2);
ERROR 1062 (23000): Duplicate entry '1-2' for key 2

Це не правильно. З одним і тим же користувачем може бути кілька рядків, але не кілька рядків з парою елементів-елементів.
Меттью Флашен

Так Так. Я неправильно його прочитав. Відредаговано, щоб зробити обмеження для пари.
NickZoic

Приємний коментар ... Я запускаю запит у PHP за допомогою mysql_query ("ВСТАВИТЬ") і залишаю або вмираю частину, тому він дозує попереджати мене про помилку, тому мені не потрібно робити ніяких перевірок
Angel.King.47

1

Хоча перед вставкою даних добре перевірити їх дублювання, я пропоную вам поставити унікальне обмеження / покажчик на стовпці, щоб не вдалося вставити повторювані дані помилково.


1

Ви можете використовувати таке рішення для вирішення своєї проблеми:

INSERT INTO x_table(instance, user, item) 
    SELECT 919191, 123, 456
        FROM dual
        WHERE 123 NOT IN (SELECT user FROM x_table)

Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, як та / або чому він вирішує проблему, покращить довгострокове значення відповіді.
Nic3500

0

Трохи змінивши відповідь Алекса, ви також можете просто посилатися на існуюче значення стовпця:

Insert into x_table (instance, user, item) values (919191, 123, 456) 
  ON DUPLICATE KEY UPDATE user=user

0

Таким чином, це означає PostgreSQL

INSERT INTO x_table
SELECT NewRow.*
FROM (SELECT 919191 as instance, 123 as user, 456 as item) AS NewRow
LEFT JOIN x_table
ON x_table.user = NewRow.user AND x_table.item = NewRow.item
WHERE x_table.instance IS NULL

-1
Insert into x_table (instance, user, item) values (919191, 123, 456) 
    where ((select count(*) from x_table where user=123 and item=456) = 0);

Синтаксис може відрізнятися залежно від вашої БД ...


2
Для чого це? MySQL?
Умаїр А.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.