Calcolo della percentuale di una riga sulla somma totale


13

Mi scuso per il cattivo titolo, non ero sicuro di quale sarebbe stato un buon titolo per questo.

Attualmente sono (vista semplificata dei) dati con cui sto lavorando

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

Devo calcolare la percentuale della commissione totale, di cui ogni agente è responsabile.

Quindi, per l'agente Smith, la percentuale sarebbe calcolata come (Agent Smith's commission / Sum(commission)*100

Quindi, i miei dati previsti sarebbero

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

Ho una funzione che restituisce la commissione per ciascun agente. Ho un'altra funzione che restituisce la percentuale come (Commission/Sum(Commission))*100. Il problema è che Sum(commission)viene calcolato per ogni riga e dato che questa query verrebbe eseguita su un data warehouse, il set di dati sarebbe piuttosto grande (attualmente, è poco meno di 2000 record) e, onestamente, un approccio errato (IMO ).

C'è un modo per Sum(Commission)non calcolare per ogni riga che viene recuperata?

Stavo pensando a qualcosa sulla linea di una query in 2 parti, la prima parte avrebbe recuperato la sum(commission)variabile in un tipo / tipo di pacchetto e la seconda parte si sarebbe riferita a questo valore pre-calcolato, ma non sono sicuro di come posso ottenere questo.

Sono limitato all'utilizzo di SQL e utilizzo Oracle 10g R2.


Ovviamente non è una domanda DBA (forse se si trattasse di tablespace piuttosto che di venditori?) - dovrebbe probabilmente essere in Stack Overflow.
Gaius,

Risposte:


23

Stai cercando analytical function ratio_to_report

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

Fantastico, non lo sapevo, grazie!
Sathyajith Bhat,

9

Per restituire tutti gli agenti con le loro commissioni e percentuali di commissione utilizzare una funzione analitica senza clausole analitiche in modo che la partizione si trovi su tutta la tabella:

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

Come ho appreso da René Nyffenegger (+1), la funzione ratio_to_report restringe questa sintassi.

L'uso di un pacchetto per archiviare il SUM della Commissione comporterebbe PL / SQL, che è stato espressamente escluso indicando che si desidera una soluzione SQL, ma poiché si stanno già utilizzando funzioni presumo che l'intenzione non fosse quella di escludere PL / SQL. In questo caso, la soluzione del pacchetto può essere d'aiuto, ma dipende da come funziona l'applicazione.

Quando la tua sessione viene creata per la prima volta e chiama la funzione nel pacchetto per ottenere la commissione, c'è una chiamata implicita al costruttore dei pacchetti che potrebbe ottenere la somma e memorizzarla. Quindi puoi fare riferimento alla somma memorizzata nella tua funzione get commission e dovrebbe fare la somma una sola volta. Naturalmente, non appena si chiama la funzione da una sessione diversa, la somma verrà nuovamente calcolata. Inoltre, la chiamata della funzione per ogni agente sarebbe notevolmente meno efficiente rispetto alla chiamata di un'istruzione SQL per tutti gli agenti se l'applicazione può essere progettata in quel modo.

È possibile considerare la possibilità di trasformare la propria funzione in una procedura che restituisce un cursore per la query sopra o magari avere una funzione che restituisce i risultati della query come set di risultati con pipeline.

Dati di esempio:

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

Puoi provare la seguente query, la somma (commissione) verrà calcolata una sola volta:

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;

Funziona e restituisce i dati corretti, ma è meno efficiente di una funzione analitica che esegue una scansione completa della tabella anziché due (presupponendo che non vi siano indici).
Leigh Riffel,

1
@Leigh ~ Come può farlo in un passaggio poiché il modo manuale richiede due passaggi? Non riesco a vedere come i computer potrebbero rendere% ofTotal un'operazione magica con un solo passaggio ...
jcolebrand

@jcolebrand I dati vengono letti dai blocchi del database solo una volta. Probabilmente sta facendo più passaggi dei suoi risultati in memoria, ma questo è generalmente più veloce della lettura dei blocchi del database due volte. Ci sono compromessi nella memoria e nella CPU tra queste opzioni, quindi la scelta potrebbe non essere sempre chiara, ma in questo caso penso che lo sia.
Leigh Riffel,

1
@Leigh ~~ Sì, un'ulteriore considerazione mi indurrebbe a credere che è tutto ciò che potrebbe fare, solo ottimizzazioni agitate di black box. Comunque, una soluzione elegante nella tua risposta. Grazie: D
jcolebrand

0
  select 
  Agent, Commission,
  (
      ROUND(
       (Commission *100) / 
          (
            (SELECT SUM(Commission)
             FROM commissions AS A)
          )
       ) 
  ) AS Porcentaje
  from  
  commissions
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.