web-dev-qa-db-pt.com

Como "inserir se não existe" no MySQL?

Comecei pesquisando e encontrei este article que fala sobre tabelas mutex.

Eu tenho uma mesa com ~ 14 milhões de registros. Se eu quiser adicionar mais dados no mesmo formato, existe uma maneira de garantir que o registro que eu quero inserir ainda não exista sem usar um par de consultas (ou seja, uma consulta para verificar e uma para inserir é o conjunto de resultados é esvaziar)?

Uma restrição unique em um campo garante que a variável insert falhará se já estiver lá?

Parece que com meramente uma restrição, quando eu emito a inserção via php, o script croaks.

745
warren

use INSERT IGNORE INTO table

veja http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html

existe também a sintaxe INSERT … ON DUPLICATE KEY UPDATE, você pode encontrar explicações em dev.mysql.com


Post de bogdan.org.ua de acordo com webcache do Google :

18 de outubro de 2007

Para começar: a partir do último MySQL, a sintaxe apresentada no título não é possível. Mas existem várias maneiras muito fáceis de realizar o que é esperado usando a funcionalidade existente.

Existem 3 soluções possíveis: usando INSERT IGNORE, REPLACE ou INSERT… ON DUPLICATE KEY UPDATE.

Imagine que temos uma mesa:

