Índice único
Un índice único (UNIQUE INDEX) combina dos funciones: acelera las búsquedas como cualquier índice y garantiza que no existan valores duplicados en la columna indexada. Es la implementación interna de la restricción UNIQUE que vimos en la sección de restricciones.
Sintaxis
-- Con CREATE INDEX
CREATE UNIQUE INDEX nombre_indice ON tabla (columna);
-- Con ALTER TABLE
ALTER TABLE tabla ADD UNIQUE INDEX nombre_indice (columna);
-- En CREATE TABLE
CREATE TABLE tabla (
columna tipo,
UNIQUE INDEX nombre_indice (columna)
);
-- Atajo: la restricción UNIQUE crea el índice automáticamente
CREATE TABLE tabla (
columna tipo UNIQUE
);Ejemplo básico
CREATE TABLE codigos_descuento (
id INT AUTO_INCREMENT PRIMARY KEY,
codigo VARCHAR(20) NOT NULL,
descuento DECIMAL(5,2) NOT NULL,
usos_restantes INT DEFAULT 1,
UNIQUE INDEX uq_codigo (codigo)
);
INSERT INTO codigos_descuento (codigo, descuento, usos_restantes) VALUES
('BIENVENIDO10', 10.00, 100),
('VERANO25', 25.00, 50),
('VIP50', 50.00, 10);
-- Buscar un código es rápido gracias al índice
EXPLAIN SELECT * FROM codigos_descuento WHERE codigo = 'VIP50';| id | select_type | table | type | possible_keys | key | rows | Extra |
|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | codigos_descuento | const | uq_codigo | uq_codigo | 1 | NULL |
type: const es el tipo de acceso más rápido. MySQL sabe que solo puede haber una fila con ese valor.
-- Intentar insertar un código duplicado falla
-- INSERT INTO codigos_descuento (codigo, descuento) VALUES ('VIP50', 15.00);
-- Error: Duplicate entry 'VIP50' for key 'uq_codigo'Índices únicos en tienda_mysql
SELECT TABLE_NAME, INDEX_NAME, COLUMN_NAME
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = 'tienda_mysql'
AND NON_UNIQUE = 0
AND INDEX_NAME != 'PRIMARY'
ORDER BY TABLE_NAME;| TABLE_NAME | INDEX_NAME | COLUMN_NAME |
|---|---|---|
| clientes | ||
| empleados |
Las tablas clientes y empleados tienen índices únicos en la columna email, creados automáticamente por la restricción UNIQUE en el CREATE TABLE.
-- El índice único se usa para búsquedas rápidas
EXPLAIN SELECT * FROM clientes WHERE email = 'maria.garcia@email.com';| id | select_type | table | type | possible_keys | key | rows | Extra |
|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | clientes | const | 1 | NULL |
Índice único compuesto
Un índice único puede cubrir múltiples columnas. La combinación de valores debe ser única, pero cada columna individual puede tener duplicados:
CREATE TABLE inscripciones (
id INT AUTO_INCREMENT PRIMARY KEY,
estudiante_id INT NOT NULL,
curso_id INT NOT NULL,
fecha DATE NOT NULL,
UNIQUE INDEX uq_estudiante_curso (estudiante_id, curso_id)
);
-- Un estudiante puede inscribirse en diferentes cursos
INSERT INTO inscripciones (estudiante_id, curso_id, fecha) VALUES
(1, 101, '2026-02-01'),
(1, 102, '2026-02-01'),
(2, 101, '2026-02-01');
-- Pero no puede inscribirse dos veces al mismo curso
-- INSERT INTO inscripciones (estudiante_id, curso_id, fecha) VALUES (1, 101, '2026-02-15');
-- Error: Duplicate entry '1-101' for key 'uq_estudiante_curso'NULL en índices únicos
A diferencia de la mayoría de bases de datos, MySQL permite múltiples valores NULL en una columna con índice único. NULL no se considera igual a otro NULL:
CREATE TABLE perfiles (
id INT AUTO_INCREMENT PRIMARY KEY,
usuario VARCHAR(50) NOT NULL,
twitter VARCHAR(50),
UNIQUE INDEX uq_twitter (twitter)
);
-- Múltiples NULLs son válidos
INSERT INTO perfiles (usuario, twitter) VALUES
('María', '@maria_dev'),
('Carlos', NULL),
('Ana', NULL);
SELECT * FROM perfiles;| id | usuario | |
|---|---|---|
| 1 | María | @maria_dev |
| 2 | Carlos | |
| 3 | Ana |
Carlos y Ana tienen NULL en twitter y ambos se aceptan. Pero no puedes insertar otro @maria_dev.
UNIQUE vs PRIMARY KEY
| Característica | PRIMARY KEY | UNIQUE INDEX |
|---|---|---|
| NULL | No permite | Permite (múltiples) |
| Por tabla | Solo una | Varias |
| Índice clustered | Sí (InnoDB) | No |
| Creado automáticamente | Con PRIMARY KEY | Con UNIQUE |
Crear índice único sobre datos existentes
Si la tabla ya tiene datos duplicados, la creación del índice falla:
-- Verificar duplicados antes de crear el índice
SELECT ciudad, COUNT(*) AS repeticiones
FROM clientes
GROUP BY ciudad
HAVING COUNT(*) > 1;| ciudad | repeticiones |
|---|---|
| Madrid | 2 |
| Barcelona | 2 |
| Zaragoza | 2 |
| Valencia | 2 |
Como hay ciudades duplicadas, no podemos crear un índice único sobre ciudad. Esto es correcto: múltiples clientes pueden vivir en la misma ciudad.
Limpieza
DROP TABLE IF EXISTS codigos_descuento;
DROP TABLE IF EXISTS inscripciones;
DROP TABLE IF EXISTS perfiles;En el siguiente artículo veremos los índices invisibles, una funcionalidad de MySQL 8.0 que permite desactivar índices sin eliminarlos.
Escrito por Eduardo Lázaro
