Bueno, para esto podemos utilizar varias técnicas como realizar sub-consultas, realizar un proceso que llene una temporal y realice los cálculos, pero existe una forma muy sencilla y no compleja de usar que es el comando OVER.
Con el comando OVER podemos obtener resultados de funciones de grupo de una determinada partición de datos.
Como podemos usar el comando OVER?
Para contestar esta pregunta mejor veamos un ejemplo:
EJEMPLO USANDO EL COMANDO OVER
Tenemos información de almacenes vs ventas y deseamos obtener la siguiente información: Almacen, ventas, cantidades y % de Ventas con relación al total.
ALMACEN_ID | VALOR_VENTAS | CANTIDAD_VENTAS | % VENTAS |
ALM_30 | 150.000 | 15 | 33,33% |
ALM_10 | 100.000 | 10 | 22,22% |
ALM_20 | 200.000 | 20 | 44,44% |
TOTAL | 450.000 | 45 | 100,00% |
- Creamos la tablaCreate Table Almacenes_Ventas
(
Almacen_IdVarchar2 (10),
Valor_Ventas Number,
Cantidad_Ventas Number
)
- Insertamos los datosInsert Into Almacenes_Ventas
(Almacen_Id,Valor_Ventas, Cantidad_Ventas)
Values ('ALM_30', 150000, 15);
Insert Into Almacenes_Ventas
(Almacen_Id, Valor_Ventas, Cantidad_Ventas)
Values('ALM_10', 100000, 10);
Insert Into Almacenes_Ventas
(Almacen_Id,Valor_Ventas, Cantidad_Ventas)
Values ('ALM_20',200000, 20);
Commit;
- Realizamos la consultaSelect v.*,
v.valor_ventas/sum(valor_ventas)over()*100"% Ventas",
sum(valor_ventas)over()gran_total
From Almacenes_Ventas V
ALMACEN_ID | VALOR_VENTAS | CANTIDAD_VENTAS | % Ventas | GRAN_TOTAL |
ALM_30 | 150.000 | 15 | 33,33333333 | 450.000 |
ALM_10 | 100.000 | 10 | 22,22222222 | 450.000 |
ALM_20 | 200.000 | 20 | 44,44444444 | 450.000 |
Vemos que la columna GRAN_TOTAL de la consulta, es el mismo valor en las tres filas ya que indicamos por medio del OVER que es la suma del total de registros en el campo VALOR_VENTAS. Ya con lo definido en con el OVER podemos obtener el campo "% Ventas".
Fíjense que aunque usamos la función de grupo SUM no estamos usando declaración GROUP BY en nuestra consulta SQL.
EJEMPLO USAANDO EL COMANDO OVER (PARTITION BY)
Al ejemplo anterior agreguemos un campo periodo para tener las ventas de los almacenes por mes.
PERIODO | ALMACEN_ID | VALOR_VTA | % VTA PERIODO | % VTA |
201101 | ALM_10 | 10.000 | 22,22% | 9,52% |
201101 | ALM_20 | 15.000 | 33,33% | 14,29% |
201101 | ALM_30 | 20.000 | 44,44% | 19,05% |
Total 201101 | 45.000 | 100,00% | 42,86% | |
201102 | ALM_10 | 20.000 | 33,33% | 19,05% |
201102 | ALM_20 | 40.000 | 66,67% | 38,10% |
Total 201102 | 60.000 | 100,00% | 57,14% | |
GRAN TOTAL | 105.000 | 100,00% |
Ahora vemos que la información que necesitamos mostrar es más compleja. "% VTA Periodo" es el porcentaje de ventas con relación al total del periodo y "% VTA" es el % del total de las ventas.
Ahora veamos como lo podemos resolver con una consulta SQL.
- Creamos la tablaCreate Table Almacenes_Ventas_Periodo
(
Periodo varchar2(6),
Almacen_Id Varchar2 (10),
Valor_Ventas Number,
Cantidad_Ventas Number
)
- Insertamos los registrosInsert into ALMACENES_VENTAS_PERIODO
(PERIODO,ALMACEN_ID, VALOR_VENTAS, CANTIDAD_VENTAS)
Values ('201101', 'ALM_30', 20000, 30);
Insert into ALMACENES_VENTAS_PERIODO
(PERIODO, ALMACEN_ID, VALOR_VENTAS, CANTIDAD_VENTAS)
Values ('201101','ALM_10', 10000, 10);
Insert into ALMACENES_VENTAS_PERIODO
(PERIODO, ALMACEN_ID, VALOR_VENTAS, CANTIDAD_VENTAS)
Values ('201101','ALM_20', 15000,20);
Insert into ALMACENES_VENTAS_PERIODO
(PERIODO, ALMACEN_ID, VALOR_VENTAS, CANTIDAD_VENTAS)
Values ('201102','ALM_10', 20000, 20);
Insert into ALMACENES_VENTAS_PERIODO
(PERIODO, ALMACEN_ID, VALOR_VENTAS, CANTIDAD_VENTAS)
Values ('201102','ALM_20', 40000, 40);
COMMIT;
- Realizamos la consultaSelectV.*,
Round(Valor_Ventas/Sum(Valor_Ventas) Over (Partition By Periodo) * 100, 2)
"%Periodo",
Round(Valor_Ventas/Sum(Valor_Ventas) Over() * 100, 2) "%Total",
Sum(Valor_Ventas) Over (Partition By Periodo) Periodo_Total,
Sum(Valor_Ventas) Over () Gran_Total
From Almacenes_Ventas_Periodo V
Order By Periodo, Almacen_Id
--Resultado
PERIODO | ALMACEN_ID | VALOR_VENTAS | %Periodo | %Total | PERIODO_TOTAL | GRAN_TOTAL |
201101 | ALM_10 | 10000 | 22,22 | 9,52 | 45.000 | 105.000 |
201101 | ALM_20 | 15000 | 33,33 | 14,29 | 45.000 | 105.000 |
201101 | ALM_30 | 20000 | 44,44 | 19,05 | 45.000 | 105.000 |
201102 | ALM_10 | 20000 | 33,33 | 19,05 | 60.000 | 105.000 |
201102 | ALM_20 | 40000 | 66,67 | 38,1 | 60.000 | 105.000 |
El campo GRAN_TOTAL es el total de todos los registros del campo VALOR_VENTAS y PERIODO_TOTAL es el total del campo VALOR_VENTAS pero de la agrupación PERIODO. Aquí vemos que agregamos al OVER la clausula PARTITION BY que no indica sobre que partición de datos deseamos realizar la operación de grupo (SUM, AVG, MAX, MIN, ETC.).
Para obtener la fila "%Periodo" debemos dividir VALOR_VENTAS para el total de ventas del grupo periodo "Sum(Valor_Ventas) Over (Partition By Periodo)" y oara obtener el "%Total" debemos dividir VALOR_VENTAS para el gran total "Sum (Valor_Ventas)Over ()".
CONCLUSION:
Es claro que la utilidad OVER nos ayuda a simplificar la generación de consultas complejas y elaborar mejor nuestros cubos de datos. Solo hay que ponerle cabeza para utilizar de la mejor manera estas clausulas.
Muy buenos ejemplos, fáciles de entender y de gran utilidad. Muchas gracias.
ResponderEliminarGOOD!
ResponderEliminarEstimado Victor: es la mejor explicación que he hallado. Excelente muchas gracias. Tienes cualidades para la docencia. Sigue publicando.
ResponderEliminarSaludos, Raúl
Muchas gracias!!
EliminarMuy bien explicado. ; )
ResponderEliminarGracias
Gracias ojalá hubiera encontrado este blog mucho antes JEJEJE.
ResponderEliminarGrandiosa explicación!
ResponderEliminarMuchas gracias.
Muchas gracias, una pregunta: si Sum(Valor_Ventas) = 0, ¿cómo validarlo, para evitar el error division por cero?
ResponderEliminarMuy buena la explicación, solo tengo una duda, ¿A que se debe que al sumar el porcentaje la suma sea mas de 100 %?, realice algunas pruebas en mi código y en algunos casos o es menor o mayor a 100%
ResponderEliminarPuede que en la %Total, la suma de mas de 100, por el redondeo a 2, pero si quitas el round ahí si la suma debe de darte 100 exacto
EliminarSaludos
Muy útil y claro! Muchas gracias
ResponderEliminarGracias
ResponderEliminar