Условная вставка MySQL

99

Мне сложно сформировать условный INSERT

У меня есть x_table со столбцами (экземпляр, пользователь, элемент), где идентификатор экземпляра уникален. Я хочу вставить новую строку только в том случае, если у пользователя уже нет данного элемента.

Например, при попытке вставить instance = 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 

Любая помощь или руководство в правильном направлении будут очень благодарны.

Неизвестный
источник

Ответы:

116

Если ваша СУБД не накладывает ограничений на то, из какой таблицы вы выбираете при выполнении вставки, попробуйте:

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 вам действительно не нужно иметь таблицу с именем 'dual', чтобы существовать: это специальное имя таблицы, которое можно использовать для выбора из нее чего угодно.
Кристофер Шульц
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).

MrD
источник
2
Согласно документации MySQL INSERT IGNORE является эквивалентом REPLACE ... 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 с первой попытки, где принятый ответ мне не показался.
mightypile 04
10

С помощью a 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
Я не могу настроить уникальный (пользователь, элемент), потому что могут быть случаи, когда несколько экземпляров с одним и тем же пользователем и элементом ... только не тогда, когда я делаю эту вставку.
Неизвестный
3

Если вы не хотите устанавливать уникальное ограничение, это работает как шарм:

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

Надеюсь, поможет !

Gew
источник
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
источник
Это не правильно. Может быть несколько строк с одним и тем же пользователем, но не несколько строк с парой пользователь-элемент.
Мэтью Флашен,
Ага-ага. Я неправильно это понял. Отредактировано, чтобы установить ограничение на пару.
NickZoic
Хороший комментарий ... я запускаю запрос в PHP с помощью mysql_query ("INSERT") и оставляю часть or die, поэтому он не предупреждает меня об ошибке, поэтому мне не нужно делать никаких проверок
Angel.King.47
1

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

Битлз1692
источник
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
xsubira
источник
-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?
Умайр А.