MySQL:为什么auto_increment仅限于主键?


10

我知道MySQL将auto_increment列限制为主键。为什么是这样?我的第一个想法是这是对性能的限制,因为在某个位置可能必须锁定一些计数器表才能获得此值。

为什么同一张表中不能有多个auto_increment列?

谢谢。


我只是注意到我忘了在交易中使用@pop。我重试了示例,并将其张贴在答案的底部!
RolandoMySQLDBA 2011年

我喜欢这个问题,因为它使我想到了MySQL的即用型功能,而在周五的晚上我做得并不多。+1 !!!
RolandoMySQLDBA 2011年

Answers:


9

为什么要有一个不是主键的auto_increment列?

根据定义,如果您希望一列是auto_increment,则不会在该列中存储有意义的数据。唯一有意义的存储无意义信息的情况是您希望拥有合成主键的特殊情况。在那种情况下,信息的缺乏是有好处的,因为没有风险,因为将来某个实体会改变某些实体的某些属性,因此将来有人会出现这种情况。

在同一表中具有多个auto_increment列似乎更奇怪。两列将具有相同的数据-它们是由相同的算法生成的,并且毕竟是在同一时间填充的。我想您可以提出一个实现,如果有足够的并发会话,它们可能会稍微不同步。但是我无法想象这在应用程序中将如何有用。


这更多是一个理论上的问题-我没有用于多个auto_increment列的实际用途,我只是想听听人们对于为什么不可能实现的解释。感谢您抽出宝贵的时间来回答!:)
Christopher Armstrong

2
“为什么要有一个不是主键的auto_increment列?” -我个人可以想到一些原因。但这是旧约。:-)
Denis de Bernardy 2011年

我现在正在做这样的事情-它不是毫无意义的数据。我需要知道插入到表中的所有记录的计数,但是已经有了更多有用的主键。这就解决了,因为每个新记录都具有它的值。(弱)类比将是“您是我们的第10,000个客户”的要求。客户会随着时间的流逝而被删除,因此COUNT(*)无效。
Cylindric

为什么要有一个不是主键的auto_increment列?
phil_w 2014年

2
为什么要有一个不是主键的auto_increment列?可能的原因包括:因为PK也用于物理排序行。示例:假设您为用户存储消息。您需要阅读每个用户的所有消息,因此希望将它们放在一起以进行有效的检索。由于innodb通过PK对它们进行排序,因此您可能需要这样做:创建表消息(用户,标识,txt,主键(用户,标识))
phil_w 2014年

8

实际上,AUTO_INCREMENT属性不仅限于PRIMARY KEY(不再是)。在旧版本中曾经如此-肯定是3.23,可能是4.0。自4.1以来的所有版本的MySQL手册仍然是这样的

每个表只能有一个AUTO_INCREMENT列,必须对其建立索引,并且不能具有DEFAULT值。

因此,您确实可以在不是主键的表中包含一个AUTO_INCREMENT列。如果这有意义,则是另一个话题。

我还应该提到,AUTO_INCREMENT列应始终为整数类型(技术上也允许使用浮点类型),并且应为UNSIGNED。SIGNED类型不仅浪费键空间的一半,而且如果偶然插入负值也会导致巨大的问题。

最后,MySQL 4.1和更高版本为BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE 定义了类型别名SERIAL


1
+1是因为auto_increment不仅限于PK。虽然不确定为什么您认为使用负数作为替代键会导致“巨大问题”。
nvogel 2011年

4

这是一个有趣的问题,因为不同的数据库具有提供auto_increment的独特方法。

MySQL:仅生成一个auto_increment键来唯一标识表中的一行。为什么没有很多解释,只是实现。根据数据类型,auto_increment值由数据类型的长度(以字节为单位)固定:

  • 最大TINYINT是127
  • 最大未签名色调为255
  • 最大INT为2147483647
  • 最大未签名整数是4294967295

PostgreSQL的

内部数据类型串行用于从1到2,147,483,647的自动递增。使用bigserial允许更大的范围。

Oracle:名为SEQUENCE的模式对象可以通过简单地调用nextval函数来创建新数字。PostgreSQL也有这样的机制。

这是一个很好的URL,它提供了其他DB如何指定它们的方法:http : //www.w3schools.com/sql/sql_autoincrement.asp

现在关于您的问题,如果您真的想在单个表中具有多个auto_increment列,则必须进行模拟。

必须模仿的两个原因:

  1. MySQL与PostgreSQL,Oracle,SQL Server和MS Access一样,每个表仅容纳一个增量列。
  2. MySQL没有像Oracle和PostgreSQL这样的SEQUENCE模式对象。

您将如何模仿它?

使用只有一个auto_increment列的多个表,并将它们映射到目标表中的所需列。这是一个例子:

复制并粘贴此示例:

