MySQL: เหตุใด auto_increment จึง จำกัด เฉพาะแค่คีย์หลัก?


10

ฉันรู้ว่า MySQL จำกัด คอลัมน์ auto_increment ไว้ที่คีย์หลัก ทำไมนี้ ความคิดแรกของฉันคือมันเป็นข้อ จำกัด ด้านประสิทธิภาพเนื่องจากอาจมีบางตัวนับตารางบางแห่งที่ต้องถูกล็อคเพื่อรับค่านี้

ทำไมฉันจึงไม่มีคอลัมน์ auto_increment หลายคอลัมน์ในตารางเดียวกัน

ขอบคุณ


ฉันเพิ่งสังเกตเห็นว่าฉันลืมที่จะใช้ @pop ในการทำธุรกรรม ฉันลองใหม่อีกครั้งและโพสต์ไว้ที่ด้านล่างของคำตอบของฉัน !!!
RolandoMySQLDBA

ฉันชอบคำถามนี้เพราะทำให้ฉันคิดนอกกรอบกับ MySQL ซึ่งฉันไม่ได้ทำอะไรมากในคืนวันศุกร์ +1 !!!
RolandoMySQLDBA

คำตอบ:


9

ทำไมคุณต้องการมีคอลัมน์ auto_increment ที่ไม่ใช่คีย์หลัก

หากคุณต้องการให้คอลัมน์เป็น auto_increment ตามนิยามคุณจะไม่เก็บข้อมูลที่มีความหมายไว้ในคอลัมน์นั้น กรณีเดียวที่การเก็บข้อมูลที่ไม่มีความหมายเหมาะสมเป็นกรณีพิเศษที่คุณต้องการให้มีคีย์หลักสังเคราะห์ ในกรณีดังกล่าวการขาดข้อมูลเป็นประโยชน์เพราะไม่มีความเสี่ยงที่จะมีใครบางคนเข้ามาในอนาคตและต้องการเปลี่ยนข้อมูลเนื่องจากคุณลักษณะบางอย่างของเอนทิตีบางอย่างเปลี่ยนไป

การมีคอลัมน์ auto_increment หลายรายการในตารางเดียวกันดูเหมือนว่าจะแย่กว่าเดิม คอลัมน์ทั้งสองนั้นจะมีข้อมูลเหมือนกัน - มันถูกสร้างขึ้นโดยอัลกอริทึมเดียวกันและถูกเติมข้อมูลพร้อมกันหลังจากนั้นทั้งหมด ฉันคิดว่าคุณอาจเกิดขึ้นกับการใช้งานที่เป็นไปได้สำหรับพวกเขาที่จะซิงค์เล็กน้อยถ้ามีเซสชันที่เกิดขึ้นพร้อมกันมากพอ แต่ฉันไม่สามารถจินตนาการได้ว่าจะมีประโยชน์ในแอปพลิเคชันอย่างไร


มันเป็นคำถามเชิงทฤษฎีมากกว่า - ฉันไม่มีประโยชน์ในการใช้คอลัมน์ auto_increment หลายอันฉันแค่อยากได้ยินคำอธิบายของประชาชนว่าทำไมมันเป็นไปไม่ได้ ขอบคุณที่สละเวลาตอบ! :)
Christopher Armstrong

2
"ทำไมคุณต้องการมีคอลัมน์ auto_increment ที่ไม่ใช่คีย์หลัก" - ฉันสามารถคิดถึงเหตุผลสองสามประการเป็นการส่วนตัว แต่นั่นคือ OT :-)
Denis de Bernardy

ฉันกำลังทำอะไรแบบนี้อยู่ตอนนี้ - และมันก็ไม่ใช่ข้อมูลที่ไร้ความหมาย ฉันต้องการทราบจำนวนระเบียนทั้งหมดที่เคยใส่ลงในตาราง แต่มีคีย์หลักที่มีประโยชน์มากกว่าอยู่แล้ว นี่เป็นการแก้ปัญหาดังเช่นทุกระเบียนใหม่ที่มีค่าซึ่งเป็น การเปรียบเทียบ (อ่อนแอ) จะเป็นข้อกำหนด "คุณคือลูกค้ารายที่ 10,000 ของเรา" ลูกค้าถูกลบเมื่อเวลาผ่านไปดังนั้น COUNT (*) จึงไม่ถูกต้อง
Cylindric

ทำไมคุณต้องการมีคอลัมน์ auto_increment ที่ไม่ใช่คีย์หลัก
phil_w

2
ทำไมคุณต้องการมีคอลัมน์ auto_increment ที่ไม่ใช่คีย์หลัก เหตุผลที่เป็นไปได้รวมถึง: เนื่องจาก PK ใช้เพื่อจัดเรียงแถวทางกายภาพด้วย ตัวอย่าง: สมมติว่าคุณเก็บข้อความไว้สำหรับผู้ใช้ คุณต้องอ่านข้อความทั้งหมดต่อผู้ใช้ดังนั้นคุณจึงต้องการให้พวกเขาอยู่ด้วยกันเพื่อการดึงที่มีประสิทธิภาพ เนื่องจาก innodb จัดเรียงพวกเขาด้วย PK คุณอาจต้องการทำเช่นนั้น: สร้างข้อความตาราง (ผู้ใช้, id, txt, คีย์หลัก (ผู้ใช้, id))
phil_w

8

