避免插入失败以避免虚假的自动增量

时间:2012-05-15 11:44:55

标签: mysql sql innodb auto-increment

我的问题与Why does MySQL autoincrement increase on failed inserts?相同,但我不想增加id字段,而是希望重写导致我麻烦的INSERT查询。假设我的数据库包含两个字段idusername,其中id是主键,username是唯一键。我基本上在寻找能INSERT...IF NOT EXISTS的语法。现在,我做了

INSERT INTO `table`(`username`) 
    VALUES ('foo') 
    ON DUPLICATE KEY UPDATE `id`=`id`

我只有一个线程写入数据库,因此我不需要任何类型的并发保护。建议?

5 个答案:

答案 0 :(得分:5)

你应该用这个:

INSERT INTO tableX (username) 
  SELECT 'foo' AS username
  FROM dual
  WHERE NOT EXISTS
        ( SELECT *
          FROM tableX 
          WHERE username = 'foo'
        ) ;

如果要包含更多列的值:

INSERT INTO tableX (username, dateColumn) 
  SELECT 'foo'                       --- the aliases are not needed             
       , NOW()                       --- actually
  FROM dual
  WHERE NOT EXISTS
        ( SELECT *
          FROM tableX 
          WHERE username = 'foo'
        ) ;                      

答案 1 :(得分:3)

我认为你不能阻止计数器增加,因为答案中给出的原因是你所链接的问题。

您有三种选择:

  1. 使用跳过的标识符生活;你真的希望用64位吗?

  2. 在尝试INSERT之前检查是否存在现有记录:

    DELIMITER ;;
    
    IF NOT EXISTS (SELECT * FROM `table` WHERE username = 'foo') THEN
      INSERT INTO `table` (username) VALUES ('foo');
    END IF;;
    
    DELIMITER ;
    

    或者,更好的是,使用@ypercube建议的FROM dual WHERE NOT EXISTS ...表单。

  3. 每次插入后重置计数器。如果在存储过程中执行INSERT操作,则可以使用处理程序执行此操作以获取重复键错误:

    DELIMITER ;;
    
    CREATE PROCEDURE TEST(IN uname CHAR(16) CHARSET latin1) BEGIN
      DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' BEGIN
        -- WARNING: THIS IS NOT SAFE FOR CONCURRENT CONNECTIONS
        SET @qry = CONCAT(
          'ALTER TABLE `table` AUTO_INCREMENT = ',
          (SELECT MAX(id) FROM `table`)
        );
        PREPARE stmt FROM @qry;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @qry = NULL;
      END;
      INSERT INTO `table` (username) VALUES (uname);
    END;;
    DELIMITER ;
    

    请记住@ypercube在关于此策略的评论中提出的(有效)问题,您可以改为使用:

    SELECT AUTO_INCREMENT - 1
    FROM   INFORMATION_SCHEMA.TABLES
    WHERE  table_schema = 'db_name' AND table_name = 'table';
    

答案 2 :(得分:1)

您可以在插入失败后重置自动增量字段

ALTER TABLE table_name AUTO_INCREMENT = 1;

这不会将自动增量重置为1,但会将其重置为当前最大值加一

另请注意,与数据库连接的用户应具有对目标表的更改权限

答案 3 :(得分:0)

使用INSERT IGNORE INTO table。查看以下帖子

http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html

你可以这样做:

INSERT INTO `table`(`username`) 
VALUES ('foo')      
ON DUPLICATE KEY UPDATE `username`= 'foo' 

OR

INSERT IGNORE INTO `table`
SET `username` = 'foo'

答案 4 :(得分:0)

您也可以在插入之前检查该用户,如果之前插入了该用户,则会额外调用您的数据库,但是,如果该用户已经存在,您可以返回您找到的信息并避免伪插入。 / p>