MySQL: Tại sao auto_increment chỉ giới hạn ở các khóa chính?


10

Tôi biết MySQL giới hạn các cột auto_increment cho các khóa chính. Tại sao lại thế này? Suy nghĩ đầu tiên của tôi là đó là một hạn chế về hiệu suất, vì có lẽ có một số bảng truy cập ở đâu đó phải được khóa để có được giá trị này.

Tại sao tôi không thể có nhiều cột auto_increment trong cùng một bảng?

Cảm ơn.


Tôi chỉ nhận thấy rằng tôi đã quên sử dụng @pop trong các giao dịch. Tôi đã lấy lại ví dụ và đăng nó ở cuối câu trả lời của tôi !!!
RolandoMySQLDBA

Tôi thích câu hỏi này vì nó khiến tôi phải suy nghĩ kỹ với MySQL mà tôi không làm gì nhiều vào tối thứ Sáu. +1 !!!
RolandoMySQLDBA

Câu trả lời:


9

Tại sao bạn muốn có một cột auto_increment không phải là khóa chính?

Nếu bạn muốn một cột là auto_increment, theo định nghĩa, bạn không lưu trữ dữ liệu có ý nghĩa trong cột đó. Trường hợp duy nhất mà việc lưu trữ thông tin không có ý nghĩa có ý nghĩa là trường hợp đặc biệt mà bạn muốn có khóa chính tổng hợp. Trong trường hợp đó, việc thiếu thông tin là một lợi ích vì không có rủi ro ai đó sẽ xuất hiện trong tương lai và muốn thay đổi dữ liệu vì một số thuộc tính của một số thực thể đã thay đổi.

Có nhiều cột auto_increment trong cùng một bảng dường như thậm chí còn lạ hơn. Hai cột sẽ có cùng một dữ liệu - chúng được tạo bởi cùng một thuật toán và được tạo ra cùng một lúc. Tôi cho rằng bạn có thể đưa ra một triển khai trong đó có thể chúng hơi không đồng bộ nếu có đủ các phiên đồng thời. Nhưng tôi không thể tưởng tượng nó sẽ hữu ích như thế nào trong một ứng dụng.


Đó là một câu hỏi lý thuyết nhiều hơn - Tôi không có cách sử dụng thực tế để có nhiều cột auto_increment, tôi chỉ muốn nghe giải thích của mọi người về lý do tại sao điều đó là không thể. Cảm ơn đã dành thời gian trả lời! :)
Christopher Armstrong

2
"Tại sao bạn muốn có một cột auto_increment không phải là khóa chính?" - Tôi có thể nghĩ ra một vài lý do, cá nhân. Nhưng đó là OT. :-)
Denis de Bernardy

Bây giờ tôi đang làm một cái gì đó như thế này - và đó không phải là dữ liệu vô nghĩa. Tôi cần biết một số lượng tất cả các bản ghi từng được chèn vào một bảng, nhưng đã có các khóa chính hữu ích hơn. Điều này giải quyết rằng, như mọi bản ghi mới có một giá trị của nó. Sự tương tự (yếu) sẽ là yêu cầu "bạn là khách hàng thứ 10.000 của chúng tôi". Khách hàng bị xóa theo thời gian, do đó, COUNT (*) không hợp lệ.
Hình trụ

Tại sao bạn muốn có một cột auto_increment không phải là khóa chính?
phil_w

2
Tại sao bạn muốn có một cột auto_increment không phải là khóa chính? Lý do có thể bao gồm: Bởi vì PK cũng được sử dụng để sắp xếp các hàng vật lý. Ví dụ: Giả sử bạn lưu trữ tin nhắn cho người dùng. Bạn cần đọc tất cả các tin nhắn trên mỗi người dùng, vì vậy bạn muốn có chúng cùng nhau để truy xuất hiệu quả. Vì innodb sắp xếp chúng theo PK, bạn có thể muốn làm điều đó: tạo thông điệp bảng (người dùng, id, txt, khóa chính (người dùng, id))
phil_w

8

Trong thực tế, thuộc tính AUTO_INCREMENT không bị giới hạn ở KHÓA CHÍNH (bất kỳ nữa). Nó đã từng như vậy trong các phiên bản cũ - chắc chắn là 3.23 và có thể là 4.0. Vẫn là hướng dẫn sử dụng MySQL cho tất cả các phiên bản kể từ 4.1 đọc như thế này

Chỉ có thể có một cột AUTO_INCREMENT trên mỗi bảng, nó phải được lập chỉ mục và nó không thể có giá trị DEFAULT.

Vì vậy, bạn thực sự có thể có một cột AUTO_INCREMENT trong một bảng không phải là khóa chính. Nếu điều đó có ý nghĩa, là một chủ đề khác nhau mặc dù.

