I have a database named Cash_Flow_Statements and I need to create a saved query that calculates a Trailing Twelve Month (TTM) Free Cash Flow.
Here is an example of my database:
And here is the code for my saved query. I have used this code for other calculations so I know it works. I just don't know how to do a Trailing Twelve Month in SQL.
CREATE PROC " & Calculation & " AS _
SELECT Income_Statements.Ticker, Income_Statements.[Year], Income_Statements.Period, _
" & Formula & " AS TTM _
FROM (Income_Statements AS Income_Statements INNER JOIN Balance_Sheets AS Balance_Sheets ON (Income_Statements.Ticker = Balance_Sheets.Ticker) AND (Income_Statements.[Year] = Balance_Sheets.[Year]) AND (Income_Statements.Period = Balance_Sheets.Period)) _
INNER JOIN Cash_Flow_Statements AS Cash_Flow_Statements ON (Balance_Sheets.Ticker = Cash_Flow_Statements.Ticker) AND (Balance_Sheets.[Year] = Cash_Flow_Statements.[Year]) AND (Balance_Sheets.Period = Cash_Flow_Statements.Period)
I need the variable Formula to contain the equation that will calculate the Trailing Twelve Month for Free Cash Flow. Here is what the math looks like:
Free Cash Flow TTM = Sum(Operating Cash Flow MRQ1 + Operating Cash Flow MRQ2 + Operating Cash Flow MRQ3 + Operating Cash Flow MRQ4) - Sum(Capital Expenditures MRQ1 + Capital Expenditures MRQ2 + Capital Expenditures MRQ3 + Capital Expenditures MRQ4)
MRQ = Most Recent Quarter
I need this formula to be in SQL. How do I do this?
UPDATE
Per #Gord Thompson's answer below I have come up with this code:
SELECT Ticker, [Year], Period, (SELECT (SUM(su.Net_Cash_Flow_Operating) - SUM(su.Capital_Expenditures)) _
FROM (SELECT Ticker, [Year], Period, Net_Cash_Flow_Operating, Capital_Expenditures _
FROM Cash_Flow_Statements _
UNION ALL _
SELECT Ticker, [Year] + 1, Period - 4, Net_Cash_Flow_Operating, Capital_Expenditures _
FROM Cash_Flow_Statements) su _
WHERE su.Ticker = s.Ticker AND su.[Year] = s.[Year] AND (su.Period Between s.Period - 3 And s.Period)) _
AS SalesLast12Months _
FROM Cash_Flow_Statements AS s
When this is run by itself it returns exactly what I need. But as I mentioned in my original post I need to be able to set the variable Formula to contain the SQL statement and then insert it into the existing SQL statement.
So I've tried to adapt #Gord Thompson answer and I've come up this this.
Formula:
Formula = “(SELECT (SUM(su.Net_Cash_Flow_Operating) - SUM(su.Capital_Expenditures)) _
FROM (SELECT Ticker, [Year], Period, Net_Cash_Flow_Operating, Capital_Expenditures _
FROM Cash_Flow_Statements UNION ALL SELECT Ticker, [Year] + 1, Period - 4, Net_Cash_Flow_Operating, Capital_Expenditures _
FROM Cash_Flow_Statements) su _
WHERE su.Ticker = Cash_Flow_Statements.Ticker AND su.[Year] = Cash_Flow_Statements.[Year] AND (su.Period Between Cash_Flow_Statements.Period - 3 And Cash_Flow_Statements.Period))”
SQL statement:
"CREATE PROC " & Calculation & " AS SELECT Income_Statements.Ticker, Income_Statements.[Year], Income_Statements.Period, " & Formula & " AS TTM _
FROM (Income_Statements AS Income_Statements _
INNER JOIN Balance_Sheets AS Balance_Sheets ON (Income_Statements.Ticker = Balance_Sheets.Ticker) AND (Income_Statements.[Year] = Balance_Sheets.[Year]) AND (Income_Statements.Period = Balance_Sheets.Period)) _
INNER JOIN Cash_Flow_Statements AS Cash_Flow_Statements ON (Balance_Sheets.Ticker = Cash_Flow_Statements.Ticker) AND (Balance_Sheets.[Year] = Cash_Flow_Statements.[Year]) AND (Balance_Sheets.Period = Cash_Flow_Statements.Period)
This is the result:
Again this is very close but not quite what I need. I believe that I know what causes the code to not work when I adapt it but I don't know how to fix it yet. In #Gord Thompson code the last line is FROM Cash_Flow_Statements AS s. When I insert the variable formula into my existing SQL statement from my original post, the code is FROM (Income_Statements AS Income_Statements _
INNER JOIN Balance_Sheets AS Balance_Sheets ON (Income_Statements.Ticker = Balance_Sheets.Ticker) AND (Income_Statements.[Year] = Balance_Sheets.[Year]) AND (Income_Statements.Period = Balance_Sheets.Period)) _
INNER JOIN Cash_Flow_Statements AS Cash_Flow_Statements ON (Balance_Sheets.Ticker = Cash_Flow_Statements.Ticker) AND (Balance_Sheets.[Year] = Cash_Flow_Statements.[Year]) AND (Balance_Sheets.Period = Cash_Flow_Statements.Period). I'm almost positive that this is where the problems lies but do not know how to correct it.
Let's not get too hung up on the columns, let's concentrate on selecting the appropriate rows.
Say we had a table named [SalesSummary] containing
Item FiscalYear Quarter TotalSales
-------- ---------- ------- ----------
bicycles 2011 1 100
bicycles 2011 2 200
bicycles 2011 3 300
bicycles 2011 4 400
bicycles 2012 1 500
bicycles 2012 2 600
bicycles 2012 3 700
bicycles 2012 4 800
ham 2011 1 10
ham 2011 2 20
ham 2011 3 30
ham 2011 4 40
ham 2012 1 50
ham 2012 2 60
ham 2012 3 70
ham 2012 4 80
If we wanted to create a query that showed the quarterly figures along with the total sales for the past 12 months (current quarter plus the previous 3 quarters) one might think that we would have to start messing around with IIf() to handle the "wrap around" from one FiscalYear to the next. Fortunately, we don't, because we can just replicate the same data with "fake" quarters for the next FiscalYear: 2011_Q(4) becomes 2012_Q(0), 2011_Q(3) becomes 2012_Q(-1), and so on.
SELECT
Item,
FiscalYear,
Quarter,
TotalSales
FROM SalesSummary
UNION ALL
SELECT
Item,
FiscalYear + 1,
Quarter - 4,
TotalSales
FROM SalesSummary
returns
Item FiscalYear Quarter TotalSales
-------- ---------- ------- ----------
bicycles 2011 1 100
bicycles 2011 2 200
bicycles 2011 3 300
bicycles 2011 4 400
bicycles 2012 1 500
bicycles 2012 2 600
bicycles 2012 3 700
bicycles 2012 4 800
ham 2011 1 10
ham 2011 2 20
ham 2011 3 30
ham 2011 4 40
ham 2012 1 50
ham 2012 2 60
ham 2012 3 70
ham 2012 4 80
bicycles 2012 -3 100
bicycles 2012 -2 200
bicycles 2012 -1 300
bicycles 2012 0 400
bicycles 2013 -3 500
bicycles 2013 -2 600
bicycles 2013 -1 700
bicycles 2013 0 800
ham 2012 -3 10
ham 2012 -2 20
ham 2012 -1 30
ham 2012 0 40
ham 2013 -3 50
ham 2013 -2 60
ham 2013 -1 70
ham 2013 0 80
If we save that query as [SalesUnion] then we can use it to produce our totals like so
SELECT
Item,
FiscalYear,
Quarter,
TotalSales,
(
SELECT
SUM(su.TotalSales)
FROM SalesUnion su
WHERE su.Item = s.Item
AND su.FiscalYear = s.FiscalYear
AND (su.Quarter Between s.Quarter - 3 And s.Quarter)
) AS SalesLast12Months
FROM SalesSummary s
Or, if you don't want to rely on a saved query you can do it all in one go:
SELECT
Item,
FiscalYear,
Quarter,
TotalSales,
(
SELECT
SUM(su.TotalSales)
FROM
(
SELECT
Item,
FiscalYear,
Quarter,
TotalSales
FROM SalesSummary
UNION ALL
SELECT
Item,
FiscalYear + 1,
Quarter - 4,
TotalSales
FROM SalesSummary
) su
WHERE su.Item = s.Item
AND su.FiscalYear = s.FiscalYear
AND (su.Quarter Between s.Quarter - 3 And s.Quarter)
) AS SalesLast12Months
FROM SalesSummary s
Either way, the results are
Item FiscalYear Quarter TotalSales SalesLast12Months
-------- ---------- ------- ---------- -----------------
bicycles 2011 1 100 100
bicycles 2011 2 200 300
bicycles 2011 3 300 600
bicycles 2011 4 400 1000
bicycles 2012 1 500 1400
bicycles 2012 2 600 1800
bicycles 2012 3 700 2200
bicycles 2012 4 800 2600
ham 2011 1 10 10
ham 2011 2 20 30
ham 2011 3 30 60
ham 2011 4 40 100
ham 2012 1 50 140
ham 2012 2 60 180
ham 2012 3 70 220
ham 2012 4 80 260
The variable Formula should contain this:
SELECT (SUM(su.Net_Cash_Flow_Operating) - SUM(su.Capital_Expenditures)) FROM (SELECT Ticker, [Year], Period, Net_Cash_Flow_Operating, Capital_Expenditures FROM Cash_Flow_Statements UNION ALL SELECT Ticker, [Year] + 1, Period - 4, Net_Cash_Flow_Operating, Capital_Expenditures FROM Cash_Flow_Statements) su WHERE su.Ticker = c.Ticker AND su.[Year] = c.[Year] AND (su.Period Between c.Period - 3 And c.Period)
Related
Please consider this table:
Year Month Value YearMonth
2011 1 70 201101
2011 1 100 201101
2011 2 200 201102
2011 2 50 201102
2011 3 80 201103
2011 3 250 201103
2012 1 100 201201
2012 2 200 201202
2012 3 250 201203
I want to get a cumulative sum based on each year. For the above table I want to get this result:
Year Month Sum
-----------------------
2011 1 170
2011 2 420 <--- 250 + 170
2011 3 750 <--- 330 + 250 + 170
2012 1 100
2012 2 300 <--- 200 + 100
2012 3 550 <--- 250 + 200 + 100
I wrote this code:
Select c1.YearMonth, Sum(c2.Value) CumulativeSumValue
From #Tbl c1, #Tbl c2
Where c1.YearMonth >= c2.YearMonth
Group By c1.YearMonth
Order By c1.YearMonth Asc
But its CumulativeSumValue is calculated twice for each YearMonth:
YearMonth CumulativeSumValue
201101 340 <--- 170 * 2
201102 840 <--- 420 * 2
201103 1500
201201 850
201202 1050
201203 1300
How can I achieve my desired result?
I wrote this query:
select year, (Sum (aa.[Value]) Over (partition by aa.Year Order By aa.Month)) as 'Cumulative Sum'
from #Tbl aa
But it returned multiple records for 2011:
Year Cumulative Sum
2011 170
2011 170
2011 420
2011 420
2011 750
2011 750
2012 100
2012 300
2012 550
You are creating a cartesian product here. In your ANSI-89 implicit JOIN (you really need to stop using those and switch to ANSI-92 syntax) you are joining on c1.YearMonth >= c2.YearMonth.
For your first month you have two rows with the same value of the year and month, so each of those 2 rows joins to the other 2; this results in 4 rows:
Year
Month
Value1
Value2
2011
1
70
70
2011
1
70
100
2011
1
100
70
2011
1
100
100
When you SUM this value you get 340, not 170, as you have 70+70+100+100.
Instead of a triangular JOIN however, you should be using a windowed SUM. As you want to also get aggregate nmonths into a single rows, you'll need to also aggregate inside the windowed SUM like so:
SELECT V.YearMonth,
SUM(SUM(V.Value)) OVER (PARTITION BY Year ORDER BY V.YearMonth) AS CumulativeSum
FROM (VALUES (2011, 1, 70, 201101),
(2011, 1, 100, 201101),
(2011, 2, 200, 201102),
(2011, 2, 50, 201102),
(2011, 3, 80, 201103),
(2011, 3, 250, 201103),
(2012, 1, 100, 201201),
(2012, 2, 200, 201202),
(2012, 3, 250, 201203)) V (Year, Month, Value, YearMonth)
GROUP BY V.YearMonth,
V.Year;
I have two tables:
Meter
ID SerialNumber
=======================
1 ABC1
2 ABC2
3 ABC3
4 ABC4
5 ABC5
6 ABC6
RegisterLevelInformation
ID MeterID ReadValue Consumption PreviousReadDate ReadType
============================================================================
1 1 250 250 1 jan 2015 EST
2 1 550 300 1 feb 2015 ACT
3 1 1000 450 1 apr 2015 EST
4 2 350 350 1 jan 2015 EST
5 2 850 500 1 feb 2015 ACT
6 2 1000 150 1 apr 2015 ACT
7 3 1500 1500 1 jan 2015 EST
8 3 2500 1000 1 mar 2015 EST
9 3 5000 2500 4 apr 2015 EST
10 4 250 250 1 jan 2015 EST
11 4 550 300 1 feb 2015 ACT
12 4 1000 450 1 apr 2015 EST
13 5 350 350 1 jan 2015 ACT
14 5 850 500 1 feb 2015 ACT
15 5 1000 150 1 apr 2015 ACT
16 6 1500 1500 1 jan 2015 EST
17 6 2500 1000 1 mar 2015 EST
18 6 5000 2500 4 apr 2015 EST
I am trying to group by meter serial and return the last actual read date for each of the meters but I am unsure as to how to accomplish this. Here is the sql I have thus far:
select a.SerialNumber, ReadTypeCode, MAX(PreviousReadDate) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
where ReadType = 'ACT'
group by a.SerialNumber,b.ReadTypeCode, PreviousReadDate
order by a.SerialNumber
I can't seem to get the MAX function to take effect in returning only the latest actual reading row and it returns all dates and the same meter serial is displayed several times.
If I use the following sql:
select a.SerialNumber, count(*) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
group by a.SerialNumber
order by a.SerialNumber
then each serial is shown only once. Any help would be greatly appreciated.
Like #PaulGriffin said in his comment you need to remove PreviousReadDate column from your GROUP BY clause.
Why are you experiencing this behaviour?
Basically the partition you have chosen - (SerialNumber,ReadTypeCode,PreviousReadDate) for each distinct pair of those values prints you SerialNumber, ReadTypeCode, MAX(PreviousReadDate). Since you are applying a MAX() function to each row of the partition that includes this column you are simply using an aggregate function on one value - so the output of MAX() will be equal to the one without it.
What you wanted to achieve
Get MAX value of PreviousReadDate for every pair of (SerialNumber,ReadTypeCode). So this is what your GROUP BY clause should include.
select a.SerialNumber, ReadTypeCode, MAX(PreviousReadDate) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
where ReadType = 'ACT'
group by a.SerialNumber,b.ReadTypeCode
order by a.SerialNumber
Is the correct SQL query for what you want.
Difference example
ID MeterID ReadValue Consumption PreviousReadDate ReadType
============================================================================
1 1 250 250 1 jan 2015 EST
2 1 550 300 1 feb 2015 ACT
3 1 1000 450 1 apr 2015 EST
Here if you apply the query with grouping by 3 columns you would get result:
SerialNumber | ReadTypeCode | PreviousReadDate
ABC1 | EST | 1 jan 2015 -- which is MAX of 1 value (1 jan 2015)
ABC1 | ACT | 1 feb 2015
ABC1 | EST | 1 apr 2015
But instead when you only group by SerialNumber,ReadTypeCode it would yield result (considering the sample data that I posted):
SerialNumber | ReadTypeCode | PreviousReadDate
ABC1 | EST | 1 apr 2015 -- which is MAX of 2 values (1 jan 2015, 1 apr 2015)
ABC1 | ACT | 1 feb 2015 -- which is MAX of 1 value (because ReadTypeCode is different from the row above
Explanation of your second query
In this query - you are right indeed - each serial is shown only once.
select a.SerialNumber, count(*) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
group by a.SerialNumber
order by a.SerialNumber
But this query would produce you odd results you don't expect if you add grouping by more columns (which you have done in your first query - try it yourself).
You need to remove PreviousReadDate from your Group By clause.
This is what your query should look like:
select a.SerialNumber, ReadTypeCode, MAX(PreviousReadDate) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
where ReadType = 'ACT'
group by a.SerialNumber,b.ReadTypeCode
order by a.SerialNumber
To understand how the group by clause works when you mention multiple columns, follow this link: Using group by on multiple columns
You will understand what was wrong with your query and why it returns all dates and the same meter serial is displayed several times.
Good luck!
Kudos! :)
I'm currently combining two tables through a UNION ALL query and performing SUM and GROUP BY operations on the result. Everything is working as expected, but I have a unique requirement which I can't seem to figure out how to implement.
My aim is to write SQL that says "when DEV_AGE column is >= 12 set the REVENUE value to what it would be if this column was 12". I provide the code below as I know this description can be a bit confusing:
REVENUE table:
ACC_YR DEV_AGE STATE REVENUE LOSS
2012 3 MA 4000 0
2012 6 MA 8000 0
2012 9 MA 12000 0
2012 12 MA 16000 0
LOSS table:
ACC_YR DEV_AGE STATE REVENUE LOSS
2012 3 MA 0 2000
2012 6 MA 0 7000
2012 9 MA 0 9000
2012 12 MA 0 10000
2012 15 MA 0 14000
2012 18 MA 0 14000
2012 21 MA 0 14000
2012 24 MA 0 15000
2012 27 MA 0 17000
Table after UNION ALL, GROUP BY, SUM:
ACC_YR DEV_AGE STATE REVENUE LOSS
2012 3 MA 4000 2000
2012 6 MA 8000 7000
2012 9 MA 12000 9000
2012 12 MA 16000 10000
2012 15 MA 0 14000
2012 18 MA 0 14000
2012 21 MA 0 14000
2012 24 MA 0 15000
2012 27 MA 0 17000
What I WANT to accomplish:
ACC_YR DEV_AGE STATE REVENUE LOSS
2012 3 MA 4000 2000
2012 6 MA 8000 7000
2012 9 MA 12000 9000
2012 12 MA 16000 10000
2012 15 MA 16000 14000
2012 18 MA 16000 14000
2012 21 MA 16000 14000
2012 24 MA 16000 15000
2012 27 MA 16000 17000
In other words, my REVENUE stops developing at a DEV_AGE of 12 (there are no rows in the REVENUE table beyond a DEV_AGE of 12), but I want every DEV_AGE beyond 12 to equal what the REVENUE was at 12 in the final table.
Here is an approach that uses window functions to calculate the revenue for age 12 and then logic to assign it:
select acc_yr, dev_age, state,
(case when dev_age > 12 then rev12 else revenue end) as revenue, loss
from (select l.acc_yr, l.dev_age, l.state, r.revenue, l.loss,
max(case when l.dev_age = 12 then r.revenue end) over (partition by l.acc_yr, l.state) as rev12
from loss l left join
revenue r
on l.acc_yr = r.acc_yr and l.dev_age = r.dev_age and l.state = dev.state
) lr;
I am working on a million data rows table.The table look likes below
Departement year Candidate Spent Saved
Electrical 2013 A 50 50
Electrical 2013 B 25 50
Electrical 2013 C 11 50
Electrical 2013 D 25 0
Electrical 2013 Dt 86 50
Electrical 2014 AA 50 50
Electrical 2014 BB 25 0
Electrical 2014 CH 11 50
Electrical 2014 DG 25 0
Electrical 2014 DH 0 50
Computers 2013 Ax 50 50
Computers 2013 Bc 25 50
Computers 2013 Cx 11 50
Computers 2013 Dx 25 0
Computers 2013 Dx 86 50
I am looking output like below.
Departement year NoOfCandidates NoOfCandidatesWith50$save NoOfCandidatesWith0$save
Electrical 2013 5 4 1
Electrical 2014 5 3 2
Computers 2013 5 4 1
I am using #TEMP tables for every count where conditions and left outer joining at last .So it takes me more time.
Is there any way so i can perform better for above Table .
Thanks in advance.
You want to do this as a single aggregation query. There is no need for temporary tables:
select department, year, count(*) as NumCandidates,
sum(case when saved = 50 then 1 else 0 end) as NumCandidatesWith50Save
sum(case when saved = 0 then 1 else 0 end) as NumCandidatesWith00Save
from table t
group by department, year
order by 1, 2;
I have Three tables as shown below..
I need output as shown in output table
for this i need to join three tables and order output in month order
tbl_MonthList
MonthID MonthList
1 January
2 February
3 March
4 April
5 May
6 June
7 July
8 August
9 September
10 October
11 November
12 December
tbl_Amount:
Month_id Amount_Received Customer_id
3 500 aaa
3 1000 bbb
4 700 jjj
5 300 aaa
5 400 jjj
5 500 ppp
7 1000 aaa
10 1500 bbb
12 700 jjj
tbl_Month_Target
MonthID MonthF_L
1 10000
2 150000
3 1000
4 50000
5 5000
6 3000
7 20000
8 12000
9 34000
10 85000
11 34000
12 45000
I need output as shown below
Month Total_amount MonthF_L
January 0 10000
February 0 150000
March 2000 1000
April 700 50000
May 1200 5000
June 0 3000
July 1000 20000
August 0 12000
September 0 34000
October 1500 85000
November 0 34000
December 700 45000
SELECT ML.MonthList AS Month,
Sum(A.Amount_Received) AS Total_amount,
First(MT.MonthF_L) AS MonthF_L
FROM (tbl_MonthList AS ML
INNER JOIN tbl_Month_Target AS MT ON ML.MonthID = MT.MonthID)
LEFT JOIN tbl_Amount AS A ON ML.MonthID = A.Month_id
GROUP BY ML.MonthList, ML.MonthID
ORDER BY ML.MonthID
Note: In MS Access, multiple joins must be explicitly nested within parentheses
Try this:
select ml.MonthList, sum(a.Amount_Received), mt.MonthF_L from tbl_MonthList ml
left join tbl_Month_Target mt on mt.MonthID = ml.MonthID
left join tbl_Amount a on ml.Month_id = ml.MonthID
group by ml.MonthList, mt.MonthF_L