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

607

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

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

Ограничение unique в поле гарантирует, что insert завершится с ошибкой, если он уже существует?

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

  • 2
    dev.mysql.com/doc/refman/5.0/en/if.html
  • 0
    См. Stackoverflow.com/questions/44550788/… для обсуждения о том, чтобы не записывать значения auto_inc.
Показать ещё 6 комментариев
Теги:
performance
primary-key

11 ответов

630
Лучший ответ

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

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

Есть еще один вариант: использовать INSERT... ON DUPLICATE KEY UPDATE синтаксис, а в части UPDATE просто ничего не делают (пустой), например, вычисление 0 + 0 (Geoffray предлагает сделать id = id для механизма оптимизации MySQL, чтобы игнорировать этот операция). Преимуществом этого метода является то, что он игнорирует только дубликаты ключевые события, и все еще прерывает другие ошибки.

Как последнее замечание: этот пост был вдохновлен Xaprb. Идентификация также проконсультируйтесь со своим другим сообщением о написании гибких SQL-запросов.

  • 3
    и могу ли я объединить это с задержкой, чтобы ускорить сценарий?
  • 3
    да, вставка с задержкой может ускорить процесс для вас. попробуй это
Показать ещё 13 комментариев
143
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 
  • 4
    Можете ли вы дать больше информации о том, как использовать это?
  • 22
    Как насчет производительности по этому запросу?
Показать ещё 9 комментариев
41

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

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

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

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

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

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

  • 1
    я добавил уточнение к вопросу - ваш ответ по-прежнему применим?
  • 2
    Я верю, что это так. Уникальное ограничение приведет к сбою неправильных вставок. Примечание: вы должны иметь дело с этой ошибкой в вашем коде, но это вполне стандартно.
Показать ещё 2 комментария
16

Вот 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'
  )
);
?>
  • 2
    Довольно дорого, если у вас огромный груз вставок.
  • 0
    верно, но эффективно, если вам нужно добавить определенные проверки
15
REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

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

  • 6
    REPLACE может удалить строку, а затем вставить вместо обновления. Побочным эффектом является то, что ограничения могут удалять другие объекты и запускать триггеры удаления.
13

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

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
  • 1
    Попробуйте это ответы на StackOverflow малозначительны, потому что они очень мало делают для обучения ОП и тысяч будущих исследователей. Пожалуйста, отредактируйте этот ответ, чтобы включить, как работает решение и почему это хорошая идея.
10

Replace может работать для вас.

4

Try:

// 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" (в моем примере).

  • 1
    Подобные ответы уже были предоставлены, а принятый ответ еще лучше.
2

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

0

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

или просто перейдите к

INSERT IGNORE INTO таблица

Сообщество Overcoder
Наверх
Меню