Berechnung des Prozentsatzes einer Zeile über der Gesamtsumme

13

Entschuldigung für den schlechten Titel, ich war mir nicht sicher, was ein guter Titel dafür sein würde.

Dies sind derzeit (vereinfachte Ansicht der) Daten, mit denen ich arbeite

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

Ich muss den Prozentsatz der gesamten Provision berechnen, für den jeder Agent verantwortlich ist.

Für Agent Smith würde der Prozentsatz also wie folgt berechnet werden (Agent Smith's commission / Sum(commission)*100

Meine erwarteten Daten wären also

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

Ich habe eine Funktion, die die Provision für jeden Agenten zurückgibt. Ich habe eine andere Funktion, die den Prozentsatz als zurückgibt (Commission/Sum(Commission))*100. Das Problem ist, dass dies Sum(commission)für jede einzelne Zeile berechnet wird. Angesichts der Tatsache, dass diese Abfrage in einem Data Warehouse ausgeführt würde, wäre der Datensatz ziemlich umfangreich (derzeit sind es knapp 2000 Datensätze) und ganz ehrlich, ein schlechter Ansatz (IMO) ).

Gibt es eine Möglichkeit, das Sum(Commission)nicht für jede abgerufene Zeile berechnen zu lassen?

Ich dachte etwas in den Zeilen einer zweiteiligen Abfrage, der erste Teil würde das sum(commission)in eine Paketvariable / -typ holen und der zweite Teil würde sich auf diesen vorberechneten Wert beziehen, aber ich bin nicht sicher, wie ich dies erreichen kann.

Ich kann nur SQL verwenden und arbeite mit Oracle 10g R2.

Sathyajith Bhat
quelle
Keine offensichtliche DBA-Frage (vielleicht waren es eher Tablespaces als Verkäufer?) - sollte wahrscheinlich Stack Overflow sein.
Gaius

Antworten:

23

Sie suchen nach dem analytical function ratio_to_report

select 
  agent,
  round(ratio_to_report(commission) over ()*100) "% Comm."
from  
  commissions;
René Nyffenegger
quelle
Super, wusste nichts davon, danke!
Sathyajith Bhat
9

Um alle Agenten mit ihren Provisionen und Provisionsprozentsätzen zurückzugeben, verwenden Sie eine Analysefunktion ohne Analyseklausel, sodass sich die Partition über die gesamte Tabelle erstreckt:

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

Wie ich von René Nyffenegger (+1) gelernt habe, verschärft die ratio_to_report-Funktion diese Syntax.

Die Verwendung eines Pakets zum Speichern der Provisionssumme würde PL / SQL umfassen, das Sie ausdrücklich ausgeschlossen haben, indem Sie angegeben haben, dass Sie eine SQL-Lösung wünschen. Da Sie jedoch bereits Funktionen verwenden, gehe ich davon aus, dass Sie PL / SQL nicht ausschließen wollten. In diesem Fall kann die Paketlösung hilfreich sein, hängt jedoch davon ab, wie Ihre Anwendung funktioniert.

Wenn Ihre Sitzung zum ersten Mal erstellt wird und die Funktion im Paket aufruft, um die Provision zu erhalten, wird implizit der Paketkonstruktor aufgerufen, der die Summe abrufen und speichern kann. Dann könnten Sie die gespeicherte Summe in Ihrer Funktion "Provision erhalten" referenzieren und sie müsste nur einmal ausgeführt werden. Sobald Sie die Funktion aus einer anderen Sitzung aufrufen, wird die Summe natürlich erneut berechnet. Das Aufrufen der Funktion für jeden Agenten ist außerdem erheblich weniger effizient als das Aufrufen einer SQL-Anweisung für alle Agenten, wenn Ihre Anwendung auf diese Weise entworfen werden kann.

Möglicherweise möchten Sie Ihre Funktion in eine Prozedur verwandeln, die einen Cursor für die obige Abfrage zurückgibt, oder eine Funktion, die die Ergebnisse der Abfrage als Ergebnismenge in Form einer Pipeline zurückgibt.

Beispieldaten:

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);
Leigh Riffel
quelle
5

Sie könnten die folgende Abfrage versuchen, Summe (Provision) wird nur einmal berechnet:

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;
Robert Durgin
quelle
Dies funktioniert und gibt die korrekten Daten zurück, ist jedoch weniger effizient als eine Analysefunktion, die einen vollständigen Tabellenscan anstelle von zwei durchführt (sofern keine Indizes angenommen werden).
Leigh Riffel
1
@Leigh ~ Wie kann es in einem Durchgang gemacht werden, da der manuelle Weg zwei Durchgänge erfordert? Ich kann nicht sehen, wie Computer% von Total zu einer magischen One-Pass-Operation machen können ...
jcolebrand
@jcolebrand Die Daten werden nur einmal aus den Datenbankblöcken gelesen. Es werden wahrscheinlich mehrere Durchläufe der Ergebnisse im Speicher ausgeführt, dies ist jedoch im Allgemeinen schneller als das zweimalige Lesen der Datenbankblöcke. Es gibt Kompromisse zwischen Speicher und CPU zwischen diesen Optionen, daher ist die Auswahl möglicherweise nicht immer eindeutig, aber in diesem Fall denke ich, dass dies der Fall ist.
Leigh Riffel
1
@Leigh ~~ Ja, weitere Überlegungen lassen mich glauben, dass das alles ist, was es tun könnte, nur Blackbox-gezitterte Optimierungen. Wie auch immer, eine raffinierte Lösung in Ihrer Antwort. Danke: D
jcolebrand
0
  select 
  Agent, Commission,
  (
      ROUND(
       (Commission *100) / 
          (
            (SELECT SUM(Commission)
             FROM commissions AS A)
          )
       ) 
  ) AS Porcentaje
  from  
  commissions
JoeDeg
quelle