MySQL: auto_increment neden sadece birincil anahtarlarla sınırlı?


10

MySQL, auto_increment sütunlarını birincil anahtarlara sınırladığını biliyorum. Bu neden? İlk düşüncem, bu bir performans kısıtlamasıdır, çünkü muhtemelen bu değeri elde etmek için kilitlenmesi gereken bir yerde bazı sayaç tablosu vardır.

Neden aynı tabloda birden fazla auto_increment sütunu alamıyorum?

Teşekkürler.


Sadece @pop'u işlemlerde kullanmayı unuttuğumu fark ettim. Örneği yeniden denedim ve cevabımın alt kısmına gönderdim !!!
RolandoMySQLDBA

Bu soruyu beğendim çünkü beni bir Cuma gecesi çok fazla yapmadığım MySQL ile düşünmeme neden oldu. +1 !!!
RolandoMySQLDBA

Yanıtlar:


9

Neden birincil anahtar olmayan bir auto_increment sütununa sahip olmak istiyorsunuz?

Bir sütunun auto_increment olmasını istiyorsanız, tanım gereği, o sütunda anlamlı veriler depolamıyorsunuzdur. Anlamlı olmayan bilgilerin depolanmasının mantıklı olduğu tek durum, sentetik bir birincil anahtara sahip olmak istediğiniz özel durumdur. Bu durumda, bilgi eksikliği bir avantajdır, çünkü gelecekte gelecekte birinin gelmesi ve verileri değiştirmek istemesi riski yoktur, çünkü bazı varlıkların bazı nitelikleri değişmiştir.

Aynı tabloda birden fazla auto_increment sütunu bulunması daha da garip görünüyor. İki sütun aynı verilere sahip olacaktır - aynı algoritma tarafından oluşturuluyor ve sonuçta aynı anda dolduruluyorlar. Sanırım yeterli eşzamanlı oturumlar varsa, senkronizasyondan biraz çıkmalarının mümkün olduğu bir uygulama bulabilirsin. Ancak bunun bir uygulamada nasıl yararlı olacağını hayal bile edemiyorum.


Bu daha teorik bir soruydu - birden fazla otomatik_kütle sütununa sahip olmak için pratik bir kullanımım yok, sadece insanların neden mümkün olmadığına dair açıklamalarını duymak istedim. Cevaplamak için zaman ayırdığınız için teşekkür ederiz! :)
Christopher Armstrong

2
"Neden birincil anahtar olmayan bir auto_increment sütununa sahip olmak istiyorsunuz?" - Şahsen birkaç sebep düşünebilirim. Ama bu OT. :-)
Denis de Bernardy

Şimdi böyle bir şey yapıyorum - ve anlamsız bir veri değil. Şimdiye kadar bir tabloya eklenen tüm kayıtların sayısını bilmek gerekir, ancak daha yararlı birincil anahtarları zaten var. Bu, her yeni kaydın değeri olduğu için bunu çözer. (Zayıf) bir benzetme "siz 10.000'inci müşterimizsiniz" gereksinimi olacaktır. Müşteriler zaman içinde silinir, dolayısıyla COUNT (*) geçerli değildir.
Silindirik

Neden birincil anahtar olmayan bir auto_increment sütununa sahip olmak istiyorsunuz?
phil_w

2
Neden birincil anahtar olmayan bir auto_increment sütununa sahip olmak istiyorsunuz? Olası nedenler şunlardır: Çünkü PK, satırları fiziksel olarak sipariş etmek için de kullanılır. Örnek: Diyelim ki kullanıcılar için mesajlar saklıyorsunuz. Kullanıcı başına tüm iletileri okumalısınız, bu nedenle verimli bir şekilde alınabilmeleri için bir araya getirilmesini istersiniz. İnnodb onları PK tarafından sıraladığından bunu yapmak isteyebilirsiniz: tablo mesajları oluşturmak (kullanıcı, id, txt, birincil anahtar (kullanıcı, id))
phil_w

8

Aslında AUTO_INCREMENT özniteliği PRIMARY KEY ile sınırlı değildir (artık). Eski versiyonlarda öyle - kesinlikle 3.23 ve muhtemelen 4.0. 4.1 sürümünden bu yana tüm sürümler için hala MySQL kılavuzu

Tablo başına yalnızca bir AUTO_INCREMENT sütunu olabilir, dizine eklenmelidir ve DEFAULT değerine sahip olamaz.

Dolayısıyla, birincil anahtar olmayan bir tabloda gerçekten bir AUTO_INCREMENT sütunu olabilir. Bu mantıklıysa, farklı bir konudur.

