En la versión 8.0 de MySQL se han añadido muchas funciones nuevas, y una de las más destacadas es el soporte de funciones de ventana. En este artículo nos enfocaremos en la función ROW_NUMBER() que es de uso frecuente. La función ROW_NUMBER() ofrece capacidades poderosas para el análisis de datos y la generación de informes, permitiendo ordenar y clasificar datos según condiciones específicas de manera sencilla. En este artículo explicaremos en detalle desde el uso básico de esta función hasta ejemplos avanzados y métodos alternativos en versiones anteriores.
Audiencia objetivo
Principiantes a intermedios con conocimientos básicos de SQL
Ingenieros y analistas de datos que utilizan MySQL para procesamiento y análisis de datos
Personas que están considerando migrar a la última versión de MySQL
Ventajas de la función ROW_NUMBER()
Esta función puede asignar un número único a los datos según condiciones específicas. Así, tareas como “crear un ranking por ventas más altas” o “extraer y organizar datos duplicados” pueden describirse de forma concisa. Además, en versiones anteriores era necesario usar consultas complejas con variables de usuario, pero al utilizar la función ROW_NUMBER() se mejora la simplicidad y legibilidad del código. En este artículo, presentaremos ejemplos concretos de consultas y ofreceremos una explicación fácil de entender incluso para principiantes. En la siguiente sección, examinaremos en detalle la sintaxis básica y el funcionamiento de esta función.
2. ¿Qué es la función ROW_NUMBER?
El función ROW_NUMBER() añadida recientemente en MySQL 8.0 es un tipo de función ventana que asigna números secuenciales a cada fila de datos. Posee la capacidad de numerar según un orden o grupo específico, lo que resulta muy útil para el análisis de datos y la generación de informes. En esta sección se explica en detalle su sintaxis básica y ejemplos concretos.
Sintaxis básica de la función ROW_NUMBER
Primero, la sintaxis básica de la función ROW_NUMBER() es la siguiente.
SELECT
columna,
ROW_NUMBER() OVER (PARTITION BY columna_grupo ORDER BY columna_orden) AS numero_fila
FROM
nombre_tabla;
Significado de cada elemento
ROW_NUMBER(): Función que asigna un número secuencial a cada fila.
OVER: Palabra clave que define la función ventana.
PARTITION BY: Agrupa los datos por la columna especificada. Es opcional; si no se indica, se asigna una secuencia a todas las filas.
ORDER BY: Especifica el orden en que se asignan los números, permitiendo definir el criterio de clasificación.
Ejemplo básico de uso
Por ejemplo, supongamos que existe una tabla llamada «sales» con los siguientes datos.
employee
department
sale
A
Ventas
500
B
Ventas
800
C
Desarrollo
600
D
Desarrollo
700
Para asignar números secuenciales por cada departamento según el orden descendente de ventas, se utiliza la siguiente consulta.
SELECT
employee,
department,
sale,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY sale DESC) AS row_num
FROM
sales;
Resultado de la ejecución
employee
department
sale
row_num
B
Ventas
800
1
A
Ventas
500
2
D
Desarrollo
700
1
C
Desarrollo
600
2
De este resultado se observa que se muestra la clasificación de ventas por cada departamento.
Cómo usar PARTITION BY
En el ejemplo anterior, los datos se agrupan por la columna «department», lo que permite asignar números secuenciales diferentes para cada departamento. Si se omite PARTITION BY, se asigna un número secuencial único a todas las filas.
SELECT
employee,
sale,
ROW_NUMBER() OVER (ORDER BY sale DESC) AS row_num
FROM
sales;
Resultado de la ejecución
employee
sale
row_num
B
800
1
D
700
2
C
600
3
A
500
4
Características y consideraciones de la función ROW_NUMBER()
Numeración única: Incluso con valores idénticos, los números secuenciales son únicos.
Manejo de valores NULL: Si ORDER BY incluye NULL, se colocan al inicio en orden ascendente y al final en orden descendente.
Impacto en el rendimiento: En conjuntos de datos grandes, la carga de procesamiento de ORDER BY puede ser alta, por lo que es importante configurar índices adecuados.
3. Ejemplos prácticos de uso
Aquí, presentamos escenarios concretos que utilizan la función ROW_NUMBER() de MySQL. Esta función se puede aplicar en muchos casos útiles en el trabajo, como la clasificación de datos y el procesamiento de datos duplicados.
3-1. Clasificación por grupos
Por ejemplo, consideremos el caso de querer asignar un ranking por orden de mayor venta dentro de cada departamento a partir de datos de ventas. A continuación se muestra un ejemplo de datos.
employee
department
sale
A
Ventas
500
B
Ventas
800
C
Desarrollo
600
D
Desarrollo
700
Ejemplo de consulta: Ranking de ventas por departamento
SELECT
employee,
department,
sale,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY sale DESC) AS rank
FROM
sales;
Resultado:
employee
department
sale
rank
B
Ventas
800
1
A
Ventas
500
2
D
Desarrollo
700
1
C
Desarrollo
600
2
De esta manera, se asigna una numeración secuencial por orden de ventas en cada departamento, lo que permite una clasificación sencilla.
3-2. Extracción de los N mejores registros
A continuación, veamos el caso de querer extraer los empleados con las tres mayores ventas en cada departamento. Ejemplo de consulta: Consulta para extraer los N mejores registros
WITH RankedSales AS (
SELECT
employee,
department,
sale,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY sale DESC) AS rank
FROM
sales
)
SELECT
employee,
department,
sale
FROM
RankedSales
WHERE
rank <= 3;
Resultado:
employee
department
sale
B
Ventas
800
A
Ventas
500
D
Desarrollo
700
C
Desarrollo
600
En este ejemplo, solo se obtienen los datos de las tres mayores ventas por departamento. Así, la función ROW_NUMBER() es útil no solo para ranking, sino también para filtrar los datos superiores.
3-3. Extracción y eliminación de datos duplicados
En bases de datos pueden existir datos duplicados. En tales casos, también se puede manejar fácilmente usando la función ROW_NUMBER(). Ejemplo de consulta: Extracción de datos duplicados
SELECT *
FROM (
SELECT
employee,
sale,
ROW_NUMBER() OVER (PARTITION BY employee ORDER BY sale DESC) AS rank
FROM
sales
) tmp
WHERE rank > 1;
Esta consulta detecta duplicados cuando existen varios registros con el mismo nombre de empleado. Ejemplo de consulta: Eliminación de datos duplicados
DELETE FROM sales
WHERE id IN (
SELECT id
FROM (
SELECT
id,
ROW_NUMBER() OVER (PARTITION BY employee ORDER BY sale DESC) AS rank
FROM
sales
) tmp
WHERE rank > 1
);
Resumen
función ROW_NUMBER() es,
Clasificación por grupos
Extracción de los N mejores registros
Detección y eliminación de datos duplicados
Se utiliza en diversos escenarios como los anteriores. Esto permite realizar procesos y análisis de datos complejos de manera sencilla y eficiente.
4. Comparación con otras funciones de ventana
MySQL 8.0, además de ROW_NUMBER(), se proporcionan funciones de ventana como RANK() y DENSE_RANK() que pueden usarse para clasificar y calcular posiciones. Estas funciones tienen roles similares, pero difieren en su comportamiento y resultados. Aquí comparamos cada función y explicamos los escenarios de uso apropiados.
4-1. Función RANK()
Función RANK() es una función de clasificación que asigna el mismo rango a valores idénticos y omite el siguiente rango.
Sintaxis básica
SELECT
column_name,
RANK() OVER (PARTITION BY group_column ORDER BY order_column) AS rank
FROM
table_name;
Ejemplo de uso
Usaremos los siguientes datos para obtener el ranking de ventas.
employee
department
sale
A
Ventas
800
B
Ventas
800
C
Ventas
600
D
Ventas
500
Ejemplo de consulta: Uso de RANK()
SELECT
employee,
sale,
RANK() OVER (ORDER BY sale DESC) AS rank
FROM
sales;
Resultado:
employee
sale
rank
A
800
1
B
800
1
C
600
3
D
500
4
Características:
Los empleados A y B, que tienen el mismo monto de ventas (800), se consideran en el «1.º» lugar.
El siguiente rango, «2.º», se omite, y C ocupa el «3.º» lugar.
4-2. Función DENSE_RANK()
Función DENSE_RANK() es similar a RANK() en que asigna el mismo rango a valores idénticos, pero no omite el siguiente rango.
Sintaxis básica
SELECT
column_name,
DENSE_RANK() OVER (PARTITION BY group_column ORDER BY order_column) AS rank
FROM
table_name;
Ejemplo de uso
Usaremos los mismos datos de antes para probar la función DENSE_RANK(). Ejemplo de consulta: Uso de DENSE_RANK()
SELECT
employee,
sale,
DENSE_RANK() OVER (ORDER BY sale DESC) AS dense_rank
FROM
sales;
Resultado:
employee
sale
dense_rank
A
800
1
B
800
1
C
600
2
D
500
3
Características:
Los empleados A y B, con el mismo monto de ventas (800), se consideran en el «1.º» lugar.
A diferencia de RANK(), el siguiente rango comienza en «2.º», manteniendo la continuidad de los rangos.
4-3. Diferencias con la función ROW_NUMBER()
Función ROW_NUMBER() difiere de las otras dos funciones en que asigna un número único incluso cuando los valores son iguales.
Ejemplo de uso
SELECT
employee,
sale,
ROW_NUMBER() OVER (ORDER BY sale DESC) AS row_num
FROM
sales;
Resultado:
employee
sale
row_num
A
800
1
B
800
2
C
600
3
D
500
4
Características:
Incluso si los valores son iguales, se asigna un número único, por lo que no hay duplicación de rangos.
Es adecuado cuando se necesita controlar estrictamente el orden de los datos o cuando se requiere unicidad por fila.
4-4. Resumen de usos de cada función
Función
Comportamiento de clasificación
Ejemplo de uso
ROW_NUMBER()
Asigna un número único
Cuando se necesita numerar los datos secuencialmente o se requiere identificación única
RANK()
Asigna el mismo número a empates y omite el siguiente número
Cuando se desea mostrar el ranking tal cual existen valores iguales
DENSE_RANK()
Asigna el mismo número a empates sin omitir números
Cuando se prioriza la continuidad de los rangos
Resumen
ROW_NUMBER(), RANK(), DENSE_RANK() deben usarse según diferentes escenarios.
ROW_NUMBER() es adecuado cuando se necesita un número único.
RANK() es útil cuando se desea asignar el mismo rango a datos con valores iguales y resaltar los huecos en el ranking.
DENSE_RANK() es apropiado para un ranking continuo, sin huecos entre rangos.
5. Métodos alternativos para versiones anteriores a MySQL 8.0
En versiones anteriores a MySQL 8.0, la función ROW_NUMBER() y otras funciones de ventana no son compatibles. Sin embargo, al utilizar variables de usuario, se pueden lograr funcionalidades similares. Aquí se explica concretamente los métodos alternativos para versiones anteriores a MySQL 8.0.
5-1. Numeración secuencial usando variables de usuario
En entornos con MySQL 5.7 o anteriores, es posible asignar números secuenciales a cada fila usando variables de usuario. Veamos el siguiente ejemplo.
Ejemplo de uso: ranking de ventas por departamento
Ejemplo de datos:
employee
department
sale
A
Ventas
500
B
Ventas
800
C
Desarrollo
600
D
Desarrollo
700
Consulta:
SET @row_num = 0;
SET @dept = '';
SELECT
employee,
department,
sale,
@row_num := IF(@dept = department, @row_num + 1, 1) AS rank,
@dept := department
FROM
(SELECT * FROM sales ORDER BY department, sale DESC) AS sorted_sales;
Resultado de la ejecución:
employee
department
sale
rank
B
Ventas
800
1
A
Ventas
500
2
D
Desarrollo
700
1
C
Desarrollo
600
2
5-2. Extracción de los N registros superiores
Para obtener los N registros superiores, se utilizan variables de usuario de manera similar. Consulta:
SET @row_num = 0;
SET @dept = '';
SELECT *
FROM (
SELECT
employee,
department,
sale,
@row_num := IF(@dept = department, @row_num + 1, 1) AS rank,
@dept := department
FROM
(SELECT * FROM sales ORDER BY department, sale DESC) AS sorted_sales
) AS ranked_sales
WHERE rank <= 3;
Resultado:
employee
department
sale
rank
B
Ventas
800
1
A
Ventas
500
2
D
Desarrollo
700
1
C
Desarrollo
600
2
Esta consulta asigna un ranking por departamento y luego extrae solo los datos dentro de los 3 primeros registros.
5-3. Detección y eliminación de datos duplicados
El manejo de datos duplicados también se puede abordar usando variables de usuario. Ejemplo de consulta: detección de datos duplicados
SET @row_num = 0;
SET @id_check = '';
SELECT *
FROM (
SELECT
id,
name,
@row_num := IF(@id_check = name, @row_num + 1, 1) AS rank,
@id_check := name
FROM
(SELECT * FROM customers ORDER BY name, id) AS sorted_customers
) AS tmp
WHERE rank > 1;
Ejemplo de consulta: eliminación de datos duplicados
DELETE FROM customers
WHERE id IN (
SELECT id
FROM (
SELECT
id,
@row_num := IF(@id_check = name, @row_num + 1, 1) AS rank,
@id_check := name
FROM
(SELECT * FROM customers ORDER BY name, id) AS sorted_customers
) AS tmp
WHERE rank > 1
);
5-4. Consideraciones sobre variables de usuario
Dependencia de sesión
Las variables de usuario solo son válidas dentro de la sesión. No pueden usarse en otras consultas o sesiones.
Dependencia del orden de procesamiento
Las variables de usuario dependen del orden de ejecución de la consulta, por lo que la configuración de la cláusula ORDER BY es importante.
Legibilidad y mantenibilidad del SQL
El código tiende a volverse complejo, por lo que se recomienda usar funciones de ventana en MySQL 8.0 o superior.
Resumen
En versiones anteriores a MySQL 8.0, al no poder usar funciones de ventana, se pueden emplear variables de usuario para lograr numeración secuencial y procesamiento de rankings. Sin embargo, dado que las consultas pueden volverse complejas, se recomienda considerar la migración a una versión más reciente cuando sea posible.
6. Consideraciones y mejores prácticas
La función ROW_NUMBER() de MySQL y los métodos alternativos que utilizan variables de usuario son muy útiles, pero hay puntos que deben considerarse para operarlos de manera precisa y eficiente. Aquí se explican en detalle los aspectos a considerar y las mejores prácticas para la optimización del rendimiento.
6-1. Consideraciones de rendimiento
1. Carga del ORDER BY
función ROW() debe usarse siempre en combinación con ORDER BY. Este proceso implica reordenar los datos, por lo que en conjuntos de datos grandes el tiempo de procesamiento aumenta. Medidas:
Uso de índices: Establezca índices en las columnas usadas en ORDER BY para acelerar el proceso de ordenación.
Uso de LIMIT: Obtenga solo la cantidad de datos necesaria y reduzca el volumen de datos a procesar.
Ejemplo:
SELECT
employee,
sale,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY sale DESC) AS rank
FROM
sales
LIMIT 1000;
2. Aumento del uso de memoria y de I/O de disco
Las funciones de ventana utilizan tablas temporales y memoria para procesarse, por lo que al aumentar la cantidad de datos también aumentan el consumo de memoria y el I/O de disco. Medidas:
División de consultas: Divida el procesamiento en consultas más pequeñas y extraiga los datos de forma incremental para reducir la carga.
Uso de tablas temporales: Almacene los datos extraídos en una tabla temporal y realice la agregación desde allí para distribuir la carga.
6-2. Puntos clave para el afinado de consultas
1. Verificación del plan de ejecución
En MySQL se puede usar la sentencia EXPLAIN para verificar el plan de ejecución de una consulta. Esto permite comprobar si los índices se están utilizando correctamente. Ejemplo:
EXPLAIN
SELECT
employee,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY sale DESC) AS rank
FROM
sales;
Ejemplo de salida:
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
1
SIMPLE
sales
index
NULL
sale
4
NULL
500
Using index
De esta manera, si se muestra Using index, indica que el índice se está utilizando adecuadamente.
2. Optimización de índices
Asegúrese de crear índices en las columnas usadas en cláusulas ORDER BY y WHERE. Preste especial atención a los siguientes puntos.
Índice de columna única: Aplicable cuando la condición de ordenación es simple
Índice compuesto: Útil cuando la condición incluye varias columnas
Ejemplo:
CREATE INDEX idx_department_sale ON sales(department, sale DESC);
3. Uso de procesamiento por lotes
En lugar de procesar una gran cantidad de datos de una sola vez, use procesamiento por lotes para dividir los datos y distribuir la carga. Ejemplo:
SELECT * FROM sales WHERE department = '営業部' LIMIT 1000 OFFSET 0;
SELECT * FROM sales WHERE department = '営業部' LIMIT 1000 OFFSET 1000;
6-3. Mantenimiento de la integridad de los datos
1. Actualización y recálculo de datos
La adición o eliminación de datos puede desalinear la numeración. Por lo tanto, se debe establecer un mecanismo para recalcular los datos numerados cuando sea necesario. Ejemplo:
CREATE VIEW ranked_sales AS
SELECT
employee,
sale,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY sale DESC) AS rank
FROM
sales;
Al utilizar una vista, se puede mantener el ranking basado en los datos más recientes de forma constante.
6-4. Mejores prácticas de ejemplos de consultas
A continuación se muestra un ejemplo de mejores prácticas considerando rendimiento y mantenibilidad. Ejemplo: extracción de los N primeros registros
WITH RankedSales AS (
SELECT
employee,
department,
sale,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY sale DESC) AS rank
FROM
sales
)
SELECT *
FROM RankedSales
WHERE rank <= 3;
Esta estructura utiliza expresiones de tabla comunes (CTE) para mejorar la legibilidad y reutilización del código.
Resumen
Al utilizar la función ROW_NUMBER() o sus métodos alternativos, es importante tener en cuenta los siguientes puntos.
Optimización de índices para mejorar la velocidad de procesamiento.
Verificación del plan de ejecución para identificar cuellos de botella de rendimiento.
Adaptación a actualizaciones de datos considerando la implementación de mecanismos para mantener la integridad.
Procesamiento por lotes y CTE para distribuir la carga.
Al aplicar estas mejores prácticas, el análisis de datos a gran escala y la generación de informes pueden realizarse de manera eficiente.
7. Resumen
En los artículos anteriores, nos hemos centrado en la función ROW_NUMBER(), explicando en detalle desde su uso básico hasta ejemplos avanzados, métodos alternativos para versiones anteriores, y consideraciones y mejores prácticas. En esta sección, repasaremos los puntos clave de todo el artículo y resumiremos los puntos prácticos de aplicación.
7-1. Ventajas de la función ROW_NUMBER()
función ROW_NUMBER() es muy útil en el análisis de datos y la creación de informes por los siguientes motivos.
Asignación de números secuenciales por grupo: Permite realizar fácilmente rankings de ventas por departamento y clasificación por categoría.
Extracción de los N primeros registros: Permite extraer y filtrar datos basados en condiciones específicas de manera eficiente.
Detección y eliminación de datos duplicados: Es útil para la organización y limpieza de datos.
Estas funciones permiten escribir consultas complejas de forma sencilla, mejorando significativamente la legibilidad y mantenibilidad del SQL.
7-2. Comparación con otras funciones de ventana
En comparación con funciones de ventana como RANK() o DENSE_RANK(), ROW_NUMBER() se diferencia al asignar un número único a cada valor idéntico.
Función
Características
Uso
ROW_NUMBER()
Asigna un número secuencial único a cada fila
Ideal cuando se necesita identificar datos o clasificar sin permitir duplicados
RANK()
Asigna el mismo número a filas con el mismo rango y salta el siguiente rango
Útil cuando se considera la duplicación en el ranking y los huecos de posición son importantes
DENSE_RANK()
Asigna el mismo número a filas con el mismo rango sin saltar rangos
Ranking que mantiene la continuidad de los rangos mientras considera duplicados
Selección adecuada de la función: Elegir la función óptima según el caso de uso permite un procesamiento de datos eficiente.
7-3. Estrategias para versiones anteriores
En entornos con MySQL inferior a 8.0, también se mostró cómo utilizar variables de usuario para lograr funcionalidades similares. Sin embargo, es necesario considerar los siguientes puntos al emplear estos métodos.
Reducción de la legibilidad debido a la mayor complejidad del código
Puede ser difícil optimizar el procesamiento de consultas
Se requieren procesos adicionales para mantener la integridad de los datos
Si es posible, se recomienda encarecidamente migrar a MySQL 8.0 o superior y utilizar funciones de ventana.
7-4. Puntos clave para la optimización del rendimiento
Uso de índices: Configurar índices en las columnas utilizadas en ORDER BY para mejorar la velocidad de procesamiento.
Revisión del plan de ejecución: Utilizar la sentencia EXPLAIN para validar el rendimiento anticipadamente.
Adopción de procesamiento por lotes: Dividir el procesamiento de conjuntos de datos grandes en partes más pequeñas para distribuir la carga.
Uso de vistas y CTE: Aumentar la reutilización del código y simplificar la organización de consultas complejas.
Al aplicar estas técnicas de optimización, es posible lograr un procesamiento de datos eficiente y estable.
7-5. Conclusión
función ROW_NUMBER() es una herramienta poderosa que mejora significativamente la eficiencia del análisis de datos.
En este artículo, se explicó detalladamente desde la sintaxis básica hasta ejemplos avanzados, consideraciones y métodos alternativos.
Invitamos a los lectores a probar el contenido del artículo ejecutando consultas en la práctica. Al mejorar sus habilidades en SQL, podrán abordar con confianza análisis de datos y generación de informes más complejos.