COUNT DISTINCT

Cuando necesitas saber cuántos valores diferentes existen en una columna, COUNT(DISTINCT) es la herramienta adecuada. A diferencia de COUNT(*) que cuenta todas las filas, o COUNT(columna) que cuenta las filas no nulas, COUNT(DISTINCT columna) elimina los duplicados antes de contar. Es una función que aparece constantemente en análisis de datos: cuántos clientes distintos compraron, cuántas ciudades diferentes hay en la base de datos, cuántos productos únicos se vendieron en un período.

Sintaxis

COUNT(DISTINCT expresion)
COUNT(DISTINCT expresion1, expresion2, ...)

La primera forma cuenta los valores únicos de una sola columna o expresión. La segunda forma, con múltiples expresiones, cuenta las combinaciones únicas de esos valores. En ambos casos, los valores NULL se excluyen del conteo.

Comportamiento básico

Veamos la diferencia entre las tres variantes de COUNT con un ejemplo sobre la tabla pedidos:

SELECT
    COUNT(*) AS total_filas,
    COUNT(cliente_id) AS filas_con_cliente,
    COUNT(DISTINCT cliente_id) AS clientes_distintos
FROM pedidos;
total_filasfilas_con_clienteclientes_distintos
35035048

Hay 350 pedidos en total, todos tienen un cliente_id asignado, pero solo corresponden a 48 clientes diferentes. Esto significa que, en promedio, cada cliente ha realizado unos 7 pedidos. Con una sola consulta ya tienes una visión clara de la distribución.

Caso práctico: clientes únicos por período

Uno de los usos más habituales de COUNT(DISTINCT) es medir la cantidad de clientes únicos que compraron en cada mes:

SELECT
    DATE_FORMAT(fecha_pedido, '%Y-%m') AS mes,
    COUNT(*) AS total_pedidos,
    COUNT(DISTINCT cliente_id) AS clientes_unicos
FROM pedidos
WHERE YEAR(fecha_pedido) = 2025
GROUP BY DATE_FORMAT(fecha_pedido, '%Y-%m')
ORDER BY mes;
mestotal_pedidosclientes_unicos
2025-012818
2025-023120
2025-033522
2025-042919
2025-053321
2025-063825

La diferencia entre total_pedidos y clientes_unicos te indica cuántos clientes son recurrentes en cada mes. Un mes con 38 pedidos y 25 clientes únicos tiene 13 pedidos de clientes que compraron más de una vez.

Caso práctico: productos únicos vendidos por categoría

Para analizar la variedad de productos vendidos en cada categoría:

SELECT
    c.nombre AS categoria,
    COUNT(DISTINCT dp.producto_id) AS productos_vendidos,
    COUNT(*) AS lineas_de_venta
FROM detalle_pedidos dp
JOIN productos p ON dp.producto_id = p.id
JOIN categorias c ON p.categoria_id = c.id
GROUP BY c.nombre
ORDER BY productos_vendidos DESC;
categoriaproductos_vendidoslineas_de_venta
Smartphones11187
Portátiles794
Accesorios electrónicos7156
Ropa hombre568
Ropa mujer572
Hogar445

Los accesorios electrónicos son interesantes: solo 7 productos distintos generan 156 líneas de venta, lo que indica alta rotación y recompra en esa categoría.

Caso práctico: COUNT DISTINCT con múltiples columnas

MySQL permite pasar varias columnas a COUNT(DISTINCT ...) para contar combinaciones únicas. Por ejemplo, si quieres saber cuántas combinaciones diferentes de cliente y producto existen (es decir, cuántos clientes distintos compraron cada producto al menos una vez):

SELECT
    COUNT(DISTINCT cliente_id, producto_id) AS combinaciones_unicas
FROM pedidos p
JOIN detalle_pedidos dp ON p.id = dp.pedido_id;
combinaciones_unicas
312

De todas las líneas de detalle, hay 312 pares únicos de cliente-producto. Esto te dice que hay 312 relaciones de compra diferentes, sin importar cuántas veces un mismo cliente compró el mismo producto.