CREATE TABLE `transcripts` (
`ensembl_transcript_id` varchar(20) NOT NULL,
`transcript_chrom_start` int(10) unsigned NOT NULL,
`transcript_chrom_end` int(10) unsigned NOT NULL,
PRIMARY KEY (`ensembl_transcript_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Agora imagine que temos um pipeline automático importando metadados de transcrições do Ensembl, e que, devido a várias razões, o pipeline pode ser quebrado em qualquer etapa da execução. Assim, precisamos garantir duas coisas: 1) execuções repetidas do pipeline não destruirão nosso banco de dados, e 2) execuções repetidas não morrerão devido a erros de "chave primária duplicada".

Método 1: usando REPLACE

É muito simples:

REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = ‘ENSORGT00000000001′,
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

Se o registro existir, ele será sobrescrito; se ainda não existir, será criado. No entanto, usar esse método não é eficiente para nosso caso: não é necessário substituir os registros existentes, mas é só ignorá-los.

Método 2: usando o INSERT IGNORE Também é muito simples:

INSERT IGNORE INTO `transcripts`
SET `ensembl_transcript_id` = ‘ENSORGT00000000001′,
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

Aqui, se o "ensembl_transcript_id" já estiver presente no banco de dados, ele será ignorado silenciosamente (ignorado). (Para ser mais preciso, aqui está uma citação do manual de referência do MySQL: "Se você usar a palavra-chave IGNORE, erros que ocorrem durante a execução da instrução INSERT são tratados como avisos. Por exemplo, sem IGNORE, uma linha que duplica um índice UNIQUE existente ou o valor PRIMARY KEY na tabela causa um erro de chave duplicada e a instrução é abortada. ”.) Se o registro ainda não existir, ele será criado.

Este segundo método tem várias fraquezas potenciais, incluindo a não-aborto da consulta no caso de qualquer outro problema ocorrer (consulte o manual). Assim, ele deve ser usado se previamente testado sem a palavra-chave IGNORE.

Há mais uma opção: usar a sintaxe INSERT… ON DUPLICATE KEY UPDATE e, na parte UPDATE, não fazer nada para fazer alguma operação (vazia) sem sentido, como calcular 0 + 0 (Geoffray sugere fazer a atribuição id = id para a otimização do MySQL motor para ignorar esta operação). A vantagem desse método é que ele apenas ignora eventos chave duplicados e ainda aborta em outros erros.

Como um aviso final: este post foi inspirado em Xaprb. Eu também aconselho a consultar sua outra postagem sobre como escrever consultas SQL flexíveis.

730
knittl
INSERT INTO `table` (value1, value2) 
SELECT 'stuff for value1', 'stuff for value2' FROM `table` 
WHERE NOT EXISTS (SELECT * FROM `table` 
      WHERE value1='stuff for value1' AND value2='stuff for value2') 
LIMIT 1 

Como alternativa, a instrução SELECT externa pode se referir a DUAL para manipular o caso em que a tabela está inicialmente vazia:

INSERT INTO `table` (value1, value2) 
SELECT 'stuff for value1', 'stuff for value2' FROM DUAL
WHERE NOT EXISTS (SELECT * FROM `table` 
      WHERE value1='stuff for value1' AND value2='stuff for value2') 
LIMIT 1 
171
Server

na atualização de chave duplicada , ou insert ignore podem ser soluções viáveis ​​com o MySQL.


Exemplo de na atualização de chave duplicada update baseada em mysql.com

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=c+1;

UPDATE table SET c=c+1 WHERE a=1;

Exemplo de insert ignore baseado em mysql.com

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    {VALUES | VALUE} ({expr | DEFAULT},...),(...),...
    [ ON DUPLICATE KEY UPDATE
      col_name=expr
        [, col_name=expr] ... ]

Ou:

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name
    SET col_name={expr | DEFAULT}, ...
    [ ON DUPLICATE KEY UPDATE
      col_name=expr
        [, col_name=expr] ... ]

Ou:

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    SELECT ...
    [ ON DUPLICATE KEY UPDATE
      col_name=expr
        [, col_name=expr] ... ]
52
Zed

Qualquer restrição simples deve fazer o trabalho, se uma exceção for aceitável. Exemplos :

  • chave primária, se não substituto
  • restrição exclusiva em uma coluna
  • restrição exclusiva de várias colunas

Desculpe, isso parece enganosamente simples. Eu sei que parece mal confrontado com o link que você compartilha conosco. ;-(

Mas eu nunca dou esta resposta, porque parece preencher sua necessidade. (Se não, isso pode desencadear a atualização dos seus requisitos, o que também seria "uma coisa boa" (TM)).

Editado : Se uma inserção quebrar a restrição exclusiva do banco de dados, uma exceção será lançada no nível do banco de dados, retransmitida pelo driver. Isso certamente irá parar seu roteiro, com um fracasso. Deve ser possível em PHP endereçar esse caso ...

24
KLE

Aqui está uma função PHP que irá inserir uma linha somente se todos os valores de colunas especificados ainda não existirem na tabela.

  • Se uma das colunas diferir, a linha será adicionada.

  • Se a tabela estiver vazia, a linha será adicionada.

  • Se existir uma linha em que todas as colunas especificadas tenham os valores especificados, a linha não será adicionada.

    function insert_unique($table, $vars)
    {
      if (count($vars)) {
        $table = mysql_real_escape_string($table);
        $vars = array_map('mysql_real_escape_string', $vars);
    
        $req = "INSERT INTO `$table` (`". join('`, `', array_keys($vars)) ."`) ";
        $req .= "SELECT '". join("', '", $vars) ."' FROM DUAL ";
        $req .= "WHERE NOT EXISTS (SELECT 1 FROM `$table` WHERE ";
    
        foreach ($vars AS $col => $val)
          $req .= "`$col`='$val' AND ";
    
        $req = substr($req, 0, -5) . ") LIMIT 1";
    
        $res = mysql_query($req) OR die();
        return mysql_insert_id();
      }
    
      return False;
    }
    

Exemplo de uso:

<?php
insert_unique('mytable', array(
  'mycolumn1' => 'myvalue1',
  'mycolumn2' => 'myvalue2',
  'mycolumn3' => 'myvalue3'
  )
);
?>
18
Jrm
REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

Se o registro existir, ele será sobrescrito; se ainda não existir, será criado.

18
Rocio

Tente o seguinte:

IF (SELECT COUNT(*) FROM beta WHERE name = 'John' > 0)
  UPDATE alfa SET c1=(SELECT id FROM beta WHERE name = 'John')
ELSE
BEGIN
  INSERT INTO beta (name) VALUES ('John')
  INSERT INTO alfa (c1) VALUES (LAST_INSERT_ID())
END
17
Jeb's

Existem várias respostas que cobrem como resolver isso se você tiver um índice UNIQUE que possa ser verificado com ON DUPLICATE KEY ou INSERT IGNORE. Isso nem sempre é o caso, e como UNIQUE tem uma restrição de comprimento (1000 bytes), talvez você não consiga alterar isso. Por exemplo, eu tive que trabalhar com metadados no WordPress (wp_postmeta).

Eu finalmente resolvi com duas perguntas:

UPDATE wp_postmeta SET meta_value = ? WHERE meta_key = ? AND post_id = ?;
INSERT INTO wp_postmeta (post_id, meta_key, meta_value) SELECT DISTINCT ?, ?, ? FROM wp_postmeta WHERE NOT EXISTS(SELECT * FROM wp_postmeta WHERE meta_key = ? AND post_id = ?);

A consulta 1 é uma consulta regular de UPDATE sem efeito quando o conjunto de dados em questão não está lá. A consulta 2 é um INSERT que depende de um NOT EXISTS, ou seja, o INSERT é executado apenas quando o conjunto de dados não existe.

5
wortwart

Experimentar:

// Check if exist cod = 56789
include "database.php";

$querycheck = mysql_query ("SELECT * FROM `YOURTABLE` WHERE `xxx` = '56789';");
$countrows = mysql_num_rows($querycheck);
if($countrows == '1')
{
  // Exist 
}
else
{
 // .... Not exist
}

Ou você pode fazer:

// Check if exist cod = 56789
include "database.php";

$querycheck = mysql_query ("SELECT * FROM `YOURTABLE` WHERE `xxx` = '56789';");
$countrows = mysql_num_rows($querycheck);
while($result = mysql_fetch_array($querycheck))
{
    $xxx = $result['xxx'];
    if($xxx == '56789')
    {
      // Exist
    }
    else
    {
      // Not exist
    }
}

Este método é rápido e fácil. Para melhorar a velocidade da consulta em sua tabela grande colunas INDEX 'xxx' (no meu exemplo).

4
Andrea php