use test
DROP TABLE IF EXISTS teacher_popquizzes;
CREATE TABLE teacher_popquizzes
(
    teacher varchar(20) not null,
    class varchar(20) not null,
    pop_mon INT NOT NULL DEFAULT 0,
    pop_tue INT NOT NULL DEFAULT 0,
    pop_wed INT NOT NULL DEFAULT 0,
    pop_thu INT NOT NULL DEFAULT 0,
    pop_fri INT NOT NULL DEFAULT 0,
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
INSERT INTO teacher_popquizzes (teacher,class) VALUES
('mr jackson','literature'),
('mrs andrews','history'),
('miss carroll','spelling');
DROP TABLE IF EXISTS mon_seq;
DROP TABLE IF EXISTS tue_seq;
DROP TABLE IF EXISTS wed_seq;
DROP TABLE IF EXISTS thu_seq;
DROP TABLE IF EXISTS fri_seq;
CREATE TABLE mon_seq
(
    val INT NOT NULL DEFAULT 0,
    nextval INT NOT NULL DEFAULT 1,
    PRIMARY KEY (val)
);
CREATE TABLE tue_seq LIKE mon_seq;
CREATE TABLE wed_seq LIKE mon_seq;
CREATE TABLE thu_seq LIKE mon_seq;
CREATE TABLE fri_seq LIKE mon_seq;
BEGIN;
INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
SELECT nextval INTO @pop FROM mon_seq;
UPDATE teacher_popquizzes SET pop_tue = pop_tue + 1 WHERE id = 2;
COMMIT;
BEGIN;
INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
SELECT nextval INTO @pop FROM tue_seq;
UPDATE teacher_popquizzes SET pop_tue = pop_tue + 1 WHERE id = 1;
COMMIT;
BEGIN;
INSERT INTO wed_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
SELECT nextval INTO @pop FROM wed_seq;
UPDATE teacher_popquizzes SET pop_wed = pop_wed + 1 WHERE id = 2;
COMMIT;
SELECT * FROM teacher_popquizzes;

这将为教师创建一个流行测验表。我还创建了五个序列模拟器,每个上学日每天一个。每个序列仿真器通过在val列中插入值0来工作。如果序列仿真器为空,则以val 0,nextval 1开头。如果不是,则nextval列递增。然后,您可以从序列仿真器中获取nextval列。

这是示例的示例结果:

mysql> CREATE TABLE teacher_popquizzes
    -> (
    ->     teacher varchar(20) not null,
    ->     class varchar(20) not null,
    ->     pop_mon INT NOT NULL DEFAULT 0,
    ->     pop_tue INT NOT NULL DEFAULT 0,
    ->     pop_wed INT NOT NULL DEFAULT 0,
    ->     pop_thu INT NOT NULL DEFAULT 0,
    ->     pop_fri INT NOT NULL DEFAULT 0,
    ->     id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
    -> );
Query OK, 0 rows affected (0.11 sec)

mysql> INSERT INTO teacher_popquizzes (teacher,class) VALUES
    -> ('mr jackson','literature'),
    -> ('mrs andrews','history'),
    -> ('miss carroll','spelling');
Query OK, 3 rows affected (0.03 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> DROP TABLE IF EXISTS mon_seq;
Query OK, 0 rows affected (0.06 sec)

mysql> DROP TABLE IF EXISTS tue_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> DROP TABLE IF EXISTS wed_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> DROP TABLE IF EXISTS thu_seq;
Query OK, 0 rows affected (0.05 sec)

mysql> DROP TABLE IF EXISTS fri_seq;
Query OK, 0 rows affected (0.07 sec)

mysql> CREATE TABLE mon_seq
    -> (
    ->     val INT NOT NULL DEFAULT 0,
    ->     nextval INT NOT NULL DEFAULT 1,
    ->     PRIMARY KEY (val)
    -> );
Query OK, 0 rows affected (0.12 sec)

mysql> CREATE TABLE tue_seq LIKE mon_seq;
Query OK, 0 rows affected (0.09 sec)

mysql> CREATE TABLE wed_seq LIKE mon_seq;
Query OK, 0 rows affected (0.08 sec)

mysql> CREATE TABLE thu_seq LIKE mon_seq;
Query OK, 0 rows affected (0.07 sec)

mysql> CREATE TABLE fri_seq LIKE mon_seq;
Query OK, 0 rows affected (0.14 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT nextval INTO @pop FROM mon_seq;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_tue = pop_tue + 1 WHERE id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
Query OK, 2 rows affected (0.00 sec)

mysql> SELECT nextval INTO @pop FROM tue_seq;
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_tue = pop_tue + 1 WHERE id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO wed_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT nextval INTO @pop FROM wed_seq;
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_wed = pop_wed + 1 WHERE id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)

mysql> SELECT * FROM teacher_popquizzes;
+--------------+------------+---------+---------+---------+---------+---------+----+
| teacher      | class      | pop_mon | pop_tue | pop_wed | pop_thu | pop_fri | id |
+--------------+------------+---------+---------+---------+---------+---------+----+
| mr jackson   | literature |       0 |       1 |       0 |       0 |       0 |  1 |
| mrs andrews  | history    |       0 |       1 |       1 |       0 |       0 |  2 |
| miss carroll | spelling   |       0 |       0 |       0 |       0 |       0 |  3 |
+--------------+------------+---------+---------+---------+---------+---------+----+
3 rows in set (0.00 sec)

mysql>

如果在MySQL中确实需要多个自动增量值,则这是模拟它的最接近方法。

试试看 !!!

更新2011-06-23 21:05

我只是在示例中注意到我不使用@pop值。

这次我将'pop_tue = pop_tue + 1'替换为'pop_tue = @pop'并重试了示例:

mysql> use test
Database changed
mysql> DROP TABLE IF EXISTS teacher_popquizzes;
Query OK, 0 rows affected (0.05 sec)

mysql> CREATE TABLE teacher_popquizzes
    -> (
    ->     teacher varchar(20) not null,
    ->     class varchar(20) not null,
    ->     pop_mon INT NOT NULL DEFAULT 0,
    ->     pop_tue INT NOT NULL DEFAULT 0,
    ->     pop_wed INT NOT NULL DEFAULT 0,
    ->     pop_thu INT NOT NULL DEFAULT 0,
    ->     pop_fri INT NOT NULL DEFAULT 0,
    ->     id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> INSERT INTO teacher_popquizzes (teacher,class) VALUES
    -> ('mr jackson','literature'),
    -> ('mrs andrews','history'),
    -> ('miss carroll','spelling');
Query OK, 3 rows affected (0.03 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> DROP TABLE IF EXISTS mon_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> DROP TABLE IF EXISTS tue_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> DROP TABLE IF EXISTS wed_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> DROP TABLE IF EXISTS thu_seq;
Query OK, 0 rows affected (0.01 sec)

mysql> DROP TABLE IF EXISTS fri_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> CREATE TABLE mon_seq
    -> (
    ->     val INT NOT NULL DEFAULT 0,
    ->     nextval INT NOT NULL DEFAULT 1,
    ->     PRIMARY KEY (val)
    -> );
Query OK, 0 rows affected (0.08 sec)

mysql> CREATE TABLE tue_seq LIKE mon_seq;
Query OK, 0 rows affected (0.09 sec)

mysql> CREATE TABLE wed_seq LIKE mon_seq;
Query OK, 0 rows affected (0.13 sec)

mysql> CREATE TABLE thu_seq LIKE mon_seq;
Query OK, 0 rows affected (0.11 sec)

mysql> CREATE TABLE fri_seq LIKE mon_seq;
Query OK, 0 rows affected (0.08 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;

Query OK, 1 row affected (0.01 sec)

mysql> SELECT nextval INTO @pop FROM tue_seq;
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_tue = @pop WHERE id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;

Query OK, 2 rows affected (0.00 sec)

mysql> SELECT nextval INTO @pop FROM tue_seq;
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_tue = @pop WHERE id = 1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO wed_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;

Query OK, 1 row affected (0.01 sec)

mysql> SELECT nextval INTO @pop FROM wed_seq;
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_wed = @pop WHERE id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM teacher_popquizzes;
+--------------+------------+---------+---------+---------+---------+---------+----+
| teacher      | class      | pop_mon | pop_tue | pop_wed | pop_thu | pop_fri | id |
+--------------+------------+---------+---------+---------+---------+---------+----+
| mr jackson   | literature |       0 |       2 |       0 |       0 |       0 |  1 |
| mrs andrews  | history    |       0 |       1 |       1 |       0 |       0 |  2 |
| miss carroll | spelling   |       0 |       0 |       0 |       0 |       0 |  3 |
+--------------+------------+---------+---------+---------+---------+---------+----+
3 rows in set (0.00 sec)

mysql>

您的总结并不完全正确:PostgreSQL支持任意数量的自动增量(序列)列,就像Oracle一样(在两种情况下,列均填充有序列中的值)。PostgreSQL还提供了一个bigserial数据类型,该数据类型提供的范围远远大于2,147,483,647
a_horse_with_no_name 2011年

@a_horse_with_no_name:很抱歉。我仍然是PostgreSQL的熟练手。稍后再更新我的答案。我正在从iPhone上获得答案。祝你有美好的一天!
RolandoMySQLDBA 2011年

0

正如XL所说,它不仅限于主键。一个潜在的限制是,每个表只能有一个这样的列,但是最好的解决方法是在另一个表中生成所需数量的数字,然后将其插入到需要的位置。

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.