MySQL: Чому auto_increment обмежений лише первинними ключами?


10

Я знаю, що MySQL обмежує стовпці auto_increment первинними ключами. Чому це? Моя перша думка полягає в тому, що це обмеження продуктивності, оскільки, напевно, десь є стільниця, яка повинна бути заблокована, щоб отримати це значення.

Чому я не можу в одній таблиці мати кілька стовпців автоматичного збільшення?

Дякую.


Щойно я помітив, що забув використовувати @pop в транзакціях. Я повторно спробував приклад і розмістив його внизу своєї відповіді !!!
RolandoMySQLDBA

Мені подобається це питання, тому що він змусив мене замислитися над MySQL, чим я не дуже займаюся в ніч на п’ятницю. +1 !!!
RolandoMySQLDBA

Відповіді:


9

Чому ви хочете мати стовпчик auto_increment, який не є первинним ключем?

Якщо ви хочете, щоб стовпець був автоматичним збільшенням, за визначенням ви не зберігаєте змістовні дані у цьому стовпці. Єдиний випадок, коли зберігання несуттєвої інформації має сенс - це особливий випадок, коли ви хочете мати синтетичний первинний ключ. У цьому випадку брак інформації - це користь, оскільки немає ризику, що хтось коли-небудь прийде в майбутнє і захоче змінити дані, оскільки змінився атрибут якоїсь сутності.

Маючи кілька стовпців auto_increment в одній таблиці, здається, навіть дивніше. Два стовпці матимуть однакові дані - вони генеруються одним і тим же алгоритмом і заселяються в той же час. Я припускаю, що ви можете придумати реалізацію, коли можливо, щоб вони були трохи синхронізовані, якщо було б достатньо одночасних сеансів. Але я не можу уявити, як це було б корисно в додатку.


Це було скоріше теоретичним питанням - я не маю практичного використання для того, щоб мати декілька стовпців автоматичного підвищення, я просто хотів почути пояснення людей, чому це неможливо. Дякуємо, що знайшли час для відповіді! :)
Крістофер Армстронг,

2
"Чому ви хочете мати стовпець auto_increment, який не є первинним ключем?" - Я можу придумати кілька причин особисто. Але це ОТ. :-)
Дені де Бернарді

Я зараз щось подібне роблю - і це не безглузді дані. Мені потрібно знати кількість всіх записів, які коли-небудь вставлені в таблицю, але вже мати більше корисних первинних ключів. Це вирішує, що як і кожен новий запис має значення, яке воно є. Слабка аналогія - це вимога "ти наш 10-тисячний клієнт". З часом клієнти видаляються, тому COUNT (*) недійсний.
Циліндрик

Чому ви хочете мати стовпчик auto_increment, який не є первинним ключем?
phil_w

2
Чому ви хочете мати стовпчик auto_increment, який не є первинним ключем? Можливі причини включають: Оскільки ПК також використовується для фізичного впорядкування рядків. Приклад. Скажімо, ви зберігаєте повідомлення для користувачів. Вам потрібно прочитати всі повідомлення на кожного користувача, тому ви хочете мати їх разом для ефективного пошуку. Оскільки innodb сортує їх за ПК, ви можете зробити це: створити повідомлення таблиці (користувач, id, txt, первинний ключ (користувач, ідентифікатор))
phil_w

8

Насправді атрибут AUTO_INCREMENT не обмежується ПЕРВИЧНИМ КЛЮЧАМ (більше). Це було раніше в старих версіях - безумовно, 3,23 та, мабуть, 4,0. Досі керівництво MySQL для всіх версій з 4.1 читає так

У таблиці може бути лише один стовпець AUTO_INCREMENT, він повинен бути індексований, і він не може мати значення DEFAULT.

Таким чином, ви дійсно можете мати стовпець AUTO_INCREMENT у таблиці, яка не є первинним ключем. Якщо це має сенс, це інша тема.

Я також повинен зазначити, що стовпець AUTO_INCREMENT завжди повинен бути цілим типом (технічно також допускається тип з плаваючою точкою) і що він повинен бути НЕ ВИЗНАЧЕНО. Підписаний тип не тільки витрачає половину ключового простору, але також може призвести до величезних проблем, якщо випадкове введення негативного значення.

Нарешті, MySQL 4.1 та пізніших версій визначає псевдонім типу SERIAL для BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE.


1
+1 за те, що функція auto_increment не обмежується ПК. Не впевнений, чому ви думаєте, що використання негативних чисел для сурогатного ключа призводить до "величезних проблем".
nvogel

4

Це цікаве запитання, оскільки різні бази даних мають унікальні підходи для забезпечення автоматичного підвищення.

MySQL : Для однозначної ідентифікації рядка в таблиці генерується лише один ключ автоматичного посилення. Позаду існує не так багато пояснень, а просто реалізація. Залежно від типу даних, значення автоматичного посилення фіксуються за довжиною типу даних у байтах:

  • Макс TINYINT - 127
  • Максимум невідомих відтінків - 255
  • Макс INT - 2147483647
  • Макс НЕЗНАЧЕНО INT - 4294967295

PostgreSQL

Внутрішній серіал даних використовується для автоматичного збільшення від 1 до 2,147,483,647. Більші діапазони дозволені за допомогою bigserial.

Oracle : Об'єкт схеми під назвою SEQUENCE може створити нові числа, просто викликавши функцію nextval. PostgreSQL також має такий механізм.

Ось приємна URL-адреса, яка визначає, як інші БД задають їх: http://www.w3schools.com/sql/sql_autoincrement.asp

Тепер щодо вашого запитання, якщо ви дійсно хочете мати кілька стовпців auto_increment в одній таблиці, вам доведеться наслідувати це.

Дві причини, чому ви повинні наслідувати це:

  1. MySQL містить лише один приріст стовпчика в таблиці, як і PostgreSQL, Oracle, SQL Server та MS Access.
  2. У MySQL немає об'єкта схеми SEQUENCE, як Oracle і PostgreSQL.

Як би ви наслідували це ???

Використання декількох таблиць, у яких є лише один стовпець з автоматичним збільшенням, і відображення їх у потрібні стовпці в цільових таблицях. Ось приклад:

Скопіюйте та вставте цей приклад:

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: вибачте за недогляд. Я все ще мандрівник з postgresql. Свою відповідь я оновлю пізніше. Я в дорозі відповідь з iPhone. Гарного дня!
RolandoMySQLDBA

0

Як каже XL, він не обмежується лише первинними ключами. Потенційним обмеженням є те, що ви можете мати лише один такий стовпець на кожну таблицю, але найкращий спосіб вирішити - створити стільки чисел, які вам потрібні, в іншій таблиці, а потім вставити їх там, де вам потрібно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.