HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Product of n Rows

Submitted by: @import:stackexchange-dba··
0
Viewed 0 times
rowsproductstackoverflow

Problem

From this data (assuming the number of rows is not known in advance):

with q1 as (select mod(ora_hash(level),5) c1 from dual connect by level <=4)
select * from q1;
/*
C1
--
 2 
 1 
 4 
 1
*/


I want the product of the c1 column from all the rows. Something like the results of SUM(c1) only I want each value multiplied by they others rather than added. In this case that would be 2 1 4 * 1 = 8.

/*
X1
--
 8
*/


Data could contain negative numbers and zero, which can be simulated using:

with q1 as (select mod(ora_hash(level),5)-1 c1 from dual connect by level <=4)
select * from q1;


or

with q1 as (select mod(ora_hash(level),5)-3 c1 from dual connect by level <=4)
select * from q1;


I know this could be done with custom aggregate function, but am interested in native approaches.

Solution

For sufficiently small aggregate products, you can use the old trick of summing the logarithms and them exponentiating the result

SQL> ed
Wrote file afiedt.buf

  1  with q1
  2    as (select mod(ora_hash(level),5) c1
  3          from dual
  4       connect by level  /

EXP(SUM(LN(C1)))
----------------
               8


Since you're using 11.2, it's a bit more verbose (though someone may be able to figure out a simpler version) but you can also use recursive common table expressions

SQL> ed
Wrote file afiedt.buf

  1  with
  2  q1 as (select level l, mod(ora_hash(level),5) c1
  3           from dual
  4        connect by level  /

RUNNING_PRODUCT
---------------
              8

Code Snippets

SQL> ed
Wrote file afiedt.buf

  1  with q1
  2    as (select mod(ora_hash(level),5) c1
  3          from dual
  4       connect by level <=4)
  5  select exp(sum(ln(c1)))
  6*   from q1
SQL> /

EXP(SUM(LN(C1)))
----------------
               8
SQL> ed
Wrote file afiedt.buf

  1  with
  2  q1 as (select level l, mod(ora_hash(level),5) c1
  3           from dual
  4        connect by level <= 4),
  5  num(n, c1, running_product)
  6  as
  7  (
  8    select 1 as N,
  9           null as c1,
 10           1 as running_product
 11      from dual
 12    union all
 13    select N+1,
 14           q1.c1,
 15           (q1.c1)*running_product
 16      from num
 17           join q1 on (num.N = q1.l)
 18  )
 19  select running_product
 20    from (select num.*,
 21                 rank() over (order by N desc) rnk
 22            from num)
 23*  where rnk = 1
SQL> /

RUNNING_PRODUCT
---------------
              8

Context

StackExchange Database Administrators Q#15019, answer score: 5

Revisions (0)

No revisions yet.