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

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

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

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

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

Ответ 1

используйте 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:

18th October 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-запросов.

Ответ 2

Решение:

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) 

Объяснение:

Внутренний запрос

SELECT * FROM 'table' 
      WHERE 'value1'='stuff for value1' AND 'value2'='stuff for value2' LIMIT 1

используется как WHERE NOT EXISTS -condition обнаруживает, существует ли уже строка с данными для вставки. После того, как одна строка такого типа найдена, запрос может остановиться, поэтому LIMIT 1 (микрооптимизация может быть опущена).

Промежуточный запрос

SELECT 'stuff for value1', 'stuff for value2' FROM DUAL

представляет значения для вставки. DUAL относится к специальной таблице из одной строки и одной таблицы столбцов, которая присутствует по умолчанию во всех базах данных Oracle (см. https://en.wikipedia.org/wiki/DUAL_table). На MySQL-сервере версии 5.7.26 я получил правильный запрос, когда пропустил FROM DUAL, но более старые версии (например, 5.5.60), по-видимому, требуют информацию FROM. Используя WHERE NOT EXISTS, промежуточный запрос возвращает пустой набор результатов, если самый внутренний запрос нашел соответствующие данные.

Внешний запрос

INSERT INTO 'table' ('value1', 'value2') 

вставляет данные, если они получены промежуточным запросом.

Ответ 3

при дублировании ключевого обновления или 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] ... ]

Ответ 4

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

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

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

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

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

Ответ 5

Вот 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'
  )
);
?>

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

Ответ 9

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

Ответ 10

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

Ответ 11

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

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

INSERT IGNORE INTO таблица

Ответ 12

Стоит отметить, что INSERT IGNORE по-прежнему будет увеличивать первичный ключ независимо от того, был ли оператор успешным или нет, как обычный INSERT.

Это вызовет пробелы в ваших первичных ключах, что может сделать программиста психически нестабильным. Или, если ваше приложение плохо спроектировано и зависит от совершенных добавочных первичных ключей, оно может стать головной болью.

Посмотрите на innodb_autoinc_lock_mode = 0 (настройка сервера и имеет небольшое снижение производительности) или сначала используйте SELECT, чтобы убедиться, что ваш запрос не потерпит неудачу (что также сопровождается снижением производительности и дополнительным кодом).