MySQL: Pourquoi l'auto_increment est-il limité aux seules clés primaires?


10

Je sais que MySQL limite les colonnes auto_increment aux clés primaires. Pourquoi est-ce? Ma première pensée est que c'est une restriction de performances, car il y a probablement une table de compteur quelque part qui doit être verrouillée pour obtenir cette valeur.

Pourquoi ne puis-je pas avoir plusieurs colonnes auto_increment dans la même table?

Merci.


Je viens de remarquer que j'ai oublié d'utiliser le @pop dans les transactions. J'ai réessayé l'exemple et je l'ai posté au bas de ma réponse !!!
RolandoMySQLDBA

J'aime cette question parce qu'elle m'a fait sortir des sentiers battus avec MySQL, ce que je ne fais pas beaucoup un vendredi soir. +1 !!!
RolandoMySQLDBA

Réponses:


9

Pourquoi voudriez-vous avoir une colonne auto_increment qui n'est pas la clé primaire?

Si vous voulez qu'une colonne soit un incrément automatique, par définition, vous ne stockez pas de données significatives dans cette colonne. Le seul cas où le stockage d'informations non significatives est logique est le cas spécial où vous souhaitez avoir une clé primaire synthétique. Dans ce cas, le manque d'informations est un avantage car il n'y a aucun risque que quelqu'un vienne un jour dans le futur et veuille changer les données car un attribut d'une entité a changé.

Avoir plusieurs colonnes auto_increment dans la même table semble encore plus étrange. Les deux colonnes auraient les mêmes données - elles sont générées par le même algorithme et remplies en même temps après tout. Je suppose que vous pourriez trouver une implémentation où il est possible qu'ils soient légèrement désynchronisés s'il y avait suffisamment de sessions simultanées. Mais je ne peux pas imaginer comment cela pourrait être utile dans une application.


C'était plus une question théorique - je n'ai aucune utilité pratique pour avoir plusieurs colonnes auto_increment, je voulais juste entendre les explications des gens sur pourquoi cela n'était pas possible. Merci d'avoir pris le temps de répondre! :)
Christopher Armstrong

2
"Pourquoi voudriez-vous avoir une colonne auto_increment qui n'est pas la clé primaire?" - Je peux penser à plusieurs raisons, personnellement. Mais c'est OT. :-)
Denis de Bernardy

Je fais quelque chose comme ça maintenant - et ce ne sont pas des données dénuées de sens. J'ai besoin de connaître le nombre de tous les enregistrements jamais insérés dans une table, mais j'ai déjà des clés primaires plus utiles. Cela résout cela, car chaque nouvel enregistrement a une valeur dont il est. Une analogie (faible) serait l'exigence «vous êtes notre 10 000e client». Les clients sont supprimés au fil du temps, donc COUNT (*) n'est pas valide.
Cylindrique

Pourquoi voudriez-vous avoir une colonne auto_increment qui n'est pas la clé primaire?
phil_w

2
Pourquoi voudriez-vous avoir une colonne auto_increment qui n'est pas la clé primaire? Les raisons possibles incluent: Parce que le PK est également utilisé pour ordonner physiquement les lignes. Exemple: supposons que vous stockiez des messages pour les utilisateurs. Vous devez lire tous les messages par utilisateur, vous voulez donc les avoir ensemble pour une récupération efficace. Puisque innodb les trie par PK, vous pouvez le faire: créer des messages de table (utilisateur, id, txt, clé primaire (utilisateur, id))
phil_w

8

En fait, l'attribut AUTO_INCREMENT n'est pas limité à la PRIMARY KEY (plus). Il en était ainsi dans les anciennes versions - certainement 3.23 et probablement 4.0. Toujours le manuel MySQL pour toutes les versions depuis 4.1 se lit comme ceci

Il ne peut y avoir qu'une seule colonne AUTO_INCREMENT par table, elle doit être indexée et ne peut pas avoir de valeur DEFAULT.

Vous pouvez donc en effet avoir une colonne AUTO_INCREMENT dans une table qui n'est pas la clé primaire. Si cela a du sens, c'est un sujet différent.