Ayrıca bir AUTO_INCREMENT sütun her zaman bir tamsayı türü (teknik olarak kayan nokta türü de izin verilir) ve UNSIGNED olması gerektiğini belirtmek gerekir. İMZA tipi sadece anahtar alanın yarısını boşa harcamakla kalmaz, aynı zamanda yanlışlıkla negatif bir değer girilirse büyük sorunlara yol açabilir.

Son olarak MySQL 4.1 ve üstü, BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE için SERIAL türü takma ad tanımlar .


1
Auto_increment'in PKs ile sınırlı olmadığı için +1. Yedek anahtar için negatif sayılar kullanmanın neden "büyük sorunlara" yol açtığını düşündüğünüzden emin değilim.
nvogel

4

Bu ilginç bir sorudur çünkü farklı veritabanlarının auto_increment sağlamak için benzersiz yaklaşımları vardır.

MySQL : Tablodaki bir satırı benzersiz bir şekilde tanımlamak için yalnızca bir auto_increment anahtarı oluşturulur. Nedeninin arkasında çok fazla açıklama yok, sadece uygulama. Veri tipine bağlı olarak, auto_increment değerleri veri tipinin bayt cinsinden uzunluğu ile sabitlenir:

  • Maksimum TINYINT 127'dir
  • MAKSİMUM AÇILMAYAN TINTINT 255
  • Maksimum INT 2147483647'dir
  • MAKSİMUM AÇILMIŞ INT 4294967295

PostgreSQL

Dahili veri tipi seri 1'den 2.147.483.647'ye otomatik artış için kullanılır. Büyük seri kullanılarak daha büyük aralıklara izin verilir.

Oracle : SEQUENCE adlı şema nesnesi, sadece nextval işlevini çağırarak yeni sayılar oluşturabilir. PostgreSQL'in de böyle bir mekanizması var.

Diğer DB'lerin bunları nasıl belirttiğini gösteren güzel bir URL: http://www.w3schools.com/sql/sql_autoincrement.asp

Şimdi sorunuzla ilgili olarak, gerçekten tek bir tabloda birden fazla auto_increment sütununa sahip olmak istiyorsanız, bunu taklit etmeniz gerekir.

Bunu taklit etmenizin iki nedeni:

  1. MySQL, PostgreSQL, Oracle, SQL Server ve MS Access gibi tablo başına yalnızca bir artış sütunu barındırır.
  2. MySQL'in Oracle ve PostgreSQL gibi bir SEQUENCE şema nesnesi yoktur.

Nasıl taklit ederdin ???

Yalnızca bir auto_increment sütunu olan birden çok tablo kullanma ve bunları hedef tablolarda istenen sütunlarla eşleme. İşte bir örnek:

Bu Örneği Kopyala ve Yapıştır:

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;

Bu öğretmenler için bir pop quiz tablosu oluşturacaktır. Ayrıca, okul haftasının her günü için bir tane olmak üzere beş dizi emülatör oluşturdum. Her dizi öykünücüsü, val sütununa 0 değerini ekleyerek çalışır. Dizi öykünücüsü boşsa, val 0, nextval 1 ile başlar. Değilse, nextval sütunu artar. Daha sonra sıra öykünücüsünden sonraki sütunu getirebilirsiniz.

Örnekten elde edilen örnek sonuçlar:

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'de gerçekten birden fazla otomatik artış değerine ihtiyacınız varsa, bu taklit etmenin en yakın yoludur.

Bir şans ver !!!

GÜNCELLEME 2011-06-23 21:05

Sadece örneğimde @pop değerini kullanmadığımı fark ettim.

Bu sefer 'pop_tue = pop_tue + 1' yerine 'pop_tue = @pop' yazdım ve örneği yeniden denedim:

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>

Özetiniz tamamen doğru değil: PostgreSQL, tıpkı Oracle'ın yaptığı gibi herhangi bir sayıda otomatik artış (seri) sütununu destekler (her iki durumda da sütunlar bir diziden bir değerle doldurulur). PostgreSQL ayrıca bigserial
2.147.483.647'den

@a_horse_with_no_name: gözetim için özür dilerim. Ben hala postgresql ile bir yolculukçuyum. Cevabımı daha sonra güncelleyeceğim. Ben iPhone'dan cevap verdim. İyi günler!
RolandoMySQLDBA

0

XL'in dediği gibi, sadece birincil anahtarlarla sınırlı değildir. Tablo başına yalnızca bir sütun alabileceğiniz potansiyel bir sınırlamadır, ancak en iyi geçici çözüm, başka bir tabloya ihtiyacınız olan sayıda sayı oluşturmak ve ardından bunları istediğiniz yere eklemektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.