如何在MySQL中插入“如果不存在”?

我开始用googlesearch,并find了这篇谈论互斥表的文章 。

我有一张约有1400万条logging的表格。 如果我想以相同的格式添加更多的数据,有没有办法确保我想要插入的logging不存在,而不使用一对查询(即,一个查询来检查和一个插入是结果集是空)?

一个字段上的unique约束是否保证insert将失败,如果它已经在那里?

似乎只有一个约束,当我通过php发出插入,剧本呱呱叫。

使用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.comfind解释


根据Google的networkingcaching从bogdan.org.ua发布:

2007年10月18日

开始:从最新的MySQL开始,标题中提供的语法是不可能的。 但是有几种非常简单的方法可以完成使用现有function的预期function。

有三种可能的解决scheme:使用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; 

现在想象一下,我们有一个自动pipe道从Ensembl导入转录元数据,并且由于各种原因,stream水线可能在任何执行步骤中被破坏。 因此,我们需要确保两件事情:1)重复执行的pipe道不会破坏我们的数据库,2)重复执行不会因为“重复的主键”错误而死亡。

方法1:使用REPLACE

这很简单:

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

如果logging存在,它将被覆盖; 如果它不存在,它将被创build。 但是,使用这种方法对于我们的情况来说效率不高:我们不需要覆盖现有logging,只需跳过它们就可以了。

方法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值会导致重复键错误,并且语句会中止。“)如果logging尚不存在,则会创build该logging。

第二种方法有几个潜在的弱点,包括在发生任何其他问题时不中断查询(参见手册)。 因此,如果以前没有使用IGNORE关键字进行testing,应该使用它。

还有一个select:使用INSERT … ON DUPLICATE KEY UPDATE语法,并且在UPDATE部分中,不做任何无意义的操作,如计算0 + 0(Geoffraybuild议为MySQL优化做id = id赋值引擎忽略这个操作)。 这种方法的好处是它只会忽略重复的键事件,并且还会中止其他错误。

作为最后通知:这篇文章受到Xaprb的启发。 我还build议在编写灵活的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可行的解决scheme。


基于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))。

已编辑 :如果插入操作会破坏数据库唯一性约束,则会在驱动程序中继的数据库级别引发exception。 它肯定会停止你的脚本,失败。 PHP中必须有可能解决这个问题

这是一个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' ) ); ?> 
 REPLACE INTO `transcripts` SET `ensembl_transcript_id` = 'ENSORGT00000000001', `transcript_chrom_start` = 12345, `transcript_chrom_end` = 12678; 

如果logging存在,它将被覆盖; 如果它不存在,它将被创build。

尝试以下操作:

 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 

Replace可能会为你工作。

尝试:

 // 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'(在我的例子中)查询的速度。

有几个答案涵盖如何解决这个问题,如果你有一个UNIQUE索引,你可以用ON DUPLICATE KEYINSERT 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只在数据集不存在的时候执行。

你可以运行一个快速select来查找它是否存在,然后不要插入任何东西,它们将是两条指令

或者干脆跟着去

INSERT IGNORE INTO表