MySQL: Mengapa auto_increment terbatas hanya pada kunci primer?

10

Saya tahu MySQL membatasi kolom auto_increment ke kunci primer. Kenapa ini? Pikiran pertama saya adalah pembatasan kinerja, karena mungkin ada beberapa meja penghitung di suatu tempat yang harus dikunci untuk mendapatkan nilai ini.

Mengapa saya tidak dapat memiliki beberapa kolom auto_increment di tabel yang sama?

Terima kasih.

Christopher Armstrong
sumber
Saya baru menyadari bahwa saya lupa menggunakan @pop dalam transaksi. Saya mencoba kembali contohnya dan mempostingnya di bagian bawah jawaban saya !!!
RolandoMySQLDBA
Saya suka pertanyaan ini karena itu membuat saya berpikir di luar kotak dengan MySQL yang saya tidak lakukan banyak pada hari Jumat malam. +1 !!!
RolandoMySQLDBA

Jawaban:

9

Mengapa Anda ingin memiliki kolom auto_increment yang bukan kunci utama?

Jika Anda ingin sebuah kolom menjadi auto_increment, menurut definisi, Anda tidak menyimpan data yang bermakna dalam kolom itu. Satu-satunya kasus di mana menyimpan informasi yang tidak bermakna masuk akal adalah kasus khusus yang Anda ingin memiliki kunci primer sintetis. Dalam hal itu, kurangnya informasi adalah keuntungan karena tidak ada risiko seseorang akan datang di masa depan dan ingin mengubah data karena beberapa atribut dari beberapa entitas berubah.

Memiliki beberapa kolom auto_increment di tabel yang sama tampaknya lebih aneh. Kedua kolom akan memiliki data yang sama - mereka dihasilkan oleh algoritma yang sama dan diisi pada saat yang sama. Saya kira Anda bisa membuat implementasi di mana mungkin bagi mereka untuk sedikit tidak sinkron jika ada cukup sesi bersamaan. Tapi saya tidak bisa membayangkan bagaimana itu akan berguna dalam suatu aplikasi.

Gua Justin
sumber
Itu lebih merupakan pertanyaan teoretis - Saya tidak memiliki penggunaan praktis untuk memiliki beberapa kolom auto_increment, saya hanya ingin mendengar penjelasan orang-orang mengapa itu tidak mungkin. Terima kasih telah meluangkan waktu untuk menjawab! :)
Christopher Armstrong
2
"Mengapa Anda ingin memiliki kolom auto_increment yang bukan kunci utama?" - Saya dapat memikirkan beberapa alasan, secara pribadi. Tapi itu PL. :-)
Denis de Bernardy
Saya melakukan sesuatu seperti ini sekarang - dan itu bukan data yang tidak berarti. Saya perlu tahu hitungan semua catatan yang pernah dimasukkan ke dalam tabel, tetapi sudah memiliki kunci primer yang lebih berguna. Ini memecahkan itu, karena setiap catatan baru memiliki nilai yang mana itu. Analogi (lemah) adalah persyaratan "Anda adalah pelanggan ke 10.000 kami". Pelanggan dihapus dari waktu ke waktu, jadi COUNT (*) tidak valid.
Silinder
Mengapa Anda ingin memiliki kolom auto_increment yang bukan kunci utama?
phil_w
2
Mengapa Anda ingin memiliki kolom auto_increment yang bukan kunci utama? Alasan yang mungkin meliputi: Karena PK juga digunakan untuk memesan baris secara fisik. Contoh: Misalkan Anda menyimpan pesan untuk pengguna. Anda perlu membaca semua pesan per pengguna, jadi Anda ingin membuatnya bersama untuk pengambilan yang efisien. Karena innodb mengurutkannya dengan PK, Anda mungkin ingin melakukan itu: membuat pesan tabel (pengguna, id, txt, kunci utama (pengguna, id))
phil_w
8

Sebenarnya atribut AUTO_INCREMENT tidak terbatas pada PRIMARY KEY (lagi). Dulu begitu dalam versi lama - pasti 3,23 dan mungkin 4.0. Masih manual MySQL untuk semua versi sejak 4.1 berbunyi seperti ini

Hanya ada satu kolom AUTO_INCREMENT per tabel, itu harus diindeks, dan itu tidak dapat memiliki nilai DEFAULT.

Jadi Anda memang dapat memiliki kolom AUTO_INCREMENT dalam tabel yang bukan kunci utama. Jika itu masuk akal, adalah topik yang berbeda.

