如何在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 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
只在数据集不存在的时候执行。
你可以运行一个快速select来查找它是否存在,然后不要插入任何东西,它们将是两条指令
或者干脆跟着去
INSERT IGNORE INTO表