Multiplication aggregate operator in SQL
By MUL do you mean progressive multiplication of values?
Even with 100 rows of some small size (say 10s), your MUL(column) is going to overflow any data type! With such a high probability of mis/ab-use, and very limited scope for use, it does not need to be a SQL Standard. As others have shown there are mathematical ways of working it out, just as there are many many ways to do tricky calculations in SQL just using standard (and common-use) methods.
Sample data:
Column
1
2
4
8
COUNT : 4 items (1 for each non-null)
SUM : 1 + 2 + 4 + 8 = 15
AVG : 3.75 (SUM/COUNT)
MUL : 1 x 2 x 4 x 8 ? ( =64 )
For completeness, the Oracle, MSSQL, MySQL core implementations *
Oracle : EXP(SUM(LN(column))) or POWER(N,SUM(LOG(column, N)))
MSSQL : EXP(SUM(LOG(column))) or POWER(N,SUM(LOG(column)/LOG(N)))
MySQL : EXP(SUM(LOG(column))) or POW(N,SUM(LOG(N,column)))
- Care when using EXP/LOG in SQL Server, watch the return type http://msdn.microsoft.com/en-us/library/ms187592.aspx
- The POWER form allows for larger numbers (using bases larger than Euler's number), and in cases where the result grows too large to turn it back using POWER, you can return just the logarithmic value and calculate the actual number outside of the SQL query
* LOG(0) and LOG(-ve) are undefined. The below shows only how to handle this in SQL Server. Equivalents can be found for the other SQL flavours, using the same concept
create table MUL(data int)
insert MUL select 1 yourColumn union all
select 2 union all
select 4 union all
select 8 union all
select -2 union all
select 0
select CASE WHEN MIN(abs(data)) = 0 then 0 ELSE
EXP(SUM(Log(abs(nullif(data,0))))) -- the base mathematics
* round(0.5-count(nullif(sign(sign(data)+0.5),1))%2,0) -- pairs up negatives
END
from MUL
Ingredients:
- taking the abs() of data, if the min is 0, multiplying by whatever else is futile, the result is 0
- When data is 0, NULLIF converts it to null. The abs(), log() both return null, causing it to be precluded from sum()
- If data is not 0, abs allows us to multiple a negative number using the LOG method - we will keep track of the negativity elsewhere
- Working out the final sign
- sign(data) returns
1 for >0
,0 for 0
and-1 for <0
. - We add another 0.5 and take the sign() again, so we have now classified 0 and 1 both as 1, and only -1 as -1.
- again use NULLIF to remove from COUNT() the 1's, since we only need to count up the negatives.
% 2
against the count() of negative numbers returns either- --> 1 if there is an odd number of negative numbers
- --> 0 if there is an even number of negative numbers
- more mathematical tricks: we take 1 or 0 off 0.5, so that the above becomes
- --> (
0.5-1=-0.5
=>round to -1) if there is an odd number of negative numbers - --> (
0.5-0= 0.5
=>round to 1) if there is an even number of negative numbers - we multiple this final 1/-1 against the SUM-PRODUCT value for the real result
- sign(data) returns
Aggregate function to multiply all values
Yes, there's a technique described here in this blog:
Basically you take the natural log of SUM
and then do an exponential (e^x
)
SELECT EXP (SUM (LN (col))) as product from t;
Since the output of this would be a floating point, you may do a FLOOR
FLOOR( EXP (SUM (LN (col))) )
DEMO
Note: I've just found that this would fail if one of the rows has 0
. so you should use a separate condition or a with
clause that if one of them is zero the product should be zero.
Is there an aggregate function to multiply column values in mysql
If all your values are integers greater than zero, you can do:
round(exp(sum(log(column1))))
MySQL - Use GROUP BY and SUM to multiply two columns
As noted by patrick3853 and Olivier Depriester in the comments, the line SUM(jo.quantity * jo.price) should instead be SUM(jo.quantity) * SUM(jo.price).
The correct query is as follows:
SELECT p.name AS 'Project Name',
SUM(jo.quantity) AS 'Job Order Quantity',
SUM(jo.price) AS 'Job Order Price',
SUM(jo.quantity) * SUM(jo.price) AS 'Cost'
FROM projects p
JOIN job_orders jo ON p.id = jo.project_id
GROUP BY name;
sql server - multiply values by themselves
select *
into #a --*?
FROM ( SELECT 4 V
UNION ALL
SELECT 5
UNION ALL
SELECT 7
) Q
select EXP(SUM(LOG(v))) As value from #a
or
SELECT EXP(SUM(LOG(v)))
FROM ( SELECT 4 V
UNION ALL
SELECT 5
UNION ALL
SELECT 7
) Q
Multiplication Aggregate Operator in Presto and AWS Athena
Turns out there's a super quick and easy hack! Harkening back to high school math class, given some {a0, a1, a2, ..., an} where all of the ai > 0 (THIS IS A BIG DEAL! We need all of the inputs to be positive, otherwise the natural logarithm is undefined!), we know that
exp(ln(a0) + ln(a1) + ... + ln(an)) = exp(ln(a0))exp(ln(a1))...exp(ln(an)) = a0a1a2...an
where ln(x) is the natural logarithm of x and exp(x) is euler's number raised to the power x. But this is precisely what we want! Even better, presto has the functions exp(), sum(), and ln() built in, so all we have to do to is write
SELECT exp(sum(ln(column1))) AS product FROM yourtable;
and voila!
SQL Server Query - groupwise multiplication
Multiplying row values is the same as adding logarithms of row values
The trick is dealing with zeros and nulls.
Ok, checked now
DECLARE @foo TABLE (GrpID varchar(10), Value float)
INSERt @foo (GrpID, Value)
SELECT 'Grp1', 2
UNION ALL SELECT 'Grp1', 5
UNION ALL SELECT 'Grp1', 3
UNION ALL SELECT 'Grp2', 3
UNION ALL SELECT 'Grp2', -5
UNION ALL SELECT 'Grp2', -2
UNION ALL SELECT 'Grp3', 4
UNION ALL SELECT 'Grp3', 0
UNION ALL SELECT 'Grp3', 1
UNION ALL SELECT 'Grp4', -2
UNION ALL SELECT 'Grp4', -4
UNION ALL SELECT 'Grp5', 7
UNION ALL SELECT 'Grp5', NULL
UNION ALL SELECT 'Grp6', NULL
UNION ALL SELECT 'Grp6', NULL
UNION ALL SELECT 'Grp7', -1
UNION ALL SELECT 'Grp7', 10
SELECT
GrpID,
CASE
WHEN MinVal = 0 THEN 0
WHEN Neg % 2 = 1 THEN -1 * EXP(ABSMult)
ELSE EXP(ABSMult)
END
FROM
(
SELECT
GrpID,
--log of +ve row values
SUM(LOG(ABS(NULLIF(Value, 0)))) AS ABSMult,
--count of -ve values. Even = +ve result.
SUM(SIGN(CASE WHEN Value < 0 THEN 1 ELSE 0 END)) AS Neg,
--anything * zero = zero
MIN(ABS(Value)) AS MinVal
FROM
@foo
GROUP BY
GrpID
) foo
Aggregate multiplicate function
Ideally, Access SQL would have a PRODUCT
aggregate function available, but it doesn't. We can however simulate it by remembering what we learned about logarithms at school (or not...), and remembering that the anti-log of the sum of logs is equal to the product:
SELECT field2, EXP(Sum(LOG(Field3))) AS ProductOfField3
FROM t
GROUP BY Field2
Note that whereas a true PRODUCT
function would simply return 0
for a group if there are any zero values, this solution will fail if there are any zero values, so watch out for that. Also, this approach won't work if there are any negative values.
To deal with zeroes we could do this:
SELECT
field2,
EXP(Sum(LOG(IIf(Field3 = 0, 1, Field3)))) AS ProductOfField3,
MIN(ABS(Field3)) AS MinOfAbsField3
FROM t
GROUP BY Field2
and then disregard the ProductOfField3
value for any row where MinOfAbsField3
is zero (as this indicates a group containing a zero, thus the 'true' product should be 0
)
To deal with negative values we could further do this:
SELECT
field2,
EXP(Sum(LOG(IIf(Field3 = 0, 1, ABS(Field3))))) AS ProductOfField3,
MIN(ABS(Field3)) AS MinOfAbsField3,
SUM(IIf(Field3 < 0, 1, 0)) AS SumOfNegativeIndicator
FROM t
GROUP BY Field2
and interpret the results with these rules:
- If
MinOfAbsField3
is zero, disregardProductOfField3
for that row - the product is zero - Otherwise, the required answer for a given row is
ProductOfField3
, negated ifSumOfNegativeIndicator
is odd in that row
How to Multiply all values within a column with SQL like SUM()
Using a combination of ROUND
, EXP
, SUM
and LOG
SELECT ROUND(EXP(SUM(LOG([Col A]))),1)
FROM yourtable
SQL Fiddle: http://sqlfiddle.com/#!3/d43c8/2/0
Explanation
LOG
returns the logarithm of col a ex. LOG([Col A])
which returns
0
0.6931471805599453
1.0986122886681098
1.3862943611198906
Then you use SUM
to Add them all together SUM(LOG([Col A]))
which returns
3.1780538303479453
Then the exponential of that result is calculated using EXP(SUM(LOG(['3.1780538303479453'])))
which returns
23.999999999999993
Then this is finally rounded using ROUND
ROUND(EXP(SUM(LOG('23.999999999999993'))),1)
to get 24
Extra Answers
Simple resolution to:
An invalid floating point operation occurred.
When you have a 0
in your data
SELECT ROUND(EXP(SUM(LOG([Col A]))),1)
FROM yourtable
WHERE [Col A] != 0
If you only have 0
Then the above would give a result of NULL
.
When you have negative numbers in your data set.
SELECT (ROUND(exp(SUM(log(CASE WHEN[Col A]<0 THEN [Col A]*-1 ELSE [Col A] END))),1)) *
(CASE (SUM(CASE WHEN [Col A] < 0 THEN 1 ELSE 0 END) %2) WHEN 1 THEN -1 WHEN 0 THEN 1 END) AS [Col A Multi]
FROM yourtable
Example Input:
1
2
3
-4
Output:
Col A Multi
-24
SQL Fiddle: http://sqlfiddle.com/#!3/01ddc/3/0
Related Topics
Unknown Column in 'Field List' Error on MySQL Update Query
Most Efficient Way in SQL Server to Get Date from Date+Time
How to List the Primary Key of a SQL Server Table
MySQL Number of Items Within "In Clause"
How Many Rows in a Database Are Too Many
Fastest Way to Update 120 Million Records
Different Current_Timestamp and Sysdate in Oracle
SQL Server: Howto Get Foreign Key Reference from Information_Schema
Accessing SQL Database in Excel-Vba
Difference Between Top and Limit Keyword in SQL
How to Use a Calculated Column to Calculate Another Column in the Same View
Oracle: What Does '(+)' Do in a Where Clause
What Does Include() Do in Linq
MySQL "Between" Clause Not Inclusive
How to Capitalize the First Letter of Each Word in a String in SQL Server