I have a large amount of data. I need to implement a product aggregation on each value. Let me explain with example to make it clear.
This is a sample data-
/*SampleTable*/
|ID|Date |Value |
| 1|201401|25 |
| 1|201402|-30 |
| 1|201403|-15 |
| 1|201404|50 |
| 1|201405|70 |
| 2|201010|1.15 |
| 2|201011|1.79 |
| 2|201012|0.82 |
| 2|201101|1.8 |
| 2|201102|1.67 |
Have to make this table-
/*ResultTable*/
|ID|Date |Aggregated Value |
| 1|201312|100 |
| 1|201401|125 |
| 1|201402|87.5 |
| 1|201403|74.375 |
| 1|201404|111.563 |
| 1|201405|189.657 |
| 2|201009|100 |
| 2|201010|101.15 |
| 2|201011|102.960 |
| 2|201012|103.804 |
| 2|201101|105.673 |
| 2|201102|107.438 |
-- Note: The 100 values are separately inserted for each ID at the month before first date
-- of previous table
Here for each ID, I have a Value (Column 2) given with corresponding Date (YYYYMM format). I have to implement the following formula to calculate the Aggregated Value column Grouped By each ID -
current_Aggregated_Value = previous_aggregated_value * ((current_value/100) + 1))
There was no easy solution for this. I have to take aggregated value of previous row, which is also a generated value by the same query (except 100, it has been manually added), to calculate aggregated value for current row. As it is not possible to take a generated value in runtime for SQL, I had to implement a product aggregate function described here.
so 2nd aggregated_value (125) was derived by (100 * ((25 / 100) + 1)) = 125
3rd aggregated_value (87.5) was derived by (125 * ((-30 / 100) + 1)) = 87.5
But as we cannot take the generated '125' value in runtime, I had to take the product aggregate of the all previous value, 100 * ((25 / 100) + 1) * ((-30 / 100) + 1) = 87.5
similarly 4th value (74.375) comes from, 100 * ((25 / 100) + 1) * ((-30 / 100) + 1) * ((-15 / 100) + 1) = 74.375
Giving a sample query below -
INSERT INTO ResultTable (ID, [Date], [Aggregate Value])
SELECT temps.ID, temps.[Date],
CASE
WHEN temps.min_val = 0 THEN 0
WHEN temps.is_negative % 2 = 1 THEN -1 * EXP(temps.abs_multiplier) * 100
ELSE EXP(temps.abs_multiplier) * 100
END AS value
FROM
(
SELECT st1.ID, st1.[Date],
-- Multiplication by taking all +ve values
SUM(LOG(ABS(NULLIF(((st2.Value / 100) + 1), 0)))) AS abs_multiplier,
-- Count of -ve values, final result is -ve if count is odd
SUM(SIGN(CASE WHEN ((st2.Value / 100) + 1) < 0 THEN 1 ELSE 0 END)) AS is_negative,
-- If any value in the multipliers is 0 the whole multiplication result will be 0
MIN(ABS((st2.Value / 100) + 1)) AS min_val
FROM SampleTable AS st1
INNER JOIN SampleTable AS st2 ON (st2.ID = st1.ID AND st2.[Date] <= st1.[Date])
GROUP BY st1.id, st1.[Date]
) AS temps;
Basically, it is taking the product aggregate for all aggreagted values of previous dates for each value to calculate the desired value. Well, it is as messy as it sounds and looks and "h-word" slow! But I couldn't find any better solution for this kind of problem in SQL Server 2008 R2 (unless u can give me one).
So, I wanna know 2 things-
1. Is it possible to do it without joining the same table like I did there?
2. Is there any better way to do product aggregation on SQL Server 2008 R2? (I know there is one way in Server 2012, but that is not an option for me)
Sorry for the L-O-N-G question! But Thanks in advance!
for 2nd value (125) the calculation was 100*((25/100)+1)
for 3rd value (87.5) the calculation was 125*((-30/100)+1). It is not possible to take 125 in runtime. So it had to be done like 100*((25/100)+1) * ((-30/100)+1)
for 4th value (74.375) it is 100*((25/100)+1) * ((-30/100)+1) * ((-15/100)+1)
and so on... @Jan Zahradník – NBM21 May 25 '15 at 11:56