MySQL:如果表中不存在,则插入记录


384

我正在尝试执行以下查询:

INSERT INTO table_listnames (name, address, tele)
VALUES ('Rupert', 'Somewhere', '022')
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name='value'
);

但这会返回错误。基本上,如果该记录的“名称”字段已经存在于另一条记录中,我不想插入一条记录-如何检查新名称是否唯一?



12
当前对此或重复项的所有答案均假定您可以添加唯一索引。有时,决策是基于不能强加于整个表的业务逻辑的。例如,您允许一列中具有特定值的多行,但该列中的另一个值将仅允许出现在一行中。我们如何做到这一点?
奥斯卡

Answers:


502

我实际上并不建议您这样做,因为UNIQUEPiskvor和其他人建议的索引是一种更好的方法,但是您实际上可以做您想做的事情:

CREATE TABLE `table_listnames` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  `address` varchar(255) NOT NULL,
  `tele` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;

插入一条记录:

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
+----+--------+-----------+------+

尝试再次插入相同的记录:

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
+----+--------+-----------+------+

插入其他记录:

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'John'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
|  2 | John   | Doe       | 022  |
+----+--------+-----------+------+

等等...


更新:

为了防止#1060 - Duplicate column name在两个值相等的情况下出错,您必须命名内部SELECT的列:

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Unknown' AS name, 'Unknown' AS address, '022' AS tele) AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+---------+-----------+------+
| id | name    | address   | tele |
+----+---------+-----------+------+
|  1 | Rupert  | Somewhere | 022  |
|  2 | John    | Doe       | 022  |
|  3 | Unknown | Unknown   | 022  |
+----+---------+-----------+------+

18
谢谢你的帮助。我的实际问题要复杂得多,并且列不能唯一并且不能依赖主键。但这正是我想要的。
鲁珀特

2
@Piskovar:同意。@Rupert:如果可能,您应该索引内部select语句(name在这种情况下)中引用的列。另请注意,您可以执行SELECT 'John', 'Doe', '022' FROM table_listnames,而不是SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp-,但这仅在table_listnames已经包含一个或多个行时才有效。我怀疑速度是否会有所不同,因此可能不必担心。
Mike

3
@VijayRamamurthy:之所以有效,是因为您插入了Select语句的结果。仔细阅读查询-> WHERE语句属于SELECT查询。Select查询返回单个数据行(已插入数据)或根本没有数据(未插入任何数据)
Philipp 2013年

13
如果要将相同的值两次插入不同的字段(例如SELECT 'Humbert', 'Humbert', '1',内部选择中),这似乎不起作用。我收到ERROR 1060 (42S21): Duplicate column name
cburgmer

28
@cburgmer我遇到了同样的问题#1060 - Duplicate column name。您找到解决方案了吗?编辑:找到了。在每个值后面加上一个AS:SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp
Kai Noack

292

INSERT不允许WHERE使用语法

您可以执行的操作:UNIQUE INDEX在字段中创建一个唯一的(name),然后使用以下任一方法:

  • 正常INSERT(如果名称已经存在,则处理错误)
  • INSERT IGNORE(如果名称已经存在,它将以静默方式失败,并发出警告(而不是错误))
  • INSERT ... ON DUPLICATE KEY UPDATEUPDATE如果名称已经存在,它将在末尾执行,请参阅文档

1
如果您需要获取警告消息,则可以稍后执行mysql> show warnings\G
佛罗伦萨

2
ime,警告消息(在的情况下INSERT IGNORE)与^Duplicate entry '.+' for key '.+'$
user2426679

1
如果您使用插入忽略,是否会忽略可能不是因为重复而发生的其他错误?
gepex

1
@gepex是的,它将。这是使用INSERT IGNOREIMO 的大问题。
shmosel

1
注意:如果唯一键约束中的一列可为空,则此答案可能不是最佳选择!该约束有效,但是您可能希望得到其他结果:)尽管两个字段一起在唯一的键约束中,但插入(“ John Doe”,NULL),(“ John Doe”,NULL)会导致两行。另请参见stackoverflow.com/questions/4081783/unique-key-with-nulls
Piemol,

57

工作时间:

INSERT INTO users (full_name, login, password) 
  SELECT 'Mahbub Tito','tito',SHA1('12345') FROM DUAL
WHERE NOT EXISTS 
  (SELECT login FROM users WHERE login='tito');

这仅适用于Oracle数据库吧?en.wikipedia.org/wiki/DUAL_table这个问题专门关于MySQL!
高度不规则的

1
我没有在Oracle数据库中进行测试。它在MySQL上运行良好并经过测试。
Mahbub Tito

