PARTITION BY EN MYSQL: CÓMO FUNCIONA Y EJEMPLOS

Desde MySQL 8.0 las funciones de ventana revolucionaron las consultas analíticas. Y el corazón de casi todas es PARTITION BY: divide el conjunto de resultados en particiones (grupos) y hace que la función se calcule de forma independiente en cada grupo. Se diferencia de "GROUP BY" porque agrupa y reduce filas (pierdes detalle) y "PARTITION BY" agrupa pero mantiene todas las filas siendo ideal para rankings, acumulados por categoría, comparaciones intra-grupo, etc...
Ejemplo:

-- Ventas por departamento y empleado
SELECT 
departamento,
empleado,
importe,
fecha,
-- Acumulado reiniciado por departamento
SUM(importe) OVER (
PARTITION BY departamento 
ORDER BY fecha
) AS acumulado_depto,

-- Ranking descendente por importe dentro de cada depto
DENSE_RANK() OVER (
PARTITION BY departamento 
ORDER BY importe DESC
) AS ranking_depto
FROM ventas
ORDER BY departamento, fecha;
Explicación de lo que hace "PARTITION BY" en el ejemplo anterior:
  • Cada departamento es una partición independiente: El SUM() se reinicia al cambiar de departamento.
  • El ranking también se calcula solo dentro de cada departamento.
Casos reales de buen uso de "PARTITION BY":
  • Top-N por grupo (ej. top 3 vendedores por región)
    SELECT * FROM (
    SELECT *,
      ROW_NUMBER() OVER (PARTITION BY region ORDER BY ventas DESC) AS rn
    FROM leaderboard
    ) t WHERE rn <= 3;
    
  • Comparar con valor anterior (LAG):
    SELECT fecha, ventas,
      ventas - LAG(ventas) OVER (PARTITION BY producto ORDER BY fecha) AS variacion
    FROM ventas_diarias;
    
  • Porcentaje dentro del total del grupo:
    SELECT departamento, empleado, importe,
      importe / SUM(importe) OVER (PARTITION BY departamento) * 100 AS pct_del_depto
    FROM ventas;
    
  • Eliminar duplicados manteniendo el más reciente:
    DELETE FROM tabla
    WHERE id IN (
    SELECT id FROM (
    SELECT id,
     ROW_NUMBER() OVER (PARTITION BY email ORDER BY created_at DESC) AS rn
    FROM usuarios
    ) t WHERE rn > 1
    );