Calculate Annual Escalations in SQL - sql

I have a dataset (in SQL) where I need to calculate the market value over the entire period. I want to automate this calculation in SQL. The Initial value is 2805.00 per month payable for 36 months. But the value is escalated at 5.5% after each block of twelve months. I have included a picture below to show the values and how they escalate. In terms of what fields I have in SQL, I have the length of the term (36 months [months]). I also have an escalation percentage (5.5% in this case [percentage]) and the starting value [starting_value], 2805.00 in this case. The total value (Expected Result [result]) is 106 635.72. I am after what the calculation should look like in SQL so that I can apply it across all market points.

Here is a fast performance formula to calculate the expected result [result] if you need to calculate every, annual salary with respect to the first one:
[result] = (N * (N + 1) / 2) * [increment] * 12 + M * (N + 1) * [increment] + [starting_value] * [months]
Where:
N = TRUNC([months] / 12) - 1// TRUNC --> Integer value of dividing [months] / 12
M = [months] MOD 12 // Rest of integer division of [months] / 12
[increment] = [percentage] * [starting_value] / 100
On the other hand, if you need to calculate each annual salary with respect to its predecessor you will need the formula below:
∑y=0TRUNC([months]/12)−1{([percentage]/100 + 1)y * 12 * [starting_value]} + ([percentage]/100 + 1)TRUNC([months]/12) + 1 * ([months] MOD 12) * [starting_value]
This is a bit confusing but there is no way to place formulas in Stack overflow. Moreover, if you need to run this in some DBMS you should assure someone that allows you to make loops, as the sum will need it.
You would need to adapt both formulas to the DBMS, taking into account the comments I placed before. Hope this to be helpful.

Related

How can I take different percentages of the value in a column and add them up to assign in a new SQL column?

I have a column in my table named as "FundedAmount" and I want to create a new column while running my query on SQL named as "Commissions" and this newly created column needs to have the tier wise percentages summed up according to the equation as follows:
5%(0$ - $5000) + 1%($5001 - $10,000) + 0.5%($10,001 - $20,000) + 0.25%(Remaining Amount)
So, For Instance in the case of the example below the "Commissions" column would have value:
5%(0$ - $5000) + 1%($5001 - $10,000) + 0.5%($10,001 - $20,000) + 0.25%(Remaining Amount)
250 + 50 + 5.515 = 305.515
Fix your tiering table so it looks like:
low high percent
0 5000 0.05
5000 10000 0.01
. . .
Note that I changed the bounds so all amount are included, including fractional amounts.
Then you can calculate this using a subquery like this:
select t.*,
(select sum( (least(tt.high, t.fundedamount) - tt.low) * tt.percent )
from tiers tt
where t.fundedamount > tt.low
) as commission
from t;

How to show number of users by dynamic age groups

I have a table which store DoB of users. Now I need a report(crystal report) that shows how many users are there in ranges of age.
For example, the report need to shows:
Age: 1-10: 50 users
Age: 11-20: 30 users
Age: 21-30: 60 users
And so on...
The report should follow rules below :
The range of age can be changed easily (the range in example above is 10)
The last range is calculated automatically based on the oldest user
Actually I don't have any idea How to do it for now. I really appreciate any help.
Thanks!
You can define the ranges using arithmetic. The exact syntax might vary by database, but it is basically:
select (floor( (a - 1) / 10 ) * 10 + 1) || '-' || (floor( (a - 1) / 10 ) * 10 + 10) as age_range,
count(*)
from t
group by (floor( (a - 1) / 10 ) * 10 + 1) || '-' || (floor( (a - 1) / 10 ) * 10 + 10)
order by min(age);
The expression floor( (a - 1) / 10 ) * 10 + 1 is just a mathematical expression that gets the first year of the range -- so 1-10 are in one group, 11-20, and so on.

SQL storing a value and using it another in the same query

I have a table Patients which looks like this:
PatientName DateOftest Eye L1 L2 L3 L4 L5
----------------------------------------------------------------
Mike 17-02-2009 L 23 25 40 32 30
Mike 17-02-2009 R 25 30 34 35 24
Bill 08-03-2006 L 20 24 30 24 25
Bill 08-03-2006 R 18 25 27 30 24
Now my query below finds mean
SELECT
PatientName, DateOfTest,
(MAX(L1) + MAX(L2) + MAX(L3) + MAX(L4) + MAX(L5))/4 as Mean,
SQRT(POW(L1 - Mean, 2) + POW(L2 - Mean, 2) + POW(L3 - Mean, 2) + POW(L4 - Mean, 2) + POW(L5 - Mean, 2)) AS Standard Deviation,
'Binocular' Eye
FROM
Patients
GROUP BY
PatientName, DateOfTest;
The above query is wrong because I have not stored mean.. is there any way to store mean to find out standard deviation in my code.. I'm asking because I have very lengthy query and more records..
To store the mean and reuse it in your query, one option would be to use a Common Table Expression. You can join the CTE to the table to use the calculated mean multiple times.
I'll admit that didn't understand the following line...
SQRT(POW(L1-Mean,2)+POW(L2-Mean,2)+POW(L3-Mean,2)+POW(L4-Mean,2)+POW(L5-Mean,2))
as Standard Deviation, 'Binocular' Eye
...but the query below shows how you would integrate the calculated mean into that line, which I think might need some additional work as well.
--This is the CTE to calculate the mean
WITH Mean_CTE AS
(
SELECT PatientName, DateOfTest,
(MAX(L1) + MAX(L2) + MAX(L3) + MAX(L4) + MAX(L5))/4 AS [Mean]
FROM Patients
GROUP BY PatientName, DateOfTest
)
--This is the original query
SELECT Patients.PatientName, Patients.DateOfTest, Mean_CTE.Mean AS Mean,
SQRT(POW(L1-Mean_CTE.Mean,2)+POW(L2-Mean_CTE.Mean,2)+POW(L3-Mean_CTE.Mean,2)
+POW(L4-Mean_CTE.Mean,2)+POW(L5-Mean_CTE.Mean,2)) as Standard Deviation,
'Binocular' Eye
FROM Patients
INNER JOIN Mean_CTE --This is where you join the two
ON Patients.PatientName = Mean_CTE.PatientName
AND Patients.DateOfTest = Mean_CTE.DateOfTest
GROUP BY Patients.PatientName, Patients.DateOfTest, Mean_CTE.Mean;
What about the possibility of adding a CALCULATED column to the table that stores the result of the formula??
This is a rather simple concept, and will sotre the value for the formula.
http://msdn.microsoft.com/en-us/library/ms191250(v=sql.105).aspx

