MySQL auto_increment在MyISAM和InnoDB中提供不同的主键

gio*_*ano 2 mysql primary-key auto-increment

我有一个带有自动增量主键的表。如果我想更改一行并添加新行,我会根据引擎 MyISAM 和 InnoDB 获得不同的主键。原表是:

pk.. 动物代码 动物名称 动物类别 有效日期 到期日期
1... 1... 狗... 米... 2013-10-21.... 9999-12-31
2... 2... 青蛙... ..一...2013-10-21..... 9999-12-31

如果我使用 MyISAM 运行更新/插入代码,我会得到以下信息:

pk.. 动物代码 动物名称 动物类别 有效日期 到期日期
1... 1... 狗... 米... 2013-10-21.... 2013-10-20
2... 2... 青蛙... .. 一个... 2013-10-21..... 9999-12-31
3 ... 1... 狗..... 米... 2013-10-21..... 9999 -12-31
4... 3... 猫... 米... 2013-10-21..... 9999-12-31

并使用InnoDB:pk..animal_codeanimal_nameanimal_class effective_dateexpiry_date
1...1...dogg...m...2013-10-21....2013-10-20
2...2...青蛙..... 一个... 2013-10-21..... 9999-12-31
4 ... 1... 狗..... 米... 2013-10-21.. ... 9999-12-31
5... 3... 猫... 米... 2013-10-21..... 9999-12-31

MyISAM 和 InnoDB 之间的新主键有所不同。显然,这两个引擎背后有不同的枚举范式。我只是想知道在这种特殊情况下会发生什么,即为什么InnoDB会跳转一个主键。获得此结果的代码如下。
感谢您的任何答复。

更新(2013年10月22日):
我将代码放在sqlfiddle上:http://sqlfiddle.com/#!2
/30cae/1 可以更改场景,注释掉一个引擎并在另一个引擎中注释。
首先在左侧面板上运行“构建架​​构”
,然后单击左侧面板上的“运行 SQL
更改场景:第 10 行和第 11 行”查看结果:

    ) ENGINE=MyISAM
--  ) ENGINE=INNODB  
Run Code Online (Sandbox Code Playgroud)

PS:如果id之间有间隙,我没有问题,我只是想知道为什么InnoDB会跳转。

use test01;

DROP TABLE animals;
DROP TABLE stage;                 

/* (1) Create data base */
CREATE TABLE animals (
 pk MEDIUMINT NOT NULL AUTO_INCREMENT
 , animal_code    INTEGER NOT NULL
 , animal_name    CHAR(30) NOT NULL
 , animal_class     CHAR(30) NOT NULL
 , effective_date DATE NOT NULL
 , expiry_date    DATE NOT NULL
 , PRIMARY KEY (pk)
--    ) ENGINE=MyISAM
  ) ENGINE=INNODB
;


/* (2) Create a stage table */
CREATE TABLE stage (
 animal_code      INTEGER NOT NULL
 , animal_name    CHAR(30) NOT NULL
 , animal_class     CHAR(30) NOT NULL
) ENGINE=MyISAM
;

/* (3.1) First import */
INSERT INTO stage (animal_code, animal_name,animal_class) VALUES
(1,'dogg','m'),(2,'frog','a')
;

/* (3.2) Second import 
TRUNCATE stage;
INSERT INTO stage (animal_code, animal_name, animal_class) VALUES
(1,'dog','m'),(2,'frog','a'),(3,'cat','m')
;
*/

/* --- (4) - (6) are use as soon new data are loaded in table stage  --- */  
/* --- So, first insert (3.1) and run (4)-(6)                        --- */  
/* --- Then import new data (3.2) into stage and run (4)-(6)         --- */    

/* (4) expire the existing product         */
UPDATE 
  animals a
, stage   b
SET
  expiry_date = SUBDATE(CURRENT_DATE, 1)
WHERE
    a.animal_code = b.animal_code
    AND ( a.animal_name <> b.animal_name
         OR b.animal_class <> b.animal_class
        )
AND expiry_date = '9999-12-31'
;

/* (5) add a new row for the changing product    */                   
INSERT INTO animals 
SELECT
  NULL  
, b.animal_code
, b.animal_name
, b.animal_class
, CURRENT_DATE
, '9999-12-31'
FROM 
  animals a
, stage   b 
WHERE  a.animal_code = b.animal_code
       AND (a.animal_name <> b.animal_name
            OR b.animal_class <> b.animal_class
           )
       AND EXISTS 
             (SELECT * 
              FROM animals x 
              WHERE  b.animal_code = x.animal_code 
                     AND a.expiry_date = SUBDATE(CURRENT_DATE, 1) 
              )
       AND NOT EXISTS 
             (SELECT *
              FROM animals y 
              WHERE b.animal_code = y.animal_code 
                    AND y.expiry_date = '9999-12-31' 
              );

/* (6) add new product                                                   */

INSERT INTO animals
SELECT
  NULL  
, animal_code
, animal_name
, animal_class
, CURRENT_DATE
, '9999-12-31'
FROM stage  
WHERE animal_code NOT IN(
                        SELECT y.animal_code 
                        FROM animals x, stage y 
                        WHERE x.animal_code = y.animal_code 
                       );
Run Code Online (Sandbox Code Playgroud)

Dav*_*ett 5

使用自动增量可以保证的是(除非您用它来重置值)生成的下一个值将高于上一个值。它通常只是更高,但你永远不应该依赖这一点:除了简单地总是增加更多细节之外,这是未定义的行为。

至于为什么不同的表类型可能会有所不同:不同的表类型在锁定/事务/并发行为方面有所不同(myISAM 非常简单/幼稚,而 InnoDB 则远不那么简单,以便提供引用完整性和性能优势)。可能是 InnoDB 很谨慎并允许并发事务,当您插入动物详细信息中的行时(步骤 5)“保留”了它认为可能需要但高估的 ID,因此没有使用其中之一,留下了间隙。myISAM 可能只是在更新期间应用了表锁,因此不会发生并发,因此它不需要在并发插入操作时保留 ID(因此,如果它高估了输出的行数,则不会留下间隙) 。