Как вставить, если не существует в MySQL?

Я начал с googling и нашел это статьи который говорит о таблицах мьютекса.

у меня есть таблица с ~14 миллионов записей. Если я хочу добавить больше данных в том же формате, есть ли способ гарантировать, что запись, которую я хочу вставить, уже не существует без использования пары запросов (т. е. один запрос для проверки и один для вставки-это результирующий набор пуст)?

тут unique ограничение на поле гарантию insert потерпит неудачу, если это уже там?

похоже, что с просто ограничение, когда я выпускаю insert через php, скрипт хрипит.

9 ответов


использовать INSERT IGNORE INTO table

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

там же INSERT … ON DUPLICATE KEY UPDATE синтаксис, вы можете найти объяснения на dev.mysql.com


сообщение от bogdan.org.ua согласно Google в webcache:

18 октября 2007

для начала: начиная с последнего MySQL, синтаксис, представленный в заголовке, не вероятный. Но есть несколько очень простых способов добиться того, что ожидается использование существующих функций.

существует 3 возможных решения: использование INSERT IGNORE, REPLACE или INSERT ... ON DUPLICATE KEY UPDATE.

представьте, что у нас есть таблица:

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;

теперь представьте, что у нас есть автоматический конвейер импорта зачетно метаданные от Ensembl, и что из-за различных причин конвейера может быть нарушена на любом этапе исполнения. Таким образом, нам нужно обеспечить два вещи: 1) повторные исполнения трубопровода не разрушат наше база данных, и 2) повторные исполнения не умрут из-за " дубликата ошибки первичного ключа.

метод 1: Использование REPLACE

Это очень просто:

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

если запись существует, она будет перезаписана; если она еще не существует существует, он будет создан. Однако использование этого метода неэффективно для нашего случая: нам не нужно перезаписывать существующие записи, это нормально просто, чтобы пропустить их.

Метод 2: Использование INSERT IGNORE также очень просто:

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

здесь, если "ensembl_transcript_id" уже присутствует в база данных, она будет молча пропущена (проигнорирована). (Если быть более точным, вот цитата из справочного руководства MySQL: "если вы используете IGNORE ключевое слово, ошибки, возникающие при выполнении инструкции INSERT вместо этого рассматриваются как предупреждения. Например, без IGNORE, строка, которая дублирует существующий уникальный индекс или значение первичного ключа в таблице вызывает ошибку повторяющегося ключа и оператор прерывается.".) Если запись еще не существует, она будет создана.

этот второй метод имеет несколько потенциальных недостатков, в том числе не прерывание запроса в случае возникновения какой-либо другой проблемы (см. руководство.) Таким образом он должен быть использован если ранее испытано без Игнорировать ключевое слово.

есть еще один вариант: использовать INSERT ... ON DUPLICATE KEY ОБНОВЛЕНИЕ синтаксис, и в части обновления просто ничего не делать, делать что-то бессмысленное (пустая) операция, например вычисление 0+0 (Джеффри предлагает выполнить id=назначение id для MySQL optimization engine, чтобы игнорировать это операция.) Преимущество этого метода заключается в том, что он игнорирует только дубликат ключевые события, и по-прежнему прерывается на других ошибках.

в качестве окончательного уведомления: этот пост был вдохновлен Xaprb. Я тоже советую обратитесь к его другому сообщению о написании гибкого SQL запросы.


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 

кроме того, внешний SELECT заявление может ссылаться на DUAL для обработки случая, когда таблица изначально пуста:

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 

на дубликат ключа обновления или вставить игнорировать могут быть жизнеспособными решениями с MySQL.


пример на дубликат ключа обновления обновление на основе 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;

пример вставить игнорировать на основе 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] ... ]

или:

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] ... ]

или:

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

любое простое ограничение должно выполнять эту работу, если допустимо исключение. Примеры:

  • первичный ключ, если не суррогат
  • уникальное ограничение для столбца
  • ограничение уникальности нескольких столбцов

извините, это кажется обманчиво простым. Я знаю, что это выглядит плохо, столкнувшись со связью, которую вы разделяете с нами. ;-(

но я никогда не даю этот ответ, потому что он, кажется, заполняет вашу потребность. (Если нет, это может вызвать обновление ваши требования, которые были бы "хорошей вещью"(TM) также).

редактировать: если вставка нарушит уникальное ограничение базы данных, исключение будет брошено на уровне базы данных, ретранслируемом драйвером. Это, безусловно, остановит ваш сценарий, с неудачей. В PHP должно быть возможно адресовать этот случай ...


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

если запись существует, она будет перезаписана; если она еще не существует, она будет создана.


вот функция PHP, которая вставит строку, только если все указанные значения столбцов еще не существуют в таблице.

  • Если один из столбцов совпадают, то строка будет добавлена.

  • Если таблица пуста, строка будет добавлена.

  • Если существует строка, в которой все указанные столбцы имеют указанные значения, строка не будет добавлена.

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

пример использования :

<?php
insert_unique('mytable', array(
  'mycolumn1' => 'myvalue1',
  'mycolumn2' => 'myvalue2',
  'mycolumn3' => 'myvalue3'
  )
);
?>

попробуйте следующее:

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

попробуй:

// 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
}

или вы можете сделать:

// 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
    }
}

этот метод быстрый и простой. Для повышения скорости запроса в Столбцах индекса большой таблицы " xxx " (в моем примере ).


есть несколько ответов, которые охватывают как решить эту проблему, если у вас есть UNIQUE индекс, который вы можете проверить с помощью ON DUPLICATE KEY или INSERT IGNORE. Это не всегда так, и как UNIQUE имеет ограничение длины (1000 байт), которое вы не сможете изменить. Например, мне пришлось работать с метаданными в WordPress (wp_postmeta).

я, наконец, решил его с двумя запросами:

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 = ?);

запрос 1 является регулярным UPDATE запрос без эффекта, когда набор данных в вопроса нет. Запрос 2-это INSERT на NOT EXISTS, т. е. INSERT выполняется только тогда, когда набор данных не существует.