MySQL: Inserir registro se não existir na tabela

384

Estou tentando executar a seguinte consulta:

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

Mas isso retorna um erro. Basicamente, não quero inserir um registro se o campo 'nome' do registro já existir em outro registro - como verificar se o novo nome é exclusivo?

Rupert
fonte
5
possível duplicata de Como 'inserir se não existir' no MySQL?
Warren
12
Todas as respostas atuais para essa ou outras bobagens assumem que você pode adicionar um índice exclusivo. Às vezes, a decisão é baseada na lógica de negócios que não pode ser imposta em toda a tabela. Por exemplo, você permite várias linhas com um determinado valor em uma coluna, mas outro valor na coluna só poderá aparecer em uma linha. Como conseguimos isso?
Oscar
Veja também: stackoverflow.com/q/1361340/4418
warren

Respostas:

502

Na verdade, não estou sugerindo que você faça isso, pois o UNIQUEíndice sugerido por Piskvor e outros é uma maneira muito melhor de fazê-lo, mas você pode realmente fazer o que estava tentando:

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;

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

Tente inserir o mesmo registro novamente:

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

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

E assim por diante...


Atualizar:

Para evitar #1060 - Duplicate column nameerros no caso de dois valores serem iguais, você deve nomear as colunas do 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  |
+----+---------+-----------+------+
Mike
fonte
18
Graças que ajudou. Meu problema real é muito mais complexo e a coluna simplesmente não pode ser única e não posso depender da chave primária. Mas é exatamente isso que eu estava procurando.
Rupert
2
@Piskovar: Concordado. @Rupert: você deve indexar a coluna mencionada na instrução de seleção interna ( nameneste caso), se possível. Observe também que você pode fazer SELECT 'John', 'Doe', '022' FROM table_listnames, em vez de SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp- mas isso só funcionará se table_listnamesjá contiver uma ou mais linhas. Duvido que a velocidade seja diferente, então provavelmente não é uma preocupação.
Mike
3
@VijayRamamurthy: Isso funciona porque você insere o resultado de uma instrução Select. Leia a consulta com atenção -> A WHEREinstrução pertence à SELECTconsulta. A consulta Select retorna uma única linha de dados (os dados são inseridos) ou nenhum dado (nada é inserido) #
277 Philipp Philipp
13
Isso não parece funcionar se você deseja inserir o mesmo valor duas vezes em campos diferentes (por exemplo, SELECT 'Humbert', 'Humbert', '1'na seleção interna). Eu recebo umERROR 1060 (42S21): Duplicate column name
cburgmer 10/10
28
@burgburg Eu tenho o mesmo problema #1060 - Duplicate column name. Você encontrou uma solução? Edit: Encontrei. Adicione por trás de cada valor um AS: #SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp
Kai Noack
292

INSERT não permite WHEREna sintaxe .

O que você pode fazer: crie um UNIQUE INDEXno campo que deve ser exclusivo ( name) e use:

  • normal INSERT(e lide com o erro se o nome já existir)
  • INSERT IGNORE(que falhará silenciosamente causa um aviso (em vez de erro) se o nome já existir)
  • INSERT ... ON DUPLICATE KEY UPDATE(que executará UPDATEno final se o nome já existir, consulte a documentação )
Piskvor saiu do prédio
fonte
11
se você precisa para obter mensagem de aviso pode depois fazermysql> show warnings\G
fiorentinoing
2
ime, a mensagem de aviso, no caso de INSERT IGNORE, corresponde à regex de^Duplicate entry '.+' for key '.+'$
user2426679
11
se você usar insert ignore, ele ignorará outros erros que podem ocorrer e não são duplicados?
Gepex # 9/18
11
@gepex Sim, será. Esse é um grande problema com o uso da INSERT IGNOREIMO.
Shmosel 31/05/19
11
Nota: esta resposta pode não ser a melhor opção se uma das colunas na restrição de chave exclusiva for anulável! A restrição funciona, mas você pode esperar outros resultados :) A inserção ("John Doe", NULL), ("John Doe", NULL) resulta em duas linhas, embora ambos os campos juntos tenham uma restrição de chave exclusiva. Ver também stackoverflow.com/questions/4081783/unique-key-with-nulls
Piemol
57

Trabalhou:

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
fonte
Isso funciona apenas para bancos de dados Oracle, certo? pt.wikipedia.org/wiki/DUAL_table Esta questão é especificamente sobre o MySQL!
Altamente irregular
11
Eu não testei no banco de dados Oracle. Está funcionando bem no MySQL e testado.
Mahbub Tito 16/10
9
Eu aprendi desde "dual" está disponível no MySQL, mas não em MariaDB, o ramo de código aberto do MySQL
altamente irregular
3
No link da wikipedia da @ HighlyIrregular "Vários outros bancos de dados (incluindo Microsoft SQL Server, MySQL, PostgreSQL, SQLite e Teradata) permitem omitir totalmente a cláusula FROM se nenhuma tabela for necessária. Isso evita a necessidade de qualquer tabela fictícia." A mesma consulta parece funcionar no MySql (5.7, se isso importa) sem o FROM DUAL.
Stannius 14/05/19
11
Testado e funcionando bem no MariaDB! Obrigado @MahbubTito
Jeff Monteiro
27

O MySQL fornece uma solução muito fofa:

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

Muito fácil de usar, pois você declarou uma chave primária exclusiva (aqui com o valor 5).