9
从那以后,我了解到“双重”在MySQL中可用,但在MySQL的开源分支MariaDB中不可用
高度不规则

3
来自@HighlyIrregular的Wikipedia链接:“如果不需要表,则其他几个数据库(包括Microsoft SQL Server,MySQL,PostgreSQL,SQLite和Teradata)都可以完全省略FROM子句。这避免了使用任何哑表。” 相同的查询似乎可以在MySql(如果重要的话,在5.7中)中运行,而无需FROM DUAL。
stannius

1
经过测试,可以在MariaDB中正常工作!谢谢@MahbubTito
Jeff Monteiro

27

MySQL提供了一个非常可爱的解决方案:

REPLACE INTO `table` VALUES (5, 'John', 'Doe', SHA1('password'));

因为您已经声明了唯一的主键(此处值为5),所以非常易于使用。


为防止这种情况,您需要创建一个唯一索引,请在此处了解更多信息:链接
Azmeer

非常危险。除非您知道自己在做什么,否则不要尝试。实际上,根本不要尝试。检查其他答案。
Jhourlad Estrella

3
注意,这会删除所有匹配的行,然后重新插入它们,更安全的版本是使用ON DUPLICTE KEY UPDATE,它更新数学行而不是删除并重新插入它们:dev.mysql.com/doc/ refman / 5.7 / en / replace.html
Omn

22
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;

此处是mysql查询,如果不存在则插入记录,并且将忽略现有的类似记录。

----Untested----

4
它对我不起作用,将IINSERT IGNORE插入emAssignedTagsForEvent SET eventId ='2',defaultTagId ='1';
pitu

8
看起来像是插入和更新语法的混合体。你是说INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);
Hobo 2014年

@Hobo从MySQL手册dev.mysql.com/doc/refman/5.7/en/insert.html插入语句:它可以是INSERT [...] IGNORE INTO mytable VALUES ...或者INSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...
Shirkam

“如果使用IGNORE修饰符,则在执行INSERT语句时发生的错误将被忽略。” 所以基本上您什么都没解决。
MarceloAgimóvel17年

18

您可以轻松使用以下方式:

INSERT INTO ... ON DUPLICATE KEY UPDATE ...

这样,您可以插入任何新的原始数据,如果有重复的数据,请替换特定的列(最好的列是timestamps)。
例如 :

CREATE TABLE IF NOT EXISTS Devices (
  id         INT(6)       NOT NULL AUTO_INCREMENT,
  unique_id  VARCHAR(100) NOT NULL PRIMARY KEY,
  created_at VARCHAR(100) NOT NULL,
  UNIQUE KEY unique_id (unique_id),
  UNIQUE KEY id (id)
)
  CHARACTER SET utf8
  COLLATE utf8_unicode_ci;

INSERT INTO Devices(unique_id, time) 
VALUES('$device_id', '$current_time') 
ON DUPLICATE KEY UPDATE time = '$current_time';

13

如果您确实无法在表上获得唯一索引,则可以尝试...

INSERT INTO table_listnames (name, address, tele)
    SELECT 'Rupert', 'Somewhere', '022'
        FROM some_other_table
        WHERE NOT EXISTS (SELECT name
                              FROM table_listnames
                              WHERE name='Rupert')
        LIMIT 1;

12

为了克服类似的问题,我将表修改为具有唯一列。使用您的示例,在创建时,我将得到以下内容:

name VARCHAR(20),
UNIQUE (name)

然后在插入查询时使用以下查询:

INSERT IGNORE INTO train
set table_listnames='Rupert'

我更喜欢这种解决方案。
Ilyas karim

9

此查询工作良好:

INSERT INTO `user` ( `username` , `password` )
    SELECT * FROM (SELECT 'ersks', md5( 'Nepal' )) AS tmp
    WHERE NOT EXISTS (SELECT `username` FROM `user` WHERE `username` = 'ersks' 
    AND `password` = md5( 'Nepal' )) LIMIT 1

您可以使用以下查询创建表:

CREATE TABLE IF NOT EXISTS `user` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `username` varchar(30) NOT NULL,
    `password` varchar(32) NOT NULL,
    `status` tinyint(1) DEFAULT '0',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

注意:在尝试使用第一个查询之前,请使用第二个查询创建表。


