协慌网

登录 贡献 社区

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

我开始使用谷歌搜索,发现这篇文章讨论了互斥表。

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

对字段的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.com找到解释


根据谷歌的网络摄像头发布来自 bogdan.org.ua:

2007 年 10 月 18 日

首先:从最新的 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' 已经存在于数据库中,它将被静默跳过(忽略)。 (更准确地说,这是 MySQL 参考手册的引用:“如果使用 IGNORE 关键字,则执行 INSERT 语句时发生的错误将被视为警告。例如,没有 IGNORE,会复制现有 UNIQUE 索引的行表中的 PRIMARY KEY 值会导致重复键错误,并且语句将被中止。“。)如果该记录尚不存在,则会创建该记录。

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

还有一个选项:使用 INSERT ... ON DUPLICATE KEY UPDATE 语法,并且在 UPDATE 部分只是不做任何无意义(空)操作,比如计算 0 + 0(Geoffray 建议为 MySQL 优化做 id = id 赋值引擎忽略此操作)。此方法的优点是它只忽略重复的键事件,并仍然中止其他错误。

最后通知:这篇文章的灵感来自 Xaprb。我还建议查阅他关于编写灵活 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 的可行解决方案。


基于 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 ignore示例

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] ... ]