Why is this SQL SUM statement correct?

In my schema, I have a table Projects, and a table Tasks. Each project is comprised of tasks. Each task has Hours and PercentComplete.
Example table:
ProjectID TaskID Hours PercentComplete
1 1 100 50
1 2 120 80
I am trying to get the weighted percentage complete for the project. I am doing this using the following SQL statement:
SELECT P.ProjectID, P.ProjectName, SUM(T.Hours) AS Hours,
SUM(T.PercentComplete * T.Hours) / 100 AS CompleteHours,
SUM(T.PercentComplete * T.Hours) / SUM(T.Hours) AS PercentComplete
FROM Projects AS P INNER JOIN
Tasks AS T ON T.ProjectID = P.ProjectID
WHERE (P.ProjectID = 1)
My question is about this part of that statement:
SUM(T.PercentComplete * T.Hours) / SUM(T.Hours) AS PercentComplete
This gives me the correct weighted percentage for this project (in the case of the sample data above, 66%). But I cannot seem to wrap my head around why it does this.
Why does this query work?
SUM(T.PercentComplete * T.Hours) / 100 is the number of complete hours.
SUM(T.Hours) is the total number of hours.
The ratio of these two amounts, i.e.:
(SUM(T.PercentComplete * T.Hours) / 100) / SUM(T.Hours)
is the proportion of hours complete (it should be between 0 and 1).
Multiplying this by 100 gives the percentage.
I prefer to keep percentages like this out of the database and move them to the presentation layer. It would be much easier if the database stored "hours completed" and "hours total" and did not store the percentages at all. The extra factors of 100 in the calculations confuse the issue.
Basically you are finding the number of hours completed over the number of hours total.
SUM(T.PercentComplete * T.Hours) computes the total number of hours that you have completed. (100 * 50) = 50 * 100 + (120 * 80) = 146 * 100 is the numerator. 146 hours have been completed on this job, and we keep a 100 multiplier for the percent (because it is [0-100] instead of [0-1])
Then we find the total number of hours worked, SUM(T.Hours), which is 100 + 120 = 220.
Then dividing, we find the weighted average. (146 * 100) / 220 = 0.663636364 * 100 = 66.4%
Is this what you were wondering about?
It calculates the two sums individually by adding up the value for each row then divides them at the end
SUM(T.PercentComplete * T.Hours)
50* 100 +
80 * 120
-------
14,600
SUM(T.Hours)
100 +
120
---
220
Then the division at the end
14,600 / 220
------------
66.3636
Edit As per HLGEM's comment it will actually return 66 due to integer division.
Aggregate functions, such as SUM(), work against the set of data defined by the GROUP BY clause. So if you group by ProjectID, ProjectName, the functions will break things down by that.
The SUM peratiorn first multiply the columns than add
( 100* 50+ 120* 80) / (100+ 120)

Interpreting coded field in SQL

Having this table, I would like to find the rows with Val fitting my Indata.
Tol field is a tolerance (varchar), that can be either an integer/float or a percentage value.
Row Val Tol Outdata
1 24 0 A
2 24 5 B
3 24 10 C
4 32 %10 D
5 32 1 E
Indata 30 for example should match rows 3 (24+10=34) and 4 (32-10%=28.8).
Can this be done in mySQL? CREATE FUNCTION?
This is going to be rather difficult to do in MySQL with that table and column design. How do you plan to differentiate what sort of comparison should be done? By doing a string comparison to see if your varchar field contains a percentage sign?
I would suggest breaking your tolerance field into (at least) two int/float columns, say tol and tol_pct. For flexibility, I would represent tol_pct as a decimal (10% => .10). Then, you can do a query that looks like:
select *
from table
where
(Indata between Val - tol and Val + tol)
or (Indata between Val * (1 + tol_pct) and Val * (1 - tol_pct))
I don't have a MySQL install to test it on, but this example is converted from Oracle sql syntax. You have to use string functions to determine if the tol is a percent and act accordingly to calculate the min and max range for that field. Then you can use a between clause.
select *
from (select t.*,
case when substr(tol, 1, 1) = '%' then
t.val * (1 + convert('.' + substr(tol, 2), number))
else
t.val + convert(tol, number)
end maxval,
case when substr(tol, 1, 1) = '%' then
t.val * (1 - convert('.' + substr(tol, 2), number))
else convert(t.val - tol, number)
end minval
from mytable
) t
where 30 between minval and maxval
;
Sure it can be done. But let me tell you that you better review your database design as it conflicts with normalization quite a bit. See http://en.wikipedia.org/wiki/Database_normalization for a quick overview.