【问题标题】:MySQL: Insert record if not exists in table [duplicate]MySQL:如果表中不存在则插入记录[重复]
【发布时间】:2011-03-11 00:29:43
【问题描述】:

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

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

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

【问题讨论】:

  • 所有当前对此或受骗者的答案都假定您可以添加唯一索引。有时决策是基于无法强加于整个表的业务逻辑。例如,您允许在一列中有多个具有某个值的行,但该列中的另一个值将只允许出现在一行上。我们如何做到这一点?

标签: mysql


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    INSERT doesn't allow WHERE in the syntax

    您可以做什么:在应该唯一的字段 (name) 上创建一个 UNIQUE INDEX,然后使用:

    • 正常INSERT(如果名称已存在则处理错误)
    • INSERT IGNORE(如果名称已经存在,它将静默失败导致警告(而不是错误)
    • INSERT ... ON DUPLICATE KEY UPDATE(如果名称已经存在,则会在末尾执行UPDATEsee documentation

    【讨论】:

    • 如果你需要收到警告信息,你可以在之后做mysql> show warnings\G
    • ime,警告信息,在INSERT IGNORE的情况下,匹配^Duplicate entry '.+' for key '.+'$的正则表达式
    • 如果使用insert ignore,是否会忽略其他可能出现的非重复错误?
    • @gepex 是的,它会的。这是使用INSERT IGNOREIMO 的一个大问题。
    • 注意:如果唯一键约束中的一列可以为空,则此答案可能不是最佳选择!该约束有效,但您可能期望其他结果:) 插入 ("John Doe", NULL), ("John Doe", NULL) 会导致两行,尽管两个字段一起处于唯一键约束中。另见stackoverflow.com/questions/4081783/unique-key-with-nulls
    【解决方案3】:

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

    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  |
    +----+---------+-----------+------+
    

    【讨论】:

    • 感谢您的帮助。我的实际问题要复杂得多,列不能是唯一的,我不能依赖主键。但这正是我想要的。
    • @Piskvar:同意。 @Rupert:如果可能的话,您应该索引内部选择语句中引用的列(在这种情况下为name)。另请注意,您可以使用SELECT 'John', 'Doe', '022' FROM table_listnames,而不是SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp - 但仅当table_listnames 已经包含一行或多行时才有效。我怀疑速度有什么不同,所以这可能不是问题。
    • @VijayRamamurthy:这是有效的,因为您插入了 Select 语句的结果。仔细阅读查询 -> WHERE 语句属于 SELECT 查询。 Select 查询要么返回单个数据行(已插入数据),要么根本不返回数据(未插入任何内容)
    • 如果您想将相同的值两次插入不同的字段(例如内部选择中的SELECT 'Humbert', 'Humbert', '1'),这似乎不起作用。我收到了ERROR 1060 (42S21): Duplicate column name
    • @cburgmer 我遇到了同样的问题#1060 - Duplicate column name。你找到解决办法了吗?编辑:找到了。在每个值后面添加一个 AS:SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp
    【解决方案4】:

    如果实在无法得到表的唯一索引,可以试试……

    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;
    

    【讨论】:

      【解决方案5】:

      布赖恩胡珀: 您几乎达到了目的,但是您的语法有错误。您的插入将永远无法工作。我在我的数据库上进行了测试,这是正确的答案:

      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 表的例子。 Insert 和 Bian Hooper 写的几乎一样,只是我把 select FROM DUAL ont from other table 放在了。 Cind 问候,伊万

      【讨论】:

      • 什么是“双重”?内置关键字?我只是看不出与布赖恩所说的不同......编辑:进一步的调查得到了混合和矛盾的(?)结果。 SQL DUAL table 页面说 MySQL 不支持 DUAL 表,MySQL manual 说它支持。我自己的测试表明它没有,因为它给出了unknown table status: TABLE_TYPE 消息,尽管查询产生了结果。可能是因为 MySQL 不需要 FROM DUAL 子句?
      【解决方案6】:
      INSERT IGNORE INTO `mytable`
      SET `field0` = '2',
      `field1` = 12345,
      `field2` = 12678;
      

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

      ----Untested----
      

      【讨论】:

      • 它对我不起作用,INSERT IGNORE INTO emAssignedTagsForEvent SET eventId='2',defaultTagId='1';
      • 看起来像是插入和更新语法的混合体。你的意思是INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);
      • @Hobo 来自 MySQL 手册 dev.mysql.com/doc/refman/5.7/en/insert.html INSERT 语法:可以是 INSERT [...] IGNORE INTO mytable VALUES ...INSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...
      • “如果使用 IGNORE 修饰符,执行 INSERT 语句时发生的错误将被忽略。”。所以基本上你什么都没解决。
      【解决方案7】:

      工作过:

      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 数据库中测试。它在 MySQL 上运行良好并经过测试。
      • 我了解到“dual”在 MySQL 中可用,但在 MySQL 的开源分支 MariaDB 中不可用
      • 来自@HighlyIrregular 的维基百科链接“如果不需要表,其他几个数据库(包括 Microsoft SQL Server、MySQL、PostgreSQL、SQLite 和 Teradata)可以完全省略 FROM 子句。这避免了需要对于任何虚拟桌子。”在没有 FROM DUAL 的情况下,相同的查询似乎在 MySql(5.7,如果重要的话)中有效。
      • 在 MariaDB 中测试并运行良好!谢谢@MahbubTito
      • 这是最好的答案,你太棒了!
      【解决方案8】:

      为了克服类似的问题,我修改了表以具有唯一的列。使用你的例子,在创建时我会有类似的东西:

      name VARCHAR(20),
      UNIQUE (name)
      

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

      INSERT IGNORE INTO train
      set table_listnames='Rupert'
      

      【讨论】:

      • 我更喜欢这个解决方案。
      【解决方案9】:
      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 是 Oracle 表而不是 MySQL
      【解决方案10】:

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

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

      非常容易使用,因为您声明了一个唯一的主键(这里的值为 5)。

      【讨论】:

      • 为了防止它你需要创建一个唯一索引,更多信息在这里:link
      • 非常危险。除非您知道自己在做什么,否则不要尝试。实际上,根本不要尝试。检查其他答案。
      • 注意,这会删除任何匹配的行,然后重新插入它们,相同行为的更安全版本是使用 ON DUPLICTE KEY UPDATE,它更新数学行而不是删除并重新插入它们: dev.mysql.com/doc/refman/5.7/en/replace.html
      【解决方案11】:

      我遇到了一个问题,Mike 建议的方法部分有效,我有一个错误 Duplicate Column name = '0',并将查询的语法更改为 this`

           $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 抛出了重复的列名,我用这种语法编写了代码,它工作得很好,

      【讨论】:

      • DUAL 的所有者是 SYS(SYS 拥有数据字典,因此 DUAL 是数据字典的一部分。)但是任何用户都可以访问 DUAL。该表有一个名为 DUMMY 的 VARCHAR2(1) 列,其值为“X”。 MySQL 允许在不需要来自任何表的数据的查询中将 DUAL 指定为表。DUAL 表是一种特殊的单行单列表,默认情况下出现在 Oracle 和其他数据库安装中。在 Oracle 中,该表有一个名为 DUMMY 的 VARCHAR2(1) 列,其值为“X”。它适用于选择 SYSDATE 或 USER 等伪列。
      【解决方案12】:

      这个查询效果很好:

      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 ;
      

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

      【讨论】:

        【解决方案13】:

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

        注意: 我必须在任何地方定义名称,因为 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: 创建表names ( OID int(11) NOT NULL AUTO_INCREMENT, name varchar(32) 整理 utf8_unicode_ci 非空, 主键 (OID), 唯一键 name_UNIQUE (name) ) ENGINE=InnoDB AUTO_INCREMENT=1;

        MSSQL: 创建表 [名称] ( [OID] INT IDENTITY (1, 1) NOT NULL, [名称] NVARCHAR (32) 非空, 主键集群([OID] ASC) ); 在 [names]([name] ASC) 上创建唯一的非聚集索引 [Index_Names_Name];

        【讨论】:

          【解决方案14】:

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

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

          通过这种方式,您可以插入任何 new 原始数据,如果您有重复数据,请替换特定列(最好的列是时间戳)。
          例如:

          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';
          

          【讨论】:

            【解决方案15】:

            此查询可用于 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字符串中配置的值不存在的情况下才会添加新项目

            【讨论】:

              【解决方案16】:

              这不是答案,只是一个注释。如果插入的值是重复的,则类似accepted answer 中的查询不起作用,如下所示:

              INSERT INTO `addr` (`email`, `name`) 
                SELECT * FROM (SELECT 'user@domain.tld', 'user@domain.tld') AS tmp 
                WHERE NOT EXISTS (
                  SELECT `email` FROM `addr` WHERE `email` LIKE 'user@domain.tld'
                );
              
              Error
              SQL query: Copy Documentation
              
              MySQL said: Documentation
              
              #1060 - Duplicate column name 'user@domain.tld'
              

              相反,来自Mahbub Tito's answer 的查询工作正常:

              INSERT INTO `addr` (`email`, `name`) 
                SELECT 'user@domain.tld', 'user@domain.tld' 
                WHERE NOT EXISTS (
                  SELECT `email` FROM `addr` WHERE `email` LIKE 'user@domain.tld'
                );
              
              1 row inserted.
              

              在 MariaDB 中测试

              【讨论】:

                猜你喜欢
                • 2014-02-15
                • 1970-01-01
                • 2014-03-08
                • 2013-12-22
                • 1970-01-01
                • 2013-12-27
                • 1970-01-01
                相关资源
                最近更新 更多