md5密码哈希...。
乍得·格兰特(

4

布莱恩·胡珀(Brian Hooper):您几乎达到了要点,但是您的synatx出现了错误。您的插入内容将永远无法使用。我在数据库上进行了测试,这是正确的答案:

INSERT INTO podatki (datum,ura,meritev1,meritev1_sunki,impulzi1,meritev2,meritev2_sunki,impulzi2)
            SELECT '$datum', '$ura', '$meritve1','$sunki1','$impulzi1','$meritve2','$sunki2','$impulzi2'
                FROM dual
                WHERE NOT EXISTS (SELECT datum,ura
                                      FROM podatki
                                      WHERE datum='$datum' and ura='$ura'

我给你我的y表的例子。插入几乎与Bian Hooper所写的相同,只是我将其他表中的select FROM DUAL ont放在了其中。亲切的问候,伊万


2
什么是“双重”?内置关键字?我只是看不出与Brian所说的不同...编辑:进一步的调查结果混杂且矛盾(?)。虽然SQL DUAL表页面说MySQL不支持DUAL表,但是MySQL手册说它支持DUAL表。我自己的测试表明unknown table status: TABLE_TYPE,尽管查询产生了结果,但由于它会提供消息,因此它没有。可能是因为MySQL不需要该FROM DUAL子句吗?
not2qubit

4
insert into customer_keyskill(customerID, keySkillID)
select  2,1 from dual
where not exists ( 
    select  customerID  from customer_keyskill 
    where customerID = 2 
    and keySkillID = 1 )

dual是不是MySQL的Oracle表
高度不规则

3

您要插入的不是更新结果。您可以在主列中定义名称列或将其设置为唯一。


3

我遇到了问题,而Mike建议的方法部分起作用,我出现了错误Dublicate Column name ='0',并以此更改了查询语法。

     $tQ = "INSERT  INTO names (name_id, surname_id, sum, sum2, sum3,sum4,sum5) 
                SELECT '$name', '$surname', '$sum', '$sum2', '$sum3','$sum4','$sum5' 
FROM DUAL
                WHERE NOT EXISTS (
                SELECT sum FROM names WHERE name_id = '$name' 
AND surname_id = '$surname') LIMIT 1;";

问题出在列名上。sum3等于sum4,并且mysql抛出了重复的列名,我以这种语法编写了代码,并且效果很好,


1
DUAL的所有者是SYS(SYS拥有数据字典,因此DUAL是数据字典的一部分。),但任何用户都可以访问DUAL。该表具有一个称为DUMMY的VARCHAR2(1)列,其值为'X'。MySQL允许在不需要任何表中数据的查询中将DUAL指定为表。DUAL表是一种特殊的单行单列表,默认情况下在Oracle和其他数据库安装中存在。在Oracle中,该表具有一个名为DUMMY的VARCHAR2(1)列,其值为'X'。它适用于选择伪列,例如SYSDATE或USER。
阿德南·海德

3

我有一个类似的问题,如果不存在,我需要插入多个。因此,从上面的示例中,我得出了这种组合...只是为了防止有人需要它。

注意:我必须在任何地方定义名称,因为MSSQL需要它。MySQL也可以使用*。

INSERT INTO names (name)
SELECT name
FROM
(
  SELECT name
  FROM
  (
     SELECT 'Test 4' as name
  ) AS tmp_single
  WHERE NOT EXISTS
  (
     SELECT name FROM names WHERE name = 'Test 4'
  )
  UNION ALL
  SELECT name
  FROM
  (
     SELECT 'Test 5' as name
  ) AS tmp_single
  WHERE NOT EXISTS
  (
     SELECT name FROM names WHERE name = 'Test 5'
  )
) tmp_all;

MySQL:CREATE TABLE namesOIDint(11)NOT NULL AUTO_INCREMENT, namevarchar(32)COLLATE utf8_unicode_ci NOT NULL,PRIMARY KEY(OID),UNIQUE KEY name_UNIQUEname))ENGINE = InnoDB AUTO_INCREMENT = 1;

要么

MSSQL:CREATE TABLE [names]([OID] INT IDENTITY(1,1)NOT NULL,[name] NVARCHAR(32)NOT NULL,PRIMARY KEY CLUSTERED([OID] ASC)); 在[名称]([名称] ASC)上创建唯一的非索引索引[Index_Names_Name];


2

该查询可以在PHP代码中使用。

我在此表中有一个ID列,因此我需要检查除此ID列以外的所有列的重复项:

#need to change values
SET @goodsType = 1, @sybType=5, @deviceId = asdf12345SDFasdf2345;    


INSERT INTO `devices` (`goodsTypeId`, `goodsId`, `deviceId`) #need to change tablename and columnsnames
SELECT * FROM (SELECT @goodsType, @sybType, @deviceId) AS tmp
WHERE NOT EXISTS (
    SELECT 'goodsTypeId' FROM `devices` #need to change tablename and columns names
    WHERE `goodsTypeId` = @goodsType
        AND `goodsId` = @sybType
        AND `deviceId` = @deviceId
) LIMIT 1;

现在仅在不存在以SET字符串配置值的行的情况下才添加新项

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.