Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a SQL window function to calculate a percentage of an aggregate

I need to calculate percentages of various dimensions in a table. I'd like to simplify things by using window functions to calculate the denominator, however I am having an issue because the numerator has to be an aggregate as well.

As a simple example, take the following table:

create temp table test (d1 text, d2 text, v numeric);
insert into test values ('a','x',5), ('a','y',5), ('a','y',10), ('b','x',20);

If I just want to calculate the share of each individual row out of d1, then windowing functions work fine:

select d1, d2, v/sum(v) over (partition by d1)
from test;

"b";"x";1.00
"a";"x";0.25
"a";"y";0.25
"a";"y";0.50

However, what I need to do is calculate the overall share for the sum of d2 out of d1. The output I am looking for is this:

"b";"x";1.00
"a";"x";0.25
"a";"y";0.75

So I try this:

select d1, d2, sum(v)/sum(v) over (partition by d1)
from test
group by d1, d2;

However, now I get an error:

ERROR:  column "test.v" must appear in the GROUP BY clause or be used in an aggregate function

I'm assuming this is because it is complaining that the window function is not accounted for in the grouping clause, however windowing functions cannot be put in the grouping clause anyway.

This is using Greenplum 4.1, which is a fork of Postgresql 8.4 and shares the same windowing functions. Note that Greenplum cannot do correlated subqueries.

like image 540
EvilPuppetMaster Avatar asked Dec 15 '11 04:12

EvilPuppetMaster


People also ask

Can we use aggregate function in window function?

Windowing Aggregate Functions. Aggregate functions can be used as window functions; that is, you can use the OVER clause with aggregate functions.

How does window function work in SQL?

Window functions can be simply explained as calculation functions similar to aggregating, but where normal aggregating via the GROUP BY clause combines then hides the individual rows being aggregated, window functions have access to individual rows and can add some of the attributes from those rows into the result set.

Can we use window function in where clause?

You can't use window functions in WHERE , because the logical order of operations in an SQL query is completely different from the SQL syntax. The logical order of operations in SQL is: FROM, JOIN. WHERE.


Video Answer


2 Answers

I think you are looking for this:

SELECT d1, d2, sum(v)/sum(sum(v)) OVER (PARTITION BY d1) AS share
FROM   test
GROUP  BY d1, d2;

Produces the requested result.

Window functions are applied after aggregate functions. The outer sum() in sum(sum(v)) OVER ... is a window function (attached OVER ... clause) while the inner sum() is an aggregate function.

Effectively the same as:

WITH x AS (
   SELECT d1, d2, sum(v) AS sv
   FROM   test
   GROUP  BY d1, d2
   )
SELECT d1, d2, sv/sum(sv) OVER (PARTITION BY d1) AS share
FROM   x;

Or (without CTE):

SELECT d1, d2, sv/sum(sv) OVER (PARTITION BY d1) AS share
FROM  (
   SELECT d1, d2, sum(v) AS sv
   FROM   test
   GROUP  BY d1, d2
   ) x;

Or @Mu's variant.

Aside: Greenplum introduced correlated subqueries with version 4.2. See release notes.

like image 123
Erwin Brandstetter Avatar answered Sep 30 '22 18:09

Erwin Brandstetter


Do you need to do it all with window functions? Sounds like you just need to group the result you have by d1 and d2 and then sum the sums:

select d1, d2, sum(p)
from (
    select d1, d2, v/sum(v) over (partition by d1) as p
    from test
) as dt
group by d1, d2

That gives me this:

 d1 | d2 |          sum           
----+----+------------------------
 a  | x  | 0.25000000000000000000
 a  | y  | 0.75000000000000000000
 b  | x  | 1.00000000000000000000
like image 41
mu is too short Avatar answered Sep 30 '22 19:09

mu is too short