อันที่จริงแล้วแอตทริบิวต์ AUTO_INCREMENT ไม่ได้ จำกัด อยู่ที่คีย์หลัก (อีกต่อไป) มันเคยเป็นเช่นนั้นในรุ่นเก่า - แน่นอน 3.23 และอาจ 4.0 ยังคงคู่มือ MySQLสำหรับทุกรุ่นตั้งแต่ 4.1 อ่านเช่นนี้

อาจมีคอลัมน์ AUTO_INCREMENT เพียงคอลัมน์เดียวต่อตารางซึ่งจะต้องมีการทำดัชนีและไม่สามารถมีค่า DEFAULT ได้

ดังนั้นคุณสามารถมีคอลัมน์ AUTO_INCREMENT ในตารางที่ไม่ใช่คีย์หลัก ถ้าเป็นเช่นนั้นก็เป็นหัวข้อที่ต่างออกไป

ฉันควรจะพูดถึงว่าคอลัมน์ AUTO_INCREMENT ควรเป็นประเภทจำนวนเต็มเสมอ (ในทางเทคนิคอนุญาตให้ใช้ประเภทจุดลอยตัวได้) และควรเป็น UNSIGNED ประเภทที่ลงนามไม่เพียง แต่ทำให้เสียพื้นที่ว่างเพียงครึ่งเดียว แต่ยังสามารถนำไปสู่ปัญหาใหญ่หากค่าลบถูกแทรกโดยอุบัติเหตุ

ในที่สุด MySQL 4.1 และใหม่กว่ากำหนดประเภทนามแฝงSERIALสำหรับ BIGINT UNSIGNED ไม่ใช่ NULL AUTO_INCREMENT UNIQUE UNIQUE


1
+1 เนื่องจากข้อเท็จจริงที่ว่า auto_increment ไม่ได้ จำกัด อยู่ที่ PKs เท่านั้น ไม่แน่ใจว่าทำไมคุณคิดว่าการใช้ตัวเลขลบสำหรับคีย์ตัวแทนจะนำไปสู่ ​​"ปัญหาใหญ่" ได้
nvogel

4

นี่เป็นคำถามที่น่าสนใจเพราะฐานข้อมูลต่างกันมีวิธีการเฉพาะสำหรับการให้บริการ auto_increment

MySQL : มีการสร้างคีย์ auto_increment เพียงคีย์เดียวเพื่อระบุแถวในตาราง ทำไมไม่มีคำอธิบายมากมาย แต่เป็นเพียงการนำไปใช้งาน ค่า auto_increment ได้รับการแก้ไขตามความยาวของประเภทข้อมูลเป็นไบต์:

  • Max TINYINT คือ 127
  • Max UNSIGNED TINTINT คือ 255
  • Max INT คือ 2147483647
  • Max UNSIGNED INT คือ 4294967295

PostgreSQL

อนุกรมประเภทข้อมูลภายในใช้สำหรับการเพิ่มอัตโนมัติจาก 1 ถึง 2,147,483,647 อนุญาตให้ใช้ช่วงที่ใหญ่กว่าได้โดยใช้ bigserial

Oracle : วัตถุสคีที่เรียกว่า SEQUENCE สามารถสร้างตัวเลขใหม่ได้โดยเพียงแค่เรียกใช้ฟังก์ชันช่วงต่อไป PostgreSQL ยังมีกลไกดังกล่าว

นี่คือ URL ที่ดีที่แสดงวิธีที่ DBs อื่นระบุ: http://www.w3schools.com/sql/sql_autoincrement.asp

ตอนนี้เกี่ยวกับคำถามของคุณหากคุณต้องการมีคอลัมน์ auto_increment หลายคอลัมน์ในตารางเดียวคุณจะต้องจำลองมัน

เหตุผลสองประการที่คุณต้องเลียนแบบสิ่งนี้:

  1. MySQL รองรับเพียงหนึ่งคอลัมน์ที่เพิ่มขึ้นต่อตารางเช่นเดียวกับ PostgreSQL, Oracle, SQL Server และ MS Access
  2. MySQL ไม่มีวัตถุสคีคี SEQUENCE เช่น Oracle และ PostgreSQL

คุณจะเลียนแบบได้อย่างไร ???

ใช้หลายตารางที่มีคอลัมน์ 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;

สิ่งนี้จะสร้างตารางแบบทดสอบป๊อปสำหรับครู ฉันยังสร้างอีมูเลเตอร์ลำดับห้าตัวหนึ่งชุดสำหรับแต่ละวันในสัปดาห์ของโรงเรียน แต่ละอีมูเลเตอร์ลำดับทำงานโดยการใส่ค่า 0 ในคอลัมน์ val หากอีมูเลเตอร์ลำดับว่างเปล่ามันจะเริ่มต้นด้วย val 0, nextval 1 หากไม่ใช่คอลัมน์ 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

@a_horse_with_no_name: ขออภัยในความผิดพลาด ฉันยังคงเป็นนักเดินทางด้วย postgresql ฉันจะอัปเดตคำตอบของฉันในภายหลัง ฉันกำลังตอบคำถามอยู่บนถนนจาก iPhone ขอให้มีความสุขในวันนี้!
RolandoMySQLDBA

0

XL บอกว่ามันไม่ได้ จำกัด อยู่แค่คีย์หลัก เป็นข้อ จำกัด ที่เป็นไปได้ที่คุณสามารถมีหนึ่งคอลัมน์ดังกล่าวต่อตารางเท่านั้น แต่วิธีการแก้ไขที่ดีที่สุดคือการสร้างตัวเลขจำนวนมากที่คุณต้องการในตารางอื่นจากนั้นแทรกคอลัมน์ที่คุณต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.