Tôi cũng nên đề cập rằng một cột AUTO_INCREMENT phải luôn là một kiểu số nguyên (về mặt kỹ thuật cũng là một loại dấu phẩy động) và nó phải được CHẤP NHẬN. Một loại đã ký không chỉ lãng phí một nửa không gian chính, nó còn có thể dẫn đến các vấn đề lớn nếu một giá trị âm được chèn vào một cách tình cờ.

Cuối cùng, MySQL 4.1 và sau đó xác định một loại bí danh SERIAL cho BIGINT KHÔNG ĐƯỢC ĐĂNG KÝ KHÔNG NULL AUTO_INCREMENT UNIQUE.


1
+1 cho thực tế là auto_increment không giới hạn ở PK. Không chắc chắn lý do tại sao bạn nghĩ rằng sử dụng số âm cho khóa thay thế dẫn đến "vấn đề lớn".
nvogel

4

Đây là một câu hỏi thú vị bởi vì các cơ sở dữ liệu khác nhau có các cách tiếp cận duy nhất để cung cấp auto_increment.

MySQL : Chỉ có một khóa auto_increment được tạo để xác định duy nhất một hàng trong bảng. Không có nhiều lời giải thích đằng sau tại sao, nhưng chỉ thực hiện. Tùy thuộc vào kiểu dữ liệu, các giá trị auto_increment được cố định theo độ dài của kiểu dữ liệu theo byte:

  • Tối đa TINYINT là 127
  • TINTINT tối đa không giới hạn là 255
  • Tối đa INT là 2147483647
  • Tối đa INT KHÔNG ĐĂNG KÝ là 4294967295

PostgreSQL

Sê-ri kiểu dữ liệu nội bộ được sử dụng để tăng tự động từ 1 đến 2.147.483.647. Phạm vi lớn hơn được phép sử dụng bigserial.

Oracle : Đối tượng lược đồ có tên SEQUENCE có thể tạo các số mới bằng cách đơn giản là triệu tập hàm nextval. PostgreSQL cũng có một cơ chế như vậy.

Đây là một URL đẹp cung cấp cách thức các DB khác chỉ định chúng: http://www.w3schools.com/sql/sql_autoincrement.asp

Bây giờ liên quan đến câu hỏi của bạn, nếu bạn thực sự muốn có nhiều cột auto_increment trong một bảng, bạn sẽ phải mô phỏng điều đó.

Hai lý do tại sao bạn phải thi đua này:

  1. MySQL chỉ chứa một cột tăng trên mỗi bảng như PostgreSQL, Oracle, SQL Server và MS Access.
  2. MySQL không có đối tượng lược đồ SEQUENCE như Oracle và PostgreSQL.

Làm thế nào bạn sẽ mô phỏng nó ???

Sử dụng nhiều bảng chỉ có một cột auto_increment và ánh xạ chúng tới các cột mong muốn trong các bảng đích. Đây là một ví dụ:

Sao chép và dán ví dụ này:

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;

Điều này sẽ tạo ra một bảng các câu đố pop cho giáo viên. Tôi cũng đã tạo ra năm trình giả lập trình tự, một cho mỗi ngày trong tuần học. Mỗi trình giả lập trình tự hoạt động bằng cách chèn giá trị 0 vào cột val. Nếu trình giả lập chuỗi trống, nó bắt đầu bằng val 0, nextval 1. Nếu không, cột nextval được tăng lên. Sau đó, bạn có thể tìm nạp cột nextval từ trình giả lập trình tự.

Dưới đây là kết quả mẫu từ ví dụ:

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>

Nếu bạn thực sự cần nhiều giá trị gia tăng tự động trong MySQL, đây là cách gần nhất để mô phỏng nó.

Hãy thử một lần !!!

CẬP NHẬT 2011-06-23 21:05

Tôi chỉ nhận thấy trong ví dụ của mình Tôi không sử dụng giá trị @pop.

Lần này tôi đã thay thế 'pop_tue = pop_tue + 1' bằng 'pop_tue = @pop' và thử lại ví dụ:

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>

Tóm tắt của bạn không hoàn toàn chính xác: PostgreSQL hỗ trợ bất kỳ số lượng cột tăng tự động (nối tiếp) nào, giống như Oracle (trong cả hai trường hợp các cột được điền với một giá trị từ một chuỗi). PostgreSQL cũng cung cấp bigserialloại dữ liệu cung cấp phạm vi lớn hơn nhiều so với 2.147.483.647
a_horse_with_no_name

@a_horse_with_no_name: xin lỗi vì sự giám sát. Tôi vẫn là một người hành trình với postgresql. Tôi sẽ cập nhật câu trả lời của tôi sau. Tôi đang trên đường trả lời từ iPhone. Chúc bạn ngày mới tốt lành!
RolandoMySQLDBA

0

Như XL nói, nó không giới hạn chỉ các khóa chính. Đó là một hạn chế tiềm năng là bạn chỉ có thể có một cột như vậy trên mỗi bảng nhưng cách giải quyết tốt nhất là tạo ra bao nhiêu số bạn cần trong một bảng khác và sau đó chèn chúng vào nơi bạn cần.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.