sdesvergez
fonte
para impedi-lo, você precisa criar um índice exclusivo, mais informações aqui: ligação
Azmeer
Muito perigoso. Não tente, a menos que saiba o que está fazendo. Na verdade, não tente nada. Verifique outras respostas.
Jhourlad Estrella
3
Observe que isso exclui todas as linhas correspondentes e as insere novamente, uma versão mais segura desse mesmo comportamento é usar ON DUPLICTE KEY UPDATE, que atualiza as linhas de cálculo de matemática em vez de excluí-las e reinseri-las: dev.mysql.com/doc/ refman / 5.7 / pt-br /
replace.html
22
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;

Aqui, a consulta mysql, que insere registros, se não existir, ignorará os registros semelhantes existentes.

----Untested----
Montaser El-sawy
fonte
4
não funcionou para mim, INSERT IGNORE INTO emAssignedTagsForEvent SET eventId = '2', defaultTagId = '1';
Pitu
8
Parece uma mistura de sintaxe de inserção e atualização. Você quer dizer INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);?
Hobo
@Hobo A partir do MySQL manual dev.mysql.com/doc/refman/5.7/en/insert.html sintaxe INSERT: Pode ser INSERT [...] IGNORE INTO mytable VALUES ...OuINSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...
Shirkam
"Se você usar o modificador IGNORE, os erros que ocorrem durante a execução da instrução INSERT serão ignorados." Então, basicamente, você não resolveu nada.
Marcelo Agimóvel
18

Você pode usar facilmente da seguinte maneira:

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

Dessa maneira, você pode inserir novos dados brutos e, se houver dados duplicados, substituir a coluna específica (as melhores colunas são os carimbos de data e hora).
Por exemplo :

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
fonte
13

Se você realmente não consegue obter um índice exclusivo sobre a tabela, tente ...

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
fonte
12

Para superar um problema semelhante, modifiquei a tabela para ter uma coluna exclusiva. Usando seu exemplo, na criação, eu teria algo como:

name VARCHAR(20),
UNIQUE (name)

e use a seguinte consulta ao inseri-lo:

INSERT IGNORE INTO train
set table_listnames='Rupert'
obsessiveCookie
fonte
Eu prefiro esta solução.
Ilyas karim
9

Esta consulta funciona bem:

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

E você pode criar a tabela usando a seguinte 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: Crie a tabela usando a segunda consulta antes de tentar usar a primeira consulta.

dakab
fonte
hash de senha md5 .... caramba!
Chad Grant
4

Brian Hooper: Você quase acertou no alvo, mas você tem um erro na sua sintaxe. Sua inserção nunca funcionará. Eu testei no meu banco de dados e aqui está a resposta certa:

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'

Estou lhe dando meu exemplo da tabela y. Insert é quase o mesmo que Bian Hooper escreveu, exceto que eu coloquei o select FROM DUAL em outra tabela. Cind cumprimentos, Ivan

Ivan Laharnar
fonte
2
O que é esse "dual"? Uma palavra-chave incorporada? Só não vejo a diferença do que Brian disse ... EDIT: Investigações adicionais foram confusas e contraditórias (?). Enquanto a página da tabela SQL DUAL diz que o MySQL não suporta tabelas DUAL, o manual do MySQL diz que sim. Meu próprio teste mostra que não, pois fornece unknown table status: TABLE_TYPEmensagens, embora a consulta tenha produzido um resultado. Provavelmente porque o MySQL não requer a 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
fonte
dual é uma tabela Oracle não MySQL
altamente irregular
3

Você está inserindo não atualizando o resultado. Você pode definir a coluna de nome na coluna principal ou defini-la como única.

Usuário 1034
fonte
3

Tive um problema e o método que Mike aconselhou funcionou parcialmente, tive um erro Dublicate Column name = '0' e alterei a sintaxe da sua 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;";

O problema estava com os nomes das colunas. sum3 era igual a sum4 e o mysql lançou nomes de colunas dublicate, e eu escrevi o código nessa sintaxe e funcionou perfeitamente,

Hovhannes Babayan
fonte
11
O proprietário do DUAL é SYS (o SYS possui o dicionário de dados, portanto o DUAL faz parte do dicionário de dados.), Mas o DUAL pode ser acessado por qualquer usuário. A tabela possui uma única coluna VARCHAR2 (1) chamada DUMMY que possui um valor de 'X'. O MySQL permite que o DUAL seja especificado como uma tabela em consultas que não precisam de dados de nenhuma tabela. A tabela DUAL é uma tabela especial de uma linha e coluna presente por padrão no Oracle e em outras instalações de banco de dados. No Oracle, a tabela possui uma única coluna VARCHAR2 (1) chamada DUMMY que possui um valor de 'X'. É adequado para uso na seleção de uma pseudo coluna, como SYSDATE ou USER.
Adnan haider
3

Eu tive um problema semelhante e precisava inserir vários, se não existir. Então, pelos exemplos acima, cheguei a essa combinação ... está aqui apenas no caso de alguém precisar.

Nota: Eu tive que definir o nome em todos os lugares, conforme exigido pelo MSSQL ... O MySQL também trabalha com *.

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;

ou

MSSQL: CREATE TABLE [nomes] ([OID] INT IDENTITY (1, 1) NÃO NULL, [nome] NVARCHAR (32) NÃO NULL, CHAVE PRIMÁRIA CLUSTERED ([OID] ASC)); CRIAR ÍNDICE EXCLUSIVO NÃO EXCLUSIVO [Index_Names_Name] ON [names] ([name] ASC);

Waldemar
fonte
2

Esta consulta pode ser usada no código PHP.

Eu tenho uma coluna de ID nesta tabela, portanto, preciso verificar se há duplicação para todas as colunas, exceto esta coluna 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;

e agora o novo item será adicionado apenas no caso de não existir uma linha com valores configurados na SETsequência

Andrew
fonte