Как «вставить, если не существует» в MySQL? - PullRequest
753 голосов
/ 01 сентября 2009

Я начал с поиска в Google и нашел статью , в которой говорится о таблицах мьютекса.

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

Гарантирует ли ограничение unique на поле, что insert потерпит неудачу, если он уже существует?

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

Ответы [ 9 ]

737 голосов
/ 01 сентября 2009

использование 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 :

18 октября 2007

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

Существует 3 возможных решения: использование INSERT IGNORE, REPLACE или ВСТАВИТЬ… НА ДУБЛИКАТ. КЛЮЧЕВОЕ ОБНОВЛЕНИЕ.

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

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, строка, которая дублирует существующий индекс UNIQUE или значение PRIMARY KEY в таблице вызывает ошибку повторяющегося ключа, и оператор отменяется. ".) Если запись еще не существует, она будет создана.

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

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

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

175 голосов
/ 11 июня 2010
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 
53 голосов
/ 01 сентября 2009

при обновлении дубликата ключа или вставка игнорировать может быть жизнеспособным решением с 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] ... ]
24 голосов
/ 01 сентября 2009

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

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

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

Но я, тем не менее, даю этот ответ, потому что он, кажется, удовлетворяет вашу потребность. (Если это не так, это может привести к обновлению ваших требований, что также будет «хорошим делом» (TM)).

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

18 голосов
/ 06 июля 2012
REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

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

18 голосов
/ 06 марта 2012

Вот функция 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'
  )
);
?>
17 голосов
/ 10 мая 2013

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

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
5 голосов
/ 18 августа 2016

Существует несколько ответов, описывающих, как решить эту проблему, если у вас есть индекс 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 выполняется только тогда, когда набор данных не существует.

4 голосов
/ 25 сентября 2015

Попробуйте:

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

Этот метод быстрый и простой. Для повышения скорости запроса в вашей большой таблице столбцы INDEX 'xxx' (в моем примере).

...