Inserción condicional de MySQL

Estoy teniendo dificultades para formar un INSERT condicionado

Tengo x_table con columnas (instancia, usuario, elemento) donde el ID de instancia es único. Quiero insertar una nueva fila solo si el usuario ya no tiene un elemento determinado.

Por ejemplo, tratando de insertar instancia = 919191 usuario = 123 elemento = 456

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

Cualquier ayuda u orientación en la dirección correcta sería muy apreciada.

Si su DBMS no impone limitaciones a la tabla que selecciona cuando ejecuta una inserción, intente:

 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) 

En esto, dual es una tabla con una sola fila (se encuentra originalmente en Oracle, ahora también en otros lugares). La lógica es que la instrucción SELECT genera una única fila de datos con los valores requeridos, pero solo cuando los valores no se encuentran ya.

Alternativamente, mira la statement MERGE.

También puede usar INSERT IGNORE que ignora silenciosamente la inserción en lugar de actualizar o insertar una fila cuando tiene un índice único en (usuario, elemento).

La consulta se verá así:

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

Puede agregar el índice exclusivo con CREATE UNIQUE INDEX user_item ON x_table (user, item) .

¿Alguna vez has intentado algo así?

 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; 

Con UNIQUE(user, item) , haz:

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

el user=123 bit es un “no-op” para hacer coincidir la syntax de la cláusula ON DUPLICATE sin hacer realmente nada cuando hay duplicados.

En caso de que no desee establecer una restricción única, esto funciona como un encanto:

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

Espero eso ayude !

Si agrega una restricción que (x_table.user, x_table.item) es única, la inserción de otra fila con el mismo usuario y elemento fallará.

p.ej:

 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 

Aunque es bueno verificar la duplicación antes de insertar sus datos, le sugiero que ponga una restricción / índice único en sus columnas para que no se puedan insertar datos duplicados por error.

Lo que quieres es INSERT INTO table (...) SELECT ... WHERE ... del manual de MySQL 5.6 .

En tu caso es:

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

O tal vez ya que no está utilizando ninguna lógica complicada para determinar si ejecutar el INSERT o no, puede simplemente establecer una clave UNIQUE en la combinación de estas dos columnas y luego usar INSERT IGNORE .

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

La syntax puede variar dependiendo de tu DB …

Leve modificación a la respuesta de Alex, también podría simplemente hacer referencia al valor de la columna existente:

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

Entonces este representa 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 

Puede usar la siguiente solución para resolver su problema:

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