Saya juga harus menyebutkan bahwa kolom AUTO_INCREMENT harus selalu menjadi tipe integer (secara teknis tipe floating point juga diperbolehkan) dan harus TIDAK DITANDATANGANI. Tipe yang DITANDATANGANI tidak hanya membuang setengah ruang utama, tetapi juga dapat menyebabkan masalah besar jika nilai negatif dimasukkan secara tidak sengaja.

Akhirnya MySQL 4.1 dan yang lebih baru mendefinisikan jenis alias SERIAL untuk BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE.

XL
sumber
1
Memberi +1 untuk fakta bahwa auto_increment tidak terbatas pada PK. Tidak yakin mengapa Anda berpikir menggunakan angka negatif untuk kunci pengganti mengarah ke "masalah besar".
nvogel
4

Ini adalah pertanyaan yang menarik karena database yang berbeda memiliki pendekatan unik untuk menyediakan auto_increment.

MySQL : Hanya satu kunci auto_increment yang dihasilkan untuk mengidentifikasi baris dalam tabel secara unik. Tidak banyak penjelasan di balik alasannya, tetapi hanya implementasi. Bergantung pada tipe data, nilai auto_increment ditetapkan oleh panjang tipe data dalam byte:

  • Max TINYINT adalah 127
  • TINTINT UNSIGNED Max adalah 255
  • Max INT adalah 2147483647
  • Max UNSIGNED INT adalah 4294967295

PostgreSQL

Seri datatype internal digunakan untuk kenaikan otomatis dari 1 menjadi 2.147.483.647. Rentang yang lebih besar diizinkan menggunakan bigserial.

Oracle : Objek skema yang disebut SEQUENCE dapat membuat angka baru dengan hanya memanggil fungsi nextval. PostgreSQL juga memiliki mekanisme seperti itu.

Berikut adalah URL yang bagus yang menyediakan bagaimana DB lain menentukannya: http://www.w3schools.com/sql/sql_autoincrement.asp

Sekarang mengenai pertanyaan Anda, jika Anda benar-benar ingin memiliki beberapa kolom auto_increment dalam satu tabel, Anda harus meniru itu.

Dua alasan mengapa Anda harus meniru ini:

  1. MySQL hanya mengakomodasi satu kolom kenaikan per tabel seperti halnya PostgreSQL, Oracle, SQL Server, dan MS Access.
  2. MySQL tidak memiliki objek skema SEQUENCE seperti Oracle dan PostgreSQL.

Bagaimana Anda meniru itu ???

Menggunakan beberapa tabel yang hanya memiliki satu kolom auto_increment dan memetakannya ke kolom yang diinginkan di tabel target. Berikut ini sebuah contoh:

Salin dan Tempel Contoh Ini:

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;

Ini akan membuat tabel kuis pop untuk guru. Saya juga membuat lima emulator urutan, satu untuk setiap hari dalam minggu sekolah. Setiap emulator urutan bekerja dengan memasukkan nilai 0 di kolom val. Jika urutan emulator kosong, itu dimulai dengan val 0, nextval 1. Jika tidak, kolom nextval bertambah. Anda kemudian dapat mengambil kolom nextval dari emulator urutan.

Berikut adalah contoh hasil dari contoh:

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>

Jika Anda benar-benar membutuhkan beberapa nilai kenaikan otomatis di MySQL, ini adalah cara terdekat untuk menirunya.

Cobalah !!!

UPDATE 2011-06-23 21:05

Saya baru saja perhatikan pada contoh saya, saya tidak menggunakan nilai @pop.

Kali ini saya mengganti 'pop_tue = pop_tue +1' dengan 'pop_tue = @pop' dan mencoba kembali contohnya:

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>
RolandoMySQLDBA
sumber
Ringkasan Anda tidak sepenuhnya benar: PostgreSQL mendukung sejumlah kolom penambahan otomatis (serial), seperti yang dilakukan Oracle (dalam kedua kasus, kolom diisi dengan nilai dari urutan). PostgreSQL juga menawarkan bigserialtipe data yang menawarkan rentang yang jauh lebih besar dari
2.147.483.647
@a_horse_with_no_name: maaf atas pengawasannya. Saya masih seorang pekerja harian dengan postgresql. Saya akan memperbarui jawaban saya nanti. Saya di jalan menjawab dari iPhone. Semoga harimu menyenangkan!
RolandoMySQLDBA
0

Seperti yang XL katakan, itu tidak terbatas hanya pada kunci primer. Ini adalah batasan potensial bahwa Anda hanya dapat memiliki satu kolom seperti itu per tabel tetapi solusi terbaik adalah menghasilkan sebanyak mungkin angka yang Anda butuhkan di tabel lain dan kemudian memasukkannya ke tempat yang Anda inginkan.

nvogel
sumber