OVER() es una instrucción que le dice a SQL sobre qué conjunto de filas debe aplicar una función de ventana. Se puede decir que es una forma de definir la "ventana" de datos para aplicar la función de ventana. Imagina que tienes una sala llena de personas y quieres contar cuántas personas hay en cada metro cuadrado del suelo. OVER() te indica en qué parte exacta de la sala te vas a enfocar. O sea, define sobre qué conjunto de filas va a trabajar la función.
El operador OVER() se usa exclusivamente con funciones de ventana para realizar operaciones sobre filas de una o varias tablas, sin agrupar los datos.
Sintaxis:
funcion_de_ventana() OVER (
[PARTITION BY ...]
[ORDER BY ...]
[ROWS/RANGE ...]
)
Dónde:
PARTITION BY— divide el conjunto de datos en grupos lógicosORDER BY— define el orden de las filas dentro de cada grupoROWS/RANGE— especifica el tamaño de la "ventana" (por ejemplo, la fila actual + 1 siguiente)
Ejemplo: OVER() sin parámetros
Cuando OVER() se usa sin parámetros adicionales, significa que la función que va antes de él va a trabajar sobre todo el conjunto de datos.
SELECT
employee_id,
salary,
ROW_NUMBER() OVER () AS row_num -- ROW_NUMBER() se aplicará a todas las filas del resultado
FROM employees;
¿Qué pasa aquí?
ROW_NUMBER()asigna un número único a cada fila.- Como en
OVER()no se indican parámetros, todas las filas de la tablaemployeesse procesan como un solo conjunto.
Resultado:
| employee_id | salary | row_num |
|---|---|---|
| 1 | 50000 | 1 |
| 2 | 60000 | 2 |
| 3 | 55000 | 3 |
Uso de PARTITION BY para definir grupos
Genial, ahora imagina que nos interesa numerar a los empleados no en toda la empresa, sino dentro de cada departamento. Aquí entra en juego PARTITION BY.
PARTITION BY dentro de OVER() divide los datos en grupos (o "particiones"). Para cada grupo, la función calcula el valor por separado. O sea, si ROW_NUMBER() fuera un camarero, empezaría a contar desde uno en cada "mesa" (partición).
Ejemplo: usando PARTITION BY
SELECT
department_id,
employee_id,
salary,
ROW_NUMBER() OVER (PARTITION BY department_id) AS row_num
FROM employees;
¿Qué pasa aquí?
- Los datos de la tabla
employeesse dividen en grupos según el valor dedepartment_id. - En cada grupo, las filas reciben un número de orden usando
ROW_NUMBER().
Resultado:
| department_id | employee_id | salary | row_num |
|---|---|---|---|
| 1 | 1 | 50000 | 1 |
| 1 | 3 | 55000 | 2 |
| 2 | 2 | 60000 | 1 |
Uso de ORDER BY para definir el orden
Ahora vamos a meterle un poco de estructura. Imagina que no solo queremos numerar las filas, sino hacerlo en un orden concreto, por ejemplo, empezando por el salario más alto. Esto se resuelve con ORDER BY.
ORDER BY define el orden en el que las filas serán procesadas por la función de ventana.
Ejemplo: usando ORDER BY dentro de OVER()
SELECT
department_id,
employee_id,
salary,
RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank
FROM employees;
¿Qué pasa aquí?
- Los datos se dividen en grupos (
PARTITION BY department_id). - Dentro de cada grupo, las filas se ordenan por salario descendente (
ORDER BY salary DESC). - A cada fila se le asigna un rango según el orden.
Resultado:
| department_id | employee_id | salary | rank |
|---|---|---|---|
| 1 | 3 | 55000 | 1 |
| 1 | 1 | 50000 | 2 |
| 2 | 2 | 60000 | 1 |
Combinando funciones de ventana
SQL te deja usar varias funciones de ventana en una sola consulta, y cada una puede trabajar con su propio conjunto de reglas. Es como si en una sala al mismo tiempo suena música y se cuenta la gente — ¡cada proceso va por su lado!
Ejemplo: varias funciones de ventana
SELECT
department_id,
employee_id,
salary,
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS row_num,
AVG(salary) OVER (PARTITION BY department_id) AS avg_salary
FROM employees;
¿Qué pasa aquí?
ROW_NUMBER()numera las filas en cada grupo por salario descendente.AVG()calcula el salario medio en cada grupo.
Resultado:
| department_id | employee_id | salary | row_num | avg_salary |
|---|---|---|---|---|
| 1 | 3 | 55000 | 1 | 52500 |
| 1 | 1 | 50000 | 2 | 52500 |
| 2 | 2 | 60000 | 1 | 60000 |
Ejemplos de la vida real
Las funciones de ventana con OVER() se usan en un montón de situaciones reales. Aquí tienes algunos ejemplos:
- Análisis de ventas: ranking de productos por cantidad de ventas en cada categoría.
- Rankings: determinar la posición de estudiantes en cada grupo según la nota media.
- Series temporales: suma acumulada de ventas a lo largo del tiempo.
Ejemplo de análisis de ventas:
SELECT
category_id,
product_id,
product_name,
SUM(sales) OVER (PARTITION BY category_id ORDER BY sales DESC) AS cumulative_sales
FROM products;
Errores comunes al trabajar con funciones de ventana
- Falta de
PARTITION BY
Si no usas PARTITION BY, la función de ventana se aplica a toda la tabla. Esto puede dar resultados inesperados, sobre todo si esperabas dividir por grupos.
💡 Asegúrate de indicar claramente cómo debe dividirse la tabla — por ejemplo, por usuario, pedido o categoría.
- Tipos de datos incorrectos en
ORDER BY
ORDER BY dentro de una función de ventana es sensible a los tipos de datos. Si ordenas por un campo de fecha guardado como texto (VARCHAR), el orden puede ser alfabético y no cronológico.
💡 Convierte esos campos al tipo correcto (DATE, INTEGER, etc.) antes de ordenar.
- Uso incorrecto de
ROWS BETWEEN
Por defecto, las funciones de ventana trabajan con marcos definidos por ROWS BETWEEN. Si no indicas el marco explícitamente, puede aplicarse el comportamiento de RANGE, que funciona diferente y puede devolver más filas de las que esperabas.
💡 Para tener control exacto, usa ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW si quieres un acumulado desde el principio hasta la fila actual.
- Manejo incorrecto de
NULL
Las funciones de ventana pueden tratar NULL de formas distintas. Por ejemplo, RANK() y DENSE_RANK() cuentan NULL como un valor y le asignan un rango aparte.
💡 Usa NULLS LAST o NULLS FIRST en ORDER BY si te importa dónde deben ir los NULL.
- Usar funciones de ventana agregadas en vez de las normales
A veces se usan funciones de ventana agregadas (SUM() OVER(...)) donde bastaría con agregados normales y GROUP BY, lo que complica la consulta y la hace más lenta.
💡 Usa funciones de ventana solo cuando necesites mantener el detalle por fila.
GO TO FULL VERSION