Reputation: 1687
I have the following table in SQL server database.
FundID | FundType | Allocation | Performance |
---|---|---|---|
1 | Credit | 0.10 | 0.15 |
2 | Credit | 0.20 | 0.25 |
3 | Credit | 0.30 | 0.35 |
4 | Credit | 0.40 | 0.45 |
5 | Credit | 0.50 | 0.55 |
6 | Equity | 0.60 | 0.65 |
7 | Equity | 0.70 | 0.75 |
8 | Equity | 0.80 | 0.85 |
9 | Cash | 0.90 | 0.95 |
10 | Cash | 0.99 | 1.55 |
I would like to derive Weighted Performance for each Fund Type based on the following formula:
WeightedPerformance = SUM ( (Allocation/SUM(Allocation Per FundType) * Performance)
Example for Credit:
Sum of Allocation = 1.50 (0.10+0.20+0.30+0.40+0.50)
WeightedPerformance = Sum(((0.1/1.5)*0.15) + ((0.2/1.5)*0.25) + ((0.3/1.5)*0.35) + ((0.4/1.5)*0.45)+ ((0.5/1.5)*0.55) )
WeightedPerformance of Credit = 0.416666666666667
How can I calculate this using T-SQL for each FundTypes?
Query so far:
SELECT
FundType,
Sum((Allocation/Sum(Allocation))*Performance) as WeightedPerformance
FROM
FundAllocation
GROUP BY
FundType
But getting:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
Note: this question is not duplicate of SQL Server "cannot perform an aggregate function on an expression containing an aggregate or a subquery", but Sybase can because the suggested question is using grouped by clause for further aggregation. In this case, it is grouped by FundType.
Upvotes: 1
Views: 80
Reputation: 5916
I presume that
Sum((Allocation/Sum(Allocation))*Performance)
is equal to
Sum(Allocation*Performance)/sum(Allocation)
There you do not use aggregation expression in a aggregation function.
See example
-- simplest solution
SELECT
FundType,
Sum(Allocation*Performance)/sum(Allocation) as WeightedPerformance
,Sum(Allocation) Sum_Allocation
FROM FundAllocation
GROUP BY FundType
FundType | WeightedPerformance | Sum_Allocation |
---|---|---|
Cash | 1.26428571428571 | 1.89 |
Credit | 0.416666666666667 | 1.5 |
Equity | 0.75952380952381 | 2.1 |
Second example - use subquery to calculate Sum(Allocation)over(partition by FundType)
-- with subquery
SELECT
FundType,
Sum((Allocation/Sum_Allocation)*Performance) as WeightedPerformance
,min(Sum_Allocation) Sum_Allocation
FROM(
select *, Sum(Allocation)over(partition by FundType) as Sum_Allocation
from FundAllocation
)a
GROUP BY FundType
FundType | WeightedPerformance | Sum_Allocation |
---|---|---|
Cash | 1.26428571428571 | 1.89 |
Credit | 0.416666666666667 | 1.5 |
Equity | 0.75952380952381 | 2.1 |
Upvotes: 3
Reputation: 37460
You need to use a subquery or Common Table Expression (CTE) to first calculate the sum of allocation per FundType
before performing the weighted calculation. Here’s how you can achieve this:
WITH FundAllocationSums AS (
SELECT
FundType,
SUM(Allocation) AS TotalAllocation
FROM FundAllocation
GROUP BY FundType
)
SELECT
f.FundType,
SUM((f.Allocation / fas.TotalAllocation) * f.Performance) AS WeightedPerformance
FROM FundAllocation f
JOIN FundAllocationSums fas
ON f.FundType = fas.FundType
GROUP BY f.FundType;
Use this to set up the table in SQL Server:
CREATE TABLE FundAllocation (
FundID INT PRIMARY KEY,
FundType VARCHAR(50),
Allocation DECIMAL(10, 5),
Performance DECIMAL(10, 5)
);
INSERT INTO FundAllocation (FundID, FundType, Allocation, Performance) VALUES
(1, 'Credit', 0.10, 0.15),
(2, 'Credit', 0.20, 0.25),
(3, 'Credit', 0.30, 0.35),
(4, 'Credit', 0.40, 0.45),
(5, 'Credit', 0.50, 0.55),
(6, 'Equity', 0.60, 0.65),
(7, 'Equity', 0.70, 0.75),
(8, 'Equity', 0.80, 0.85),
(9, 'Cash', 0.90, 0.95),
(10, 'Cash', 0.99, 1.55);
Upvotes: 1
Reputation: 72197
You can use a window function for this
WITH summed AS (
SELECT
fa.*,
SUM(fa.Allocation) OVER (PARTITION BY fa.FundType) AS AllocationPerFundType
FROM FundAllocation fa
)
SELECT
fa.FundType,
SUM(fa.Allocation / fa.AllocationPerFundType * fa.Performance) as WeightedPerformance,
SUM(Allocation) AS SumAllocation
FROM summed fa
GROUP BY
fa.FundType;
Upvotes: 1