Cálculo del porcentaje de una fila sobre la suma total


13

Disculpas por el mal título, no estaba seguro de cuál sería un buen título para esto.

Esta es actualmente (vista simplificada de) los datos con los que estoy trabajando

Agent    |  Commission     
---------|------------
Smith    |    100
Neo      |    200
Morpheus |    300

Necesito calcular el porcentaje de la comisión total, cada agente es responsable.

Entonces, para el Agente Smith, el Porcentaje se calcularía como (Agent Smith's commission / Sum(commission)*100

Entonces, mis datos esperados serían

Agent    |  Commission   |  % Commission    
---------|---------------|---------------
Smith    |    100        |     17
Neo      |    200        |     33
Morpheus |    300        |     50

Tengo una función que devuelve la comisión para cada agente. Tengo otra función que devuelve el porcentaje como (Commission/Sum(Commission))*100. El problema es que Sum(commission)se calcula para todas y cada una de las filas, y dado que esta consulta se ejecutaría en un Data Warehouse, el conjunto de datos sería bastante grande (actualmente, está por debajo de 2000 registros) y, sinceramente, un mal enfoque (IMO )

¿Hay alguna manera de que Sum(Commission)no se calcule para cada fila que se va a buscar?

Estaba pensando algo en las líneas de una consulta de 2 partes, la primera parte buscaría la sum(commission)variable / tipo de paquete y la segunda parte se referiría a este valor precalculado, pero no estoy seguro de cómo puedo lograr esto.

Estoy limitado a usar SQL y estoy ejecutando Oracle 10g R2.


Obviamente, no es una pregunta de DBA (¿tal vez si se tratara de espacios de tablas en lugar de vendedores?), Probablemente debería estar en Stack Overflow.
Gaius

Respuestas:


23

Estás buscando el analytical function ratio_to_report

select 
  agent,
  round(ratio_to_report(commission) over ()*100) "% Comm."
from  
  commissions;

Impresionante, no sabía sobre esto, gracias!
Sathyajith Bhat

9

Para devolver todos los agentes con sus comisiones y porcentajes de comisión, use una función analítica sin cláusula analítica para que la partición esté sobre toda la tabla:

SELECT Agent, commission, 100* commission / (SUM(commission) OVER ()) "% Commission" 
FROM commissions;

Como aprendí de René Nyffenegger (+1), la función ratio_to_report refuerza esta sintaxis.

El uso de un paquete para almacenar la SUMA de la Comisión implicaría PL / SQL, que excluyó específicamente al indicar que desea una solución SQL, pero dado que ya está utilizando funciones, supongo que su intención no era excluir PL / SQL. Si este es el caso, la solución del paquete puede ayudar, pero depende de cómo funcione su aplicación.

Cuando su sesión se crea por primera vez y llama a la función en el paquete para obtener la comisión, hay una llamada implícita al constructor de paquetes que podría obtener la suma y almacenarla. Luego, podría hacer referencia a la suma almacenada en su función de obtener comisión y solo tendría que hacer la suma una vez. Por supuesto, tan pronto como llame a la función desde una sesión diferente, la suma se calculará nuevamente. Además, llamar a la función para cada agente sería considerablemente menos eficiente que llamar a una declaración SQL para todos los agentes si su aplicación se puede diseñar de esa manera.

Es posible que desee considerar convertir su función en un procedimiento que devuelva un cursor para la consulta anterior o tal vez tenga una función que devuelva los resultados de la consulta como un conjunto de resultados canalizados.

Data de muestra:

create table commissions (Agent Varchar2(100), Commission Number(3));
insert into commissions values ('Smith',100);
insert into commissions values ('Neo',200);
insert into commissions values ('Morpheus',300);

5

Puede intentar la siguiente consulta, la suma (comisión) solo se calculará una vez:

WITH TOTAL_COMMISSION AS 
(SELECT SUM(COMMISSION) AS TOTAL FROM AGENTS)
SELECT A.AGENT_NAME, A.COMMISSION, ((A.COMMISSION/T.TOTAL)*100) AS "% COMMISSION"
FROM AGENTS A, TOTAL_COMMISSION T;

Eso funciona y devuelve los datos correctos, pero es menos eficiente que una función analítica que realiza un escaneo completo de la tabla en lugar de dos (suponiendo que no haya índices).
Leigh Riffel

1
@Leigh ~ ¿Cómo puede hacerlo en una pasada ya que la forma manual requiere dos pasadas? No puedo ver cómo las computadoras pueden hacer la operación mágica de una sola pasada% ofTotal un ...
jcolebrand

@jcolebrand Los datos solo se leen de los bloques de la base de datos una vez. Probablemente esté haciendo múltiples pases de sus resultados en memoria, pero esto generalmente es más rápido que leer los bloques de la base de datos dos veces. Hay compensaciones en la memoria y la CPU entre estas opciones, por lo que la elección no siempre es clara, pero en este caso creo que sí.
Leigh Riffel

1
@Leigh ~~ Sí, una mayor consideración me llevaría a creer que eso es todo lo que podría estar haciendo, solo optimizaciones de caja negra. De todos modos, una solución ingeniosa en su respuesta. Gracias: D
jcolebrand

0
  select 
  Agent, Commission,
  (
      ROUND(
       (Commission *100) / 
          (
            (SELECT SUM(Commission)
             FROM commissions AS A)
          )
       ) 
  ) AS Porcentaje
  from  
  commissions
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.