Je dois également mentionner qu'une colonne AUTO_INCREMENT doit toujours être de type entier (techniquement, un type à virgule flottante est également autorisé) et qu'elle doit être UNSIGNED. Un type SIGNÉ ne gaspille pas seulement la moitié de l'espace clé, il peut également entraîner d'énormes problèmes si une valeur négative est insérée par accident.

Enfin, MySQL 4.1 et versions ultérieures définissent un alias de type SERIAL pour BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE.


1
+1 pour le fait que auto_increment n'est pas limité aux PK. Je ne sais pas pourquoi vous pensez que l'utilisation de nombres négatifs pour une clé de substitution mène à "d'énormes problèmes".
nvogel

4

C'est une question intéressante car différentes bases de données ont des approches uniques pour fournir auto_increment.

MySQL : une seule clé auto_increment est générée pour identifier de manière unique une ligne dans une table. Il n'y a pas beaucoup d'explications derrière pourquoi, mais juste la mise en œuvre. Selon le type de données, les valeurs auto_increment sont fixées par la longueur du type de données en octets:

  • Max TINYINT est 127
  • La valeur TINTINT UNSIGNED maximale est de 255
  • L'INT max est 2147483647
  • Max UNSIGNED INT est 4294967295

PostgreSQL

La série de type de données interne est utilisée pour l'incrémentation automatique de 1 à 2 147 483 647. De plus grandes plages sont autorisées en utilisant bigserial.

Oracle : l'objet de schéma appelé SEQUENCE peut créer de nouveaux nombres en invoquant simplement la fonction nextval. PostgreSQL dispose également d'un tel mécanisme.

Voici une belle URL qui indique comment les autres bases de données les spécifient: http://www.w3schools.com/sql/sql_autoincrement.asp

Maintenant, concernant votre question, si vous voulez vraiment avoir plusieurs colonnes auto_increment dans une seule table, vous devrez émuler cela.

Deux raisons pour lesquelles vous devez émuler ceci:

  1. MySQL ne prend en charge qu'une seule colonne d'incrément par table, tout comme PostgreSQL, Oracle, SQL Server et MS Access.
  2. MySQL n'a pas d'objet de schéma SEQUENCE comme Oracle et PostgreSQL.

Comment l'imiteriez-vous ???

Utiliser plusieurs tables qui n'ont qu'une seule colonne auto_increment et les mapper aux colonnes souhaitées dans les tables cibles. Voici un exemple:

Copiez et collez cet exemple:

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;

Cela créera un tableau de quiz pop pour les enseignants. J'ai également créé cinq émulateurs de séquence, un pour chaque jour de la semaine scolaire. Chaque émulateur de séquence fonctionne en insérant la valeur 0 dans la colonne val. Si l'émulateur de séquence est vide, il commence par la valeur 0, nextval 1. Sinon, la colonne nextval est incrémentée. Vous pouvez ensuite extraire la colonne nextval de l'émulateur de séquence.

Voici les exemples de résultats de l'exemple:

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>

Si vous avez vraiment besoin de plusieurs valeurs d'incrémentation automatique dans MySQL, c'est le moyen le plus proche de l'émuler.

Essaie !!!

MISE À JOUR 2011-06-23 21:05

Je viens de remarquer dans mon exemple que je n'utilise pas la valeur @pop.

Cette fois, j'ai remplacé «pop_tue = pop_tue + 1» par «pop_tue = @pop» et réessayé l'exemple:

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>

Votre résumé n'est pas complètement correct: PostgreSQL prend en charge n'importe quel nombre de colonnes à incrémentation automatique (série), tout comme Oracle (dans les deux cas, les colonnes sont remplies avec une valeur d'une séquence). PostgreSQL propose également le bigserialtype de données qui offre une plage beaucoup plus large que
2.147.483.647

@a_horse_with_no_name: désolé pour la négligence. Je suis toujours compagnon avec postgresql. Je mettrai à jour ma réponse plus tard. Je suis sur la route de la réponse de l'iPhone. Bonne journée!
RolandoMySQLDBA

0

Comme le dit XL, il ne se limite pas aux seules clés primaires. Il est possible que vous ne puissiez avoir qu'une seule colonne de ce type par table, mais la meilleure solution consiste à générer autant de nombres dont vous avez besoin dans une autre table, puis à les insérer où vous le souhaitez.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.