Агрегатная функция для умножения всех значений - PullRequest
2 голосов
/ 07 июня 2019

Например, у меня есть такие данные

1
2
3
4
5

Как я могу умножить каждую строку? Есть ли функция, аналогичная SUM(), так что ответ будет 120 в этом примере.

Ответы [ 3 ]

3 голосов
/ 07 июня 2019

Да, есть техника, описанная здесь в этом блоге :

Обычно вы берете натуральное логарифм SUM, а затем делаете экспоненциальное (e^x)

SELECT EXP (SUM (LN (col))) as product from t;

Так как результатом этого будет плавающая точка, вы можете сделать FLOOR

FLOOR( EXP (SUM (LN (col))) ) 

DEMO

Примечание : Я только что обнаружил, что это не получится, если в одной из строк будет 0. поэтому вы должны использовать отдельное условие или условие with, что если один из них равен нулю, продукт должен быть нулевым.

2 голосов
/ 07 июня 2019

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

Есть пример в oracle docs , см. Раздел «Пример 11-12 Как создать и использовать пользовательскую статистическую функцию».

Я в основном скопировал пример и изменил несколько строк:

create or replace type AGGR_MUL_TST as object
(
  total number,
  static function ODCIAggregateInitialize(ctx IN OUT AGGR_MUL_TST) 
  return number,
  member function ODCIAggregateIterate(self IN OUT AGGR_MUL_TST, 
                                       value IN number) 
  return number,
  member function ODCIAggregateTerminate(self IN AGGR_MUL_TST, 
                                         returnValue OUT number, flags IN number) 
  return number,
  member function ODCIAggregateMerge(self IN OUT AGGR_MUL_TST, 
                                     ctx2 IN AGGR_MUL_TST) 
  return number
);


create or replace type body AGGR_MUL_TST is
   static function ODCIAGGREGATEINITIALIZE(ctx in out AGGR_MUL_TST)
   return number is
   begin
      ctx := AGGR_MUL_TST(1);
      return ODCIConst.Success;
   end;

   member function ODCIAggregateIterate(self in out AGGR_MUL_TST,
                                        value in number)
   return number is
   begin
      self.total := self.total * value;
      return ODCIConst.Success;
   end;

   member function ODCIAggregateTerminate(self in AGGR_MUL_TST,
                                          returnValue out number,
                                          flags in number
                                         )
   return number is
   begin
      returnValue := self.total;
      return ODCIConst.Success;
   end;

   member function ODCIAggregateMerge(self in out AGGR_MUL_TST,
                                      ctx2 IN AGGR_MUL_TST
                                     )
   return number is
   begin
     self.total := self.total * ctx2.total;
  end;
end;
/
create or replace function AGGR_MUL(input number) return number parallel_enable aggregate using AGGR_MUL_TST;
/

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

with nums as (
   select 1 x, 2 y from dual union all
   select 2 x, 2 y from dual union all
   select 3 x,  2 y from dual union all
   select 4 x,  3 y from dual union all
   select -5 x, 3 y from dual
)
select aggr_mul(x)
  from nums

И даже с группой по:

with nums as (
   select 1 x, 2 y from dual union all
   select 2 x, 2 y from dual union all
   select 3 x,  2 y from dual union all
   select 4 x,  3 y from dual union all
   select -5 x, 3 y from dual
)
select y,
       aggr_mul(x)
  from nums
group by y
1 голос
/ 07 июня 2019

Общее решение немного сложнее - обработка нулевых и отрицательных чисел.

select (exp(sum(ln(abs(nullif(col, 0))))) *
        (1 - 2 * mod(sum(case when col < 0 then 1 when col = 0 then 0.5 else 0 end), 2)
       ) as product 
  • nullif() обрабатывает 0, поэтому ln() не возвращает ошибку.
  • abs() обрабатывает отрицательные числа, поэтому ln() не возвращает ошибку.
  • Выражение case обрабатывает знак результата и также возвращает ноль, если любое значение равно нулю.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...