MySQL: ¿Por qué el auto_increment se limita solo a las claves primarias?


10

Sé que MySQL limita las columnas de autoincremento a las claves primarias. ¿Por qué es esto? Mi primer pensamiento es que es una restricción de rendimiento, ya que probablemente hay alguna tabla de contador en algún lugar que debe bloquearse para obtener este valor.

¿Por qué no puedo tener múltiples columnas auto_increment en la misma tabla?

Gracias.


Acabo de notar que olvidé usar el @pop en las transacciones. ¡Volví a leer el ejemplo y lo publiqué al final de mi respuesta!
RolandoMySQLDBA

Me gusta esta pregunta porque me hizo pensar de inmediato con MySQL, lo que no hago mucho los viernes por la noche. +1 !!!
RolandoMySQLDBA

Respuestas:


9

¿Por qué querrías tener una columna auto_increment que no sea la clave principal?

Si desea que una columna sea un aumento automático, por definición, no está almacenando datos significativos en esa columna. El único caso en el que tiene sentido almacenar información no significativa es el caso especial en el que desea tener una clave primaria sintética. En ese caso, la falta de información es un beneficio porque no hay riesgo de que alguien aparezca en el futuro y quiera cambiar los datos porque algún atributo de alguna entidad cambió.

Tener varias columnas auto_increment en la misma tabla parece aún más extraño. Las dos columnas tendrían los mismos datos: después de todo, se generan con el mismo algoritmo y se rellenan al mismo tiempo. Supongo que podría llegar a una implementación en la que es posible que estén ligeramente fuera de sincronización si hubiera suficientes sesiones simultáneas. Pero no puedo imaginar cómo eso sería útil en una aplicación.


Era más una pregunta teórica: no tengo ningún uso práctico para tener múltiples columnas de autoincremento, solo quería escuchar las explicaciones de las personas sobre por qué no era posible. ¡Gracias por tomarse el tiempo de responder! :)
Christopher Armstrong

2
"¿Por qué querrías tener una columna de aumento automático que no sea la clave principal?" - Puedo pensar en algunas razones, personalmente. Pero eso es OT. :-)
Denis de Bernardy

Estoy haciendo algo como esto ahora, y no son datos sin sentido. Necesito saber un recuento de todos los registros insertados en una tabla, pero ya tengo claves primarias más útiles. Esto resuelve eso, ya que cada nuevo registro tiene un valor de lo que es. Una analogía (débil) sería el requisito de "usted es nuestro cliente número 10.000". Los clientes se eliminan con el tiempo, por lo que COUNT (*) no es válido.
Cilíndrico

¿Por qué querrías tener una columna auto_increment que no sea la clave principal?
phil_w

2
¿Por qué querrías tener una columna auto_increment que no sea la clave principal? Las posibles razones incluyen: Porque el PK también se usa para ordenar las filas físicamente. Ejemplo: supongamos que almacena mensajes para los usuarios. Debe leer todos los mensajes por usuario, por lo que desea tenerlos juntos para una recuperación eficiente. Dado que innodb los ordena por PK, es posible que desee hacer eso: crear mensajes de tabla (usuario, id, txt, clave principal (usuario, id))
phil_w

8

De hecho, el atributo AUTO_INCREMENT no está limitado a la PRIMARY KEY (más). Solía ​​ser así en versiones anteriores, definitivamente 3.23 y probablemente 4.0. Aún así, el manual de MySQL para todas las versiones desde 4.1 se lee así

Solo puede haber una columna AUTO_INCREMENT por tabla, debe estar indexada y no puede tener un valor POR DEFECTO.

Por lo tanto, puede tener una columna AUTO_INCREMENT en una tabla que no es la clave principal. Si eso tiene sentido, es un tema diferente sin embargo.

