Curso de SQL completo: SELECT, JOIN, agregaciones, índices y transacciones (2026)

Un índice es una estructura de datos auxiliar que MySQL mantiene junto a la tabla para acelerar las búsquedas. Sin índices, cada consulta con WHERE tiene que leer la tabla entera (full table scan); con el índice correcto, MySQL salta directamente a las filas que cumple la condición. La diferencia puede ser de milisegundos frente a minutos en tablas grandes.

En este capítulo cubrimos los tipos de índices, cómo crearlos, cómo EXPLAIN te dice si una consulta los usa, y cuándo un índice puede perjudicar en lugar de ayudar.

Cómo funciona un índice B-tree

El tipo de índice predeterminado en MySQL InnoDB es el B-tree (árbol balanceado). Imagina el índice de un libro: en vez de leer cada página buscando un tema, buscas el tema en el índice y vas directamente a la página correcta. El B-tree permite búsquedas exactas (=), rangos (BETWEEN, >, <) y prefijos de cadena (LIKE 'abc%') de forma eficiente. No ayuda con búsquedas de sufijo (LIKE '%abc').

La PRIMARY KEY ya es un índice

-- En InnoDB, la PRIMARY KEY es el índice clúster (clustered index):
-- las filas se almacenan físicamente ordenadas por la PK.
-- Por eso las búsquedas por PK son las más rápidas.

SELECT * FROM clientes WHERE id = 42;  -- usa el índice de la PK, muy rápido

Crear índices

-- Índice simple en una columna
CREATE INDEX idx_ciudad ON clientes (ciudad);

-- Índice único: no permite valores duplicados
CREATE UNIQUE INDEX idx_email ON clientes (email);
-- Equivalente a añadir UNIQUE en la definición de la columna

-- Índice compuesto (multi-columna)
CREATE INDEX idx_ciudad_fecha ON clientes (ciudad, fecha_alta);

-- Índice en una tabla existente con ALTER TABLE
ALTER TABLE pedidos ADD INDEX idx_estado_fecha (estado, fecha);

-- Ver los índices de una tabla
SHOW INDEXES FROM clientes;
SHOW INDEXES FROM pedidos;

EXPLAIN: ver el plan de ejecución

-- Añadir EXPLAIN delante de cualquier SELECT para ver el plan
EXPLAIN SELECT * FROM clientes WHERE ciudad = 'Madrid';

-- Columnas importantes del resultado de EXPLAIN:
-- type:    'const' o 'ref' = usa índice (bueno); 'ALL' = full scan (malo)
-- key:     nombre del índice que usa (NULL si no usa ninguno)
-- rows:    estimación de filas a examinar (menor = mejor)
-- Extra:   'Using index', 'Using where', 'Using filesort' (ordenación sin índice)

Ejemplo práctico: consulta sin índice vs. con índice:

-- Antes de crear el índice:
EXPLAIN SELECT * FROM pedidos WHERE estado = 'pagado';
-- type: ALL, rows: 10000 — full table scan

ALTER TABLE pedidos ADD INDEX idx_estado (estado);

-- Después:
EXPLAIN SELECT * FROM pedidos WHERE estado = 'pagado';
-- type: ref, key: idx_estado, rows: 200 — usa el índice

Índice compuesto: el orden de las columnas importa

-- Índice (ciudad, fecha_alta) sirve para:
--   WHERE ciudad = 'Madrid'
--   WHERE ciudad = 'Madrid' AND fecha_alta > '2024-01-01'
--   ORDER BY ciudad, fecha_alta

-- NO sirve (sin la primera columna) para:
--   WHERE fecha_alta > '2024-01-01'  ← necesita su propio índice

-- Regla: el índice compuesto puede usarse por cualquier prefijo de columnas
-- de izquierda a derecha, pero no por columnas no contiguas empezando por la derecha.

Cuándo los índices perjudican

  • Inserciones y actualizaciones más lentas. Cada INSERT, UPDATE y DELETE tiene que actualizar también los índices. Una tabla con muchos índices se actualiza más lento.
  • Tablas pequeñas. En tablas de pocas filas, el full scan es más rápido que navegar el B-tree. MySQL puede ignorar el índice en estos casos.
  • Alta selectividad baja. Un índice en una columna con pocos valores distintos (como estado con 4 valores posibles) puede no reducir suficientemente las filas a examinar.
  • Funciones sobre la columna indexada. WHERE YEAR(fecha) = 2026 no usa el índice de fecha. Mejor: WHERE fecha BETWEEN '2026-01-01' AND '2026-12-31'.

FULLTEXT e índices de texto

-- Índice FULLTEXT para búsquedas de texto completo
ALTER TABLE productos ADD FULLTEXT idx_ft_nombre (nombre);

-- Usar con MATCH...AGAINST (mucho más eficiente que LIKE '%texto%')
SELECT nombre, precio FROM productos
WHERE MATCH(nombre) AGAINST ('auricular' IN BOOLEAN MODE);

-- Para consultas de búsqueda avanzada, consulta el artículo
-- "Programar un buscador con PHP y MySQL" en este sitio.

Con esto termina el Curso de SQL completo. Los conceptos de todos los capítulos se complementan: los índices del capítulo 15 hacen más rápidos los JOINs del 9 y 10; las transacciones del 14 protegen los INSERT y UPDATE de los capítulos 12 y 13. Practica combinando técnicas de varios capítulos para escribir consultas eficientes y correctas.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP