MySQL: Insertar registro si no existe en la tabla

384

Estoy tratando de ejecutar la siguiente consulta:

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

Pero esto devuelve un error. Básicamente no quiero insertar un registro si el campo 'nombre' del registro ya existe en otro registro, ¿cómo verificar si el nuevo nombre es único?

Rupert
fuente
55
posible duplicado de Cómo 'insertar si no existe' en MySQL?
warren
12
Todas las respuestas actuales a esto o engaños suponen que puede agregar un índice único. A veces, la decisión se basa en una lógica empresarial que no se puede imponer en toda la mesa. Por ejemplo, permite múltiples filas con un cierto valor en una columna, pero otro valor en la columna solo se permitirá que aparezca en una fila. ¿Cómo logramos eso?
Oscar
ver también: stackoverflow.com/q/1361340/4418
warren

Respuestas:

502

No estoy sugiriendo que hagas esto, ya que el UNIQUEíndice sugerido por Piskvor y otros es una forma mucho mejor de hacerlo, pero puedes hacer lo que estabas intentando:

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;

Insertar un registro:

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

Intente insertar el mismo registro nuevamente:

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

Insertar un registro diferente:

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

Y así...


Actualizar:

Para evitar #1060 - Duplicate column nameerrores en caso de que dos valores sean iguales, debe nombrar las columnas del SELECT interno:

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  |
+----+---------+-----------+------+
Miguel
fuente
18
Gracias, eso ayudó. Mi problema real es mucho más complejo y la columna simplemente no puede ser única y no puedo depender de la clave principal. Pero esto es exactamente lo que estaba buscando.
Rupert
2
@Piskovar: De acuerdo. @Rupert: debe indexar la columna a la que se hace referencia en la instrucción de selección interna ( name, en este caso), si es posible. Tenga en cuenta también que puede hacerlo SELECT 'John', 'Doe', '022' FROM table_listnames, en lugar de SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp, pero eso solo funcionará si table_listnamesya contiene una o más filas. Sin embargo, dudo que la velocidad sea diferente, por lo que probablemente no sea una preocupación.
Mike
3
@VijayRamamurthy: esto funciona porque inserta el resultado de una instrucción Select. Lea la consulta detenidamente -> La WHEREdeclaración pertenece a la SELECTconsulta. La consulta Seleccionar devuelve una sola fila de datos (se insertan datos) o no hay datos (no se inserta nada)
Philipp
13
Esto no parece funcionar si desea insertar el mismo valor dos veces en diferentes campos (por ejemplo, SELECT 'Humbert', 'Humbert', '1'en la selección interna). ReciboERROR 1060 (42S21): Duplicate column name
cburgmer
28
@cburgmer Tengo el mismo problema #1060 - Duplicate column name. ¿Has encontrado una solución? Editar: lo encontré. Agregue detrás de cada valor un AS:SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp
Kai Noack
292

INSERT no permite WHEREen la sintaxis .

Lo que puede hacer: cree un UNIQUE INDEXcampo que debe ser único ( name), luego use:

  • normal INSERT(y maneje el error si el nombre ya existe)
  • INSERT IGNORE(que fallará silenciosamente causará una advertencia (en lugar de error) si el nombre ya existe)
  • INSERT ... ON DUPLICATE KEY UPDATE(que ejecutará UPDATEal final si el nombre ya existe, consulte la documentación )
Piskvor salió del edificio
fuente
1
si necesita recibir un mensaje de advertencia, puede hacerlo más tardemysql> show warnings\G
fiorentinoing
2
es decir, el mensaje de advertencia, en el caso de INSERT IGNORE, coincide con la expresión regular de^Duplicate entry '.+' for key '.+'$
user2426679
1
si usa insert ignore, ¿ignorará otros errores que puedan ocurrir que no sean duplicados?
gepex
1
@gepex Sí, lo hará. Ese es un gran problema con el uso de INSERT IGNOREIMO.
shmosel
1
Nota: ¡esta respuesta podría no ser la mejor opción si una de las columnas en la restricción de clave única es anulable! La restricción funciona, pero puede esperar otros resultados :) Insertar ("John Doe", NULL), ("John Doe", NULL) da como resultado dos filas, aunque ambos campos juntos están en una restricción de clave única. Ver también stackoverflow.com/questions/4081783/unique-key-with-nulls
Piemol
57

