Antes de empezar, imagina que estás currando con una tabla de miles de filas de ventas. Tu tarea: averiguar quién es el vendedor número uno en cada categoría, quién es el segundo, y así sucesivamente. O, por ejemplo, necesitas numerar todas las filas en una consulta para seguir el orden. Todo esto se hace fácil usando funciones window.
Las funciones window son funciones SQL que trabajan con un subconjunto de filas (vamos a llamarlo "ventana") de un conjunto de datos. A diferencia de las funciones agregadas, que agrupan filas en una sola (por ejemplo, SUM() o AVG()), las funciones window dejan las filas tal cual, añadiendo valores calculados a cada una.
Diferencia con funciones agregadas
Las funciones agregadas "comprimen" los datos, agrupando filas:
SELECT department, COUNT(*)
FROM employees
GROUP BY department;
Resultado: solo un par de filas, según el número de departamentos.
Comparado con una función window — aquí las filas se quedan en su sitio, pero se añade un nuevo campo, por ejemplo, ROW_NUMBER():
SELECT employee_name, department,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_within_department
FROM employees;
Aquí vas a tener todas las mismas filas, pero con una columna extra rank_within_department, donde a cada empleado se le asigna un número dentro de su departamento.
Funciones window principales
Sintaxis de OVER()
La parte más importante de cada función window es la palabra mágica OVER(). Esto define con qué "ventana" de datos va a currar la función. Dentro de OVER() puedes poner división en grupos (PARTITION BY) y/o orden de clasificación (ORDER BY).
Sintaxis general:
<función_window>() OVER (
[PARTITION BY <grupo>]
[ORDER BY <orden>]
)
Componentes:
PARTITION BY: Divide las filas en grupos. Por ejemplo, "divide los datos por departamentos".ORDER BY: Indica el orden de procesamiento de las filas. Por ejemplo, "ordena los empleados por salario de mayor a menor".
Función ROW_NUMBER()
La función ROW_NUMBER() numera las filas, empezando desde 1, dentro de la "ventana" indicada. A veces viene bien para crear un número de fila en una tabla temporal o para saber la posición de un registro.
Ejemplo. Tabla sales (ventas):
| id | product_category | seller_name | revenue |
|---|---|---|---|
| 1 | Electronics | Alice | 1000 |
| 2 | Electronics | Bob | 850 |
| 3 | Furniture | Alice | 1200 |
| 4 | Furniture | Charlie | 1100 |
| 5 | Electronics | Dana | 750 |
Consulta:
SELECT seller_name, product_category, revenue,
ROW_NUMBER() OVER (PARTITION BY product_category ORDER BY revenue DESC) AS row_number
FROM sales;
Resultado:
| seller_name | product_category | revenue | row_number |
|---|---|---|---|
| Alice | Electronics | 1000 | 1 |
| Bob | Electronics | 850 | 2 |
| Dana | Electronics | 750 | 3 |
| Alice | Furniture | 1200 | 1 |
| Charlie | Furniture | 1100 | 2 |
Cómo funciona esto:
- Los datos se dividen en grupos por
product_category. - Cada grupo se ordena por
revenue(de mayor a menor). - Las filas dentro de cada grupo reciben un número de orden.
Función RANK()
La función RANK() se usa para rankear filas. A diferencia de ROW_NUMBER(), tiene en cuenta los valores iguales y salta números (ranks) si hay empates.
Ejemplo:
SELECT seller_name, product_category, revenue,
RANK() OVER (PARTITION BY product_category ORDER BY revenue DESC) AS rank
FROM sales;
Resultado:
| seller_name | product_category | revenue | rank |
|---|---|---|---|
| Alice | Electronics | 1000 | 1 |
| Bob | Electronics | 850 | 2 |
| Dana | Electronics | 750 | 3 |
| Alice | Furniture | 1200 | 1 |
| Charlie | Furniture | 1100 | 2 |
Función DENSE_RANK()
DENSE_RANK() es parecida a RANK(), salvo por un detalle: no salta números de rank si hay valores iguales.
Ejemplo. Añadimos una venta con el mismo revenue:
| id | product_category | seller_name | revenue |
|---|---|---|---|
| 6 | Electronics | Alice | 1000 |
| 7 | Electronics | Dana | 750 |
Consulta:
SELECT seller_name, product_category, revenue,
DENSE_RANK() OVER (PARTITION BY product_category ORDER BY revenue DESC) AS dense_rank
FROM sales;
Resultado:
| seller_name | product_category | revenue | dense_rank |
|---|---|---|---|
| Alice | Electronics | 1000 | 1 |
| Alice | Electronics | 1000 | 1 |
| Bob | Electronics | 850 | 2 |
| Dana | Electronics | 750 | 3 |
Ejemplos de uso: numeración de filas
Tarea: numerar todos los pedidos en la tabla orders, ordenados por fecha.
SELECT order_id, customer_name, order_date,
ROW_NUMBER() OVER (ORDER BY order_date) AS order_number
FROM orders;
Resultado: tienes la lista de pedidos con numeración según el orden de realización.
Ejemplos de uso: top-3 vendedores en cada categoría
Tarea: sacar los tres mejores vendedores en cada categoría de productos.
WITH ranked_sales AS (
SELECT seller_name, product_category, revenue,
RANK() OVER (PARTITION BY product_category ORDER BY revenue DESC) AS rank
FROM sales
)
SELECT seller_name, product_category, revenue
FROM ranked_sales
WHERE rank <= 3;
Ejemplos de uso: detectar valores iguales
Tarea: averiguar si hay vendedores con el mismo revenue en cada categoría.
SELECT seller_name, product_category, revenue,
DENSE_RANK() OVER (PARTITION BY product_category ORDER BY revenue DESC) AS dense_rank
FROM sales;
Ahora puedes ver los ranks donde los valores "se quedan pegados".
GO TO FULL VERSION