También puedes usarlo agrupado para ver cuántos productos distintos compró cada cliente:

SELECT
    cl.nombre,
    cl.apellidos,
    COUNT(DISTINCT dp.producto_id) AS productos_diferentes
FROM pedidos p
JOIN clientes cl ON p.cliente_id = cl.id
JOIN detalle_pedidos dp ON p.id = dp.pedido_id
GROUP BY cl.id, cl.nombre, cl.apellidos
ORDER BY productos_diferentes DESC
LIMIT 5;
nombreapellidosproductos_diferentes
MaríaGarcía López14
CarlosRodríguez Martín12
AnaMartínez Ruiz11
PedroSánchez Gómez9
LauraFernández Díaz9

Manejo de NULL

COUNT(DISTINCT) ignora los valores NULL, al igual que COUNT(columna). Si una columna tiene valores nulos, no se cuentan como un valor distinto:

SELECT
    COUNT(DISTINCT ciudad) AS ciudades_distintas,
    COUNT(ciudad) AS filas_con_ciudad,
    COUNT(*) AS total_clientes
FROM clientes;
ciudades_distintasfilas_con_ciudadtotal_clientes
154550

Hay 50 clientes, 45 tienen ciudad registrada, y entre esas 45 filas hay 15 ciudades diferentes. Los 5 clientes sin ciudad se excluyen completamente del conteo DISTINCT.

Combinación con otras funciones

COUNT(DISTINCT) es especialmente útil dentro de HAVING para filtrar grupos por diversidad:

SELECT
    DATE_FORMAT(p.fecha_pedido, '%Y-%m') AS mes,
    COUNT(DISTINCT dp.producto_id) AS productos_vendidos
FROM pedidos p
JOIN detalle_pedidos dp ON p.id = dp.pedido_id
GROUP BY DATE_FORMAT(p.fecha_pedido, '%Y-%m')
HAVING COUNT(DISTINCT dp.producto_id) >= 20
ORDER BY mes;

Esta consulta encuentra los meses donde se vendieron al menos 20 productos diferentes, lo cual puede indicar meses con campañas de marketing exitosas o temporadas altas.

Otra combinación frecuente es calcular el ratio entre valores totales y valores únicos para medir la repetición:

SELECT
    c.nombre AS categoria,
    COUNT(*) AS total_ventas,
    COUNT(DISTINCT p.cliente_id) AS clientes_unicos,
    ROUND(COUNT(*) / COUNT(DISTINCT p.cliente_id), 1) AS compras_por_cliente
FROM pedidos p
JOIN detalle_pedidos dp ON p.id = dp.pedido_id
JOIN productos pr ON dp.producto_id = pr.id
JOIN categorias c ON pr.categoria_id = c.id
GROUP BY c.nombre
ORDER BY compras_por_cliente DESC;
categoriatotal_ventasclientes_unicoscompras_por_cliente
Accesorios electrónicos156324.9
Smartphones187404.7
Ropa mujer72223.3
Portátiles94303.1

El ratio compras_por_cliente revela qué categorías generan más recompra. Los accesorios electrónicos tienen la tasa más alta con casi 5 compras por cliente único.

Rendimiento con grandes volúmenes

COUNT(DISTINCT) es más costoso que COUNT(*) porque MySQL necesita mantener en memoria los valores únicos para detectar duplicados. En tablas con millones de registros y alta cardinalidad (muchos valores distintos), la consulta puede consumir bastante memoria. Si experimentas problemas de rendimiento, considera crear un índice sobre la columna que estás contando, ya que MySQL puede usar el índice para eliminar duplicados de forma más eficiente.

Practica con COUNT DISTINCT

Usa el editor para contar valores únicos:

Simulador SQL
Ctrl+Enter para ejecutar

En el siguiente artículo veremos COUNT IF para contar filas que cumplen una condición.

Escrito por Eduardo Lázaro