Trabajó :

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');
Mahbub Tito
fuente
Esto solo funciona para bases de datos Oracle, ¿verdad? en.wikipedia.org/wiki/DUAL_table ¡ Esta pregunta es específicamente sobre MySQL!
Muy irregular
1
No pruebo en la base de datos Oracle. Está funcionando bien en MySQL y probado.
Mahbub Tito
99
Desde entonces supe que "dual" está disponible en MySQL pero no en MariaDB, la rama de código abierto de MySQL
Muy irregular
3
Desde el enlace de Wikipedia de @ HighlyIrregular "Varias otras bases de datos (incluidos Microsoft SQL Server, MySQL, PostgreSQL, SQLite y Teradata) permiten omitir la cláusula FROM por completo si no se necesita una tabla. Esto evita la necesidad de una tabla ficticia". La misma consulta parece funcionar en MySql (5.7, si eso importa) sin FROM DUAL.
stannius
1
Probado y funcionando bien en MariaDB! Gracias @MahbubTito
Jeff Monteiro
27

MySQL proporciona una solución muy linda:

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

Muy fácil de usar ya que ha declarado una clave primaria única (aquí con valor 5).

sdesvergez
fuente
para evitarlo, debe crear un índice único, más información aquí: enlace
Azmeer
Muy peligroso. No intente a menos que sepa lo que está haciendo. En realidad, no intentes nada. Verifica otras respuestas.
Jhourlad Estrella
3
Tenga en cuenta que esto BORRA las filas coincidentes y luego las vuelve a insertar, una versión más segura de este mismo comportamiento es usar ON DUPLICTE KEY UPDATE, que actualiza las filas de cálculo en lugar de eliminarlas y volver a insertarlas: dev.mysql.com/doc/ refman / 5.7 / es / replace.html
Omn
22
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;

Aquí la consulta mysql, que inserta registros si no existen e ignorará los registros similares existentes.

----Untested----
Montaser El-sawy
fuente
44
no funcionó para mí, INSERTE IGNORE EN emAssignedTagsForEvent SET eventId = '2', defaultTagId = '1';
pitu
8
Parece una mezcla de sintaxis de inserción y actualización. Qué quiere decir INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);?
Hobo
@Hobo Del manual de MySQL dev.mysql.com/doc/refman/5.7/en/insert.html INSERTAR sintaxis: Puede ser INSERT [...] IGNORE INTO mytable VALUES ...OINSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...
Shirkam
"Si utiliza el modificador IGNORE, se ignoran los errores que se producen al ejecutar la instrucción INSERT". Así que básicamente no resolviste nada.
Marcelo Agimóvel
18

Puede usar fácilmente la siguiente manera:

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

De esta manera, puede insertar cualquier nueva materia prima y, si tiene datos duplicados, reemplace la columna específica (las mejores columnas son las marcas de tiempo).
Por ejemplo :

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';
Arash Hatami
fuente
13

Si realmente no puede obtener un índice único en la tabla, puede intentar ...

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;
Brian Hooper
fuente
12

Para superar un problema similar, he modificado la tabla para tener una columna única. Usando su ejemplo, en la creación tendría algo como:

name VARCHAR(20),
UNIQUE (name)

y luego use la siguiente consulta al insertarlo:

INSERT IGNORE INTO train
set table_listnames='Rupert'
Galleta obsesiva
fuente
Prefiero esta solución
Ilyas karim
9

Esta consulta funciona bien:

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

Y puede crear la tabla usando la siguiente consulta:

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 ;

Nota: Cree una tabla usando la segunda consulta antes de intentar usar la primera consulta.

dakab
fuente
hash de contraseña md5 ... ¡ay!
Chad Grant
4

Brian Hooper: Casi llegas al punto pero tienes un error en tu synatx. Su inserto nunca funcionará. Probé en mi base de datos y aquí está la respuesta correcta:

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'

Te estoy dando mi ejemplo de y mesa. Insertar es casi lo mismo que escribió Bian Hooper, excepto que puse el select FROM DUAL en otra tabla. Saludos cordiales, Ivan

Ivan Laharnar
fuente
2
¿Qué es ese "dual"? ¿Una palabra clave incorporada? Simplemente no veo la diferencia de lo que dijo Brian ... EDITAR: La investigación adicional obtuvo resultados mixtos y contradictorios (?). Mientras que la página de la tabla DUAL de SQL dice que MySQL no admite tablas DUAL, el manual de MySQL dice que sí. Mi propia prueba muestra que no lo hace, ya que da unknown table status: TABLE_TYPEmensajes, aunque la consulta arrojó un resultado. ¿Probablemente porque MySQL no requiere la FROM DUALcláusula?
not2qubit
4
insert into customer_keyskill(customerID, keySkillID)
select  2,1 from dual
where not exists ( 
    select  customerID  from customer_keyskill 
    where customerID = 2 
    and keySkillID = 1 )
Oms G
fuente
dual es una tabla de Oracle, no MySQL
Muy irregular
3

Estás insertando no actualizando el resultado. Puede definir la columna de nombre en la columna primaria o establecerla como única.

Usuario 1034
fuente
3

Tuve un problema, y ​​el método que Mike aconsejó funcionó en parte, tuve un error Dublicate Column name = '0', y cambié la sintaxis de su consulta como esta`

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

El problema fue con los nombres de columna. sum3 era igual a sum4 y mysql arrojó nombres de columna duplicados, y escribí el código en esta sintaxis y funcionó perfectamente,

Hovhannes Babayan
fuente
1
El propietario de DUAL es SYS (SYS posee el diccionario de datos, por lo tanto, DUAL es parte del diccionario de datos), pero cualquier usuario puede acceder a DUAL. La tabla tiene una sola columna VARCHAR2 (1) llamada DUMMY que tiene un valor de 'X'. MySQL permite que DUAL se especifique como una tabla en consultas que no necesitan datos de ninguna tabla. La tabla DUAL es una tabla especial de una fila y una columna presente de manera predeterminada en Oracle y otras instalaciones de bases de datos. En Oracle, la tabla tiene una sola columna VARCHAR2 (1) llamada DUMMY que tiene un valor de 'X'. Es adecuado para su uso en la selección de una pseudo columna como SYSDATE o USER.
Adnan Haider
3

Tuve un problema similar y necesitaba insertar múltiples si no existía. Entonces, a partir de los ejemplos anteriores, llegué a esta combinación ... está aquí en caso de que alguien la necesite.

Aviso: tuve que definir el nombre en todas partes, ya que MSSQL lo requería ... MySQL también funciona con *.

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;

o

MSSQL: CREATE TABLE [nombres] ([OID] IDENTIFICACIÓN INT (1, 1) NO NULL, [nombre] NVARCHAR (32) NO NULL, CLAVE PRIMARIA CLUSIFICADA ([OID] ASC)); CREAR ÍNDICE ÚNICO NO AGRUPADO [Index_Names_Name] ON [nombres] ([nombre] ASC);

Waldemar
fuente
2

Esta consulta se puede usar en código PHP.

Tengo una columna de ID en esta tabla, así que necesito verificar la duplicación de todas las columnas, excepto esta columna de ID:

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

y ahora se agregará un nuevo elemento solo en caso de que no exista una fila con valores configurados en SETcadena

Andrés
fuente