Расчет процента строки по общей сумме

13

Извиняюсь за плохое название, я не был уверен, что будет хорошим названием для этого.

В настоящее время это (упрощенное представление) данные, с которыми я работаю

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

Мне нужно рассчитать процент от общей комиссии, за которую отвечает каждый агент.

Таким образом, для агента Смита, процент будет рассчитываться как (Agent Smith's commission / Sum(commission)*100

Итак, мои ожидаемые данные будут

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

У меня есть функция возврата комиссии для каждого агента. У меня есть другая функция, возвращающая процент как (Commission/Sum(Commission))*100. Проблема заключается в том, что Sum(commission)вычисляется для каждой строки, и, учитывая, что этот запрос будет выполняться в хранилище данных, набор данных будет довольно большим (в настоящее время он составляет менее 2000 записей) и, честно говоря, плохим подходом (IMO). ).

Есть ли способ получить Sum(Commission)не рассчитать для каждой строки выборки?

Я думал что-то о строках запроса из двух частей: первая часть извлекала бы sum(commission)переменную / тип пакета, а вторая часть ссылалась бы на это предварительно вычисленное значение, но я не уверен, как мне это сделать.

Я ограничен использованием SQL, и я работаю на Oracle 10g R2.

Сатьяджит Бхат
источник
Не очевидно, что вопрос администратора баз данных (возможно, если бы это были табличные пространства, а не продавцы?) - вероятно, должен быть в переполнении стека.
Гай

Ответы:

23

Вы ищете analytical function коэффициент_to_report

select 
  agent,
  round(ratio_to_report(commission) over ()*100) "% Comm."
from  
  commissions;
Рене Ниффенеггер
источник
Круто, не знал об этом, спасибо!
Сатьяджит Бхат
9

Чтобы вернуть всем агентам их комиссионные и проценты комиссий, используйте аналитическую функцию без аналитического предложения, чтобы разделение занимало всю таблицу:

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

Как я узнал от Рене Ниффенеггера (+1), функция ratio_to_report ужесточает этот синтаксис.

Использование пакета для хранения Комиссии SUM потребует PL / SQL, который вы специально исключили, указав, что вы хотите решение SQL, но, поскольку вы уже используете функции, я предполагаю, что вы не намеревались исключать PL / SQL. Если это так, то пакетное решение может помочь, но это зависит от того, как работает ваше приложение.

Когда ваша сессия впервые создается и вызывает функцию в пакете для получения комиссии, происходит неявный вызов конструктора пакетов, который может получить сумму и сохранить ее. Затем вы можете сослаться на сохраненную сумму в функции получения комиссионных, и вам нужно будет сделать сумму только один раз. Конечно, как только вы вызовете функцию из другого сеанса, сумма будет рассчитана снова. Кроме того, вызов функции для каждого агента будет значительно менее эффективным, чем вызов одного оператора SQL для всех агентов, если ваше приложение может быть спроектировано таким образом.

Вы можете рассмотреть возможность превращения вашей функции в процедуру, которая возвращает курсор для запроса выше, или, возможно, иметь функцию, которая возвращает результаты запроса в виде конвейерного набора результатов.

Образец данных:

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

Вы можете попробовать следующий запрос, сумма (комиссия) будет рассчитана только один раз:

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;
Роберт Дургин
источник
Это работает и возвращает правильные данные, но менее эффективно, чем аналитическая функция, которая выполняет одно полное сканирование таблицы, а не два (при условии отсутствия индексов).
Ли Риффель
1
@Leigh ~ Как это можно сделать за один проход, поскольку ручной путь требует двух проходов? Я не вижу, как компьютеры могут сделать% ofTotal волшебной однопроходной операцией ...
jcolebrand
@jcolebrand Данные считываются из блоков базы данных только один раз. Вероятно, он выполняет несколько проходов в памяти, но это обычно быстрее, чем чтение блоков базы данных дважды. Между этими опциями существует компромисс между памятью и процессором, поэтому выбор не всегда может быть четким, но в этом случае я думаю, что это так.
Ли Риффель
1
@Leigh ~~ Да, дальнейшие размышления заставят меня поверить, что это все, что он может делать, только черный ящик с дрожащими оптимизациями. В любом случае, отличное решение в вашем ответе. Спасибо: D
Jcolebrand
0
  select 
  Agent, Commission,
  (
      ROUND(
       (Commission *100) / 
          (
            (SELECT SUM(Commission)
             FROM commissions AS A)
          )
       ) 
  ) AS Porcentaje
  from  
  commissions
JoeDeg
источник