También debo mencionar que una columna AUTO_INCREMENT siempre debe ser un tipo entero (técnicamente también se permite un tipo de punto flotante) y que debe estar SIN FIRMAR. Un tipo FIRMADO no solo desperdicia la mitad del espacio clave, sino que también puede ocasionar grandes problemas si se inserta un valor negativo por accidente.

Finalmente, MySQL 4.1 y posterior define un alias de tipo SERIAL para BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE.


1
+1 por el hecho de que auto_increment no se limita a PK. Sin embargo, no estoy seguro de por qué crees que usar números negativos para una clave sustituta conduce a "grandes problemas".
nvogel

4

Esta es una pregunta interesante porque diferentes bases de datos tienen enfoques únicos para proporcionar auto_increment.

MySQL : solo se genera una clave auto_increment para identificar de forma exclusiva una fila en una tabla. No hay mucha explicación detrás de por qué, sino solo implementación. Dependiendo del tipo de datos, los valores de aumento automático se fijan por la longitud del tipo de datos en bytes:

  • Max TINYINT es 127
  • El TINTINT NO FIRMADO máximo es 255
  • INT máximo es 2147483647
  • El INT NO FIRMADO máximo es 4294967295

PostgreSQL

El tipo de datos interno serial se usa para el incremento automático de 1 a 2,147,483,647. Se permiten rangos más grandes usando bigserial.

Oráculo : el objeto de esquema llamado SECUENCIA puede crear nuevos números simplemente invocando la función nextval. PostgreSQL también tiene dicho mecanismo.

Aquí hay una buena URL que proporciona cómo otros DB los especifican: http://www.w3schools.com/sql/sql_autoincrement.asp

Ahora, con respecto a su pregunta, si realmente desea tener múltiples columnas de aumento automático en una sola tabla, tendrá que emular eso.

Dos razones por las que debes emular esto:

  1. MySQL acomoda solo una columna de incremento por tabla, al igual que PostgreSQL, Oracle, SQL Server y MS Access.
  2. MySQL no tiene un objeto de esquema SECUENCIA como Oracle y PostgreSQL.

¿Cómo lo emularías?

Usar varias tablas que tienen solo una columna de aumento automático y asignarlas a las columnas deseadas en las tablas de destino. Aquí hay un ejemplo:

Copie y pegue este ejemplo:

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;

Esto creará una tabla de cuestionarios pop para maestros. También creé cinco emuladores de secuencia, uno para cada día de la semana escolar. Cada emulador de secuencia funciona insertando el valor 0 en la columna val. Si el emulador de secuencia está vacío, comienza con val 0, nextval 1. Si no, la columna nextval se incrementa. Luego puede buscar la columna nextval del emulador de secuencia.

Aquí están los resultados de la muestra del ejemplo:

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 realmente necesita múltiples valores de incremento automático en MySQL, esta es la forma más cercana de emularlo.

Darle una oportunidad !!!

ACTUALIZACIÓN 2011-06-23 21:05

Acabo de notar en mi ejemplo que no uso el valor @pop.

Esta vez reemplacé 'pop_tue = pop_tue + 1' con 'pop_tue = @pop' y volví a intentar el ejemplo:

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>

Su resumen no es completamente correcto: PostgreSQL admite cualquier número de columnas de incremento automático (serie), al igual que Oracle (en ambos casos las columnas se rellenan con un valor de una secuencia). PostgreSQL también ofrece el bigserialtipo de datos que ofrece un rango que es mucho mayor que 2,147,483,647
a_horse_with_no_name

@a_horse_with_no_name: perdón por el descuido. Todavía soy un oficial con postgresql. Actualizaré mi respuesta más tarde. Estoy en el camino de respuesta de iPhone. ¡Tenga un buen día!
RolandoMySQLDBA

0

Como dice XL, no se limita solo a las claves principales. Es una limitación potencial que solo pueda tener una de esas columnas por tabla, pero la mejor solución es generar tantos números que necesite en otra tabla y luego insertarlos donde lo necesite.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.