T-SQL previous year total in next year column - sql

I currently have a table that looks like this:
fscYear ID Days #Invoices AVG
2011 20000807 221 7 27
2012 20000807 403 15 25
2013 20000807 390 14 26
2014 20000807 119 4 23
I would like to include the previous year's AVG in the next year, like so:
fscYear ID Days #Invoices AVG prevAVG
2011 20000807 221 7 27 0
2012 20000807 403 15 25 27
2013 20000807 390 14 26 25
2014 20000807 119 4 23 26
How I can achieve that?
edit the SQL is straightforward,
select * from theTableThatHoldsThedata

Most databases support ANSI standard window functions. You can do this with lag():
select t.*, coalesce(lag(avg) over (order by by fscyear), 0) as prevAVG
from atable t;
This will work in SQL Server 2012+. For earlier versions, you can use a correlated subquery or apply:
select t.*, coalesce(tprev.prevAvg, 0) as prevAvg
from atable t outer apply
(select top 1 t2.Avg as prevAvg
from atable t2
where t2.fscyear < t.fscyear
order by t2.fscyear desc
) tprev;

You can write as:
SELECT Cur.fscYear ,Cur.ID,Cur.Days,Cur.#Invoices,Cur.AVG,
isnull(Prv.AVG,0) AS prevAVG
FROM test AS Cur
LEFT OUTER JOIN test AS Prv
ON Cur.fscYear = Prv.fscYear + 1;
Demo

Related

How to drop a field from a running count

I am tasked with trying to come up with a total count for the number of clients we have had in any given year. I am able to run a total count of the clients we have had, but I want to drop them from the running total when they offboard from us (i.e. #EndDate)
DECLARE #EndDate Date
SET #EndDate = (SELECT DISTINCT LOAEndDate FROM tblCompany)
SELECT DISTINCT Year(DateBecameClient) AS [Year],
Count(CompanyId) OVER (ORDER BY Year(DateBecameClient)) AS NumberofClients
FROM [tblCompany] AS Company
ORDER BY [Year]
Here is the output that I get without including #EndDate.
--------------------
Year NumberofClients
2001 3
2002 6
2003 9
2004 10
2005 13
2006 15
2007 16
2008 26
2009 36
2010 78
2011 135
2012 204
2013 314
2014 385
2015 456
2016 471
2017 496
2018 507
2019 513
2020 514
2021 516
I presume that you have a separate date that indicates when the client left. You'll want to counterbalance with a -1 via a union. If a client was added and lost within the same year it'll never be counted:
with data as (
select year(DateBecameClient) as yr, 1 as num
from tblCompany
union all
select year(DateLostClient), -1
from tblCompany
)
select yr as "Year", sum(sum(num)) over (order by yr) as NumberOfClients
from data
group by yr
order by "Year";
I'm using grouping with a sum of sums to get around needing distinct. This is basically the same as your query except for the addition of the negative counters.

Detect and Set Column values between rows

I have a table relating to products:
PRD_SLD table
ID DATE SALE_IND
3 2012 0
3 2013 0
3 2014 1
3 2014 1
3 2015 1
3 2016 0
3 2017 1
I would like my final results to look like this:
PRD_SLD table
ID DATE SALE_IND STRT END
3 2012 0 2012 2014
3 2013 0 2012 2014
3 2014 1 2014 2016
3 2014 1 2014 2016
3 2015 1 2014 2016
3 2016 0 2016 2017
3 2017 1 2017 2017
I currently have a working CTE for retrieving the rows in which the values change. this CTE returns this:
PRD_SLD table
ID DATE SALE_IND
3 2012 0
3 2014 1
3 2016 0
3 2017 1
So it returns the first instance of the value in the table, and returns every time the SALE_IND changes.
Is there a way to create a start and end date based off of the date column? I am still very new to this and was enrolled in an advanced course. I'm sure there is a better way to complete this but is there a way to do it with the CTE results i have created? I know there is a between function but i don't know how to implement it into this query
One method is to define groups of adjacent records. You don't have a solid ordering of the rows, but you do have just enough information for this to work -- assuming the indicator is constant in each year.
select t.*,
min(year) over (partition by id, sale_ind, seqnum - seqnum_s) as min_year,
max(year) over (partition by id, sale_ind, seqnum - seqnum_s) as max_year
from (select t.*,
dense_rank() over (partition by id order by date) as seqnum,
dense_rank() over (partition by id, sale_ind order by date) as seqnum_s
from t
) t;

SUM and Count in one SQL Query

I have this kind of data
time Members
-------------------------------------------------- -----------
Jun 23 2016 1
Jun 23 2016 1
Jun 23 2016 2
Jun 29 2016 6
Jul 11 2016 3
Jul 11 2016 1
Jul 13 2016 1
I obtained this data using this sql query
SELECT CONVERT (VARCHAR(12), a.registered_time), COUNT(b.member_id) AS Members
FROM b
Inner JOIN a ON b.mirror_id = a.mirror_id
GROUP BY
(a.registered_time) order by a.registered_time
I want to get the sum of total numbers if they are of the same date for exampple the date of June 23 2016 will have total members of 4 and so on. Is it possible to have SUM() FUnction on Count()? How can I do this?
Convert the value to a date and include that in both the select and group by:
SELECT CONVERT(date, a.registered_time) as dte, COUNT(b.member_id) AS Members
FROM b JOIN
a
ON b.mirror_id = a.mirror_id
GROUP BY CONVERT(date, a.registered_time)
ORDER BY CONVERT(date, a.registered_time);

SQL Nested Multiple Select Statement

Im trying to create a nested SQL statement with multiple Nested Select Statements, to be used on a record set import in excel vba.
What i want to do is create something like:
SELECT
N.LimitN,
Sum(N.amountN),
Sum(N1.amountN1)
FROM (
SELECT year as yearN, Sum(amount) as amountN, limit as limitN
FROM table1
WHERE year = 2013
GROUP BY year, limit) as N
JOIN (
SELECT year as yearN1, Sum(amount) as amountN1, limit as limitN1
FROM table1
WHERE year = 2014
GROUP BY year, limit) as N1
ON N.LimitN = N1.LimitN1
GROUP BY N.LimitN
ORDER BY N.LimitN;
So that if my Raw data is like this:
Year Amount Limit
2013 100 20
2013 90 30
2013 120 40
2013 5 20
2013 100 30
2013 105 40
2013 150 50
2014 115 20
2014 50 30
2014 95 40
2014 110 50
2014 30 20
My Resulting Table/record set will be like this:
Limit AmountN (i.e. 2013) Amount N1 (i.e. 2014)
20 105 145
30 190 50
40 225 95
50 150 110
Thanks in Advance
Peter
It feels like you're overcomplicating the query a little, what you want is just a year wise sum of amount, grouped by limit. This can be done using a CASE;
SELECT
limit,
SUM(CASE WHEN year=2013 THEN amount ELSE 0 END) amountN,
SUM(CASE WHEN year=2014 THEN amount ELSE 0 END) amountN1
FROM myTable
GROUP BY limit
ORDER BY limit;
An SQLfiddle to test with.
(if we're talking Access here, you will need to use IIF instead of CASE)

Generate year to date by month report in SQL [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Running total by grouped records in table
I am trying to put together an SQL statement that returns the SUM of a value by month, but on a year to date basis. In other words, for the month of March, I am looking to get the sum of a value for the months of January, February, and March.
I can easily do a group by to get a total for each month by itself, and potentially calculate the year to date value I need in my application from this data by looping through the results set. However, I was hoping to have some of this work handled with my SQL statement.
Has anyone ever tackled this type of problem with an SQL statement, and if so, what is the trick that I am missing?
My current sql statement for monthly data is similar to the following:
Select month, year, sum(value) from mytable group by month, year
If I include a where clause on the month, and only group by the year, I can get the result for a single month that I am looking for:
select year, sum(value) from mytable where month <= selectedMonth group by year
However, this requires me to have a particular month pre-selected or to utilize 12 different SQL statements to generate one clean result set.
Any guidance that can be provided would be greatly appreciated!
Update: The data is stored on an IBM iSeries.
declare #Q as table
(
mmonth INT,
value int
)
insert into #Q
values
(1,10),
(1,12),
(2,45),
(3,23)
select sum(January) as UpToJanuary,
sum(February)as UpToFebruary,
sum(March) as UpToMarch from (
select
case when mmonth<=1 then sum(value) end as [January] ,
case when mmonth<=2 then sum(value) end as [February],
case when mmonth<=3 then sum(value) end as [March]
from #Q
group by mmonth
) t
Produces:
UpToJanuary UpToFebruary UpToMarch
22 67 90
You get the idea, right?
NOTE: This could be done easier with PIVOT tables but I don't know if you are using SQL Server or not.
As far as I know DB2 does support windowing functions although I don't know if this is also supported on the iSeries version.
If windowing functions are supported (I believe IBM calls them OLAP functions) then the following should return what you want (provided I understood your question correctly)
select month,
year,
value,
sum(value) over (partition by year order by month asc) as sum_to_date
from mytable
order by year, month
create table mon
(
[y] int not null,
[m] int not null,
[value] int not null,
primary key (y,m))
select a.y, a.m, a.value, sum(b.value)
from mon a, mon b
where a.y = b.y and a.m >= b.m
group by a.y, a.m, a.value
2011 1 120 120
2011 2 130 250
2011 3 500 750
2011 4 10 760
2011 5 140 900
2011 6 100 1000
2011 7 110 1110
2011 8 90 1200
2011 9 70 1270
2011 10 150 1420
2011 11 170 1590
2011 12 600 2190
You should try to join the table to itself by month-behind-a-month condition and generate a synthetic month-group code to group by as follows:
select
sum(value),
year,
up_to_month
from (
select a.value,
a.year,
b.month as up_to_month
from table as a join table as b on a.year = b.year and b.month => a.month
)
group by up_to_month, year
gives that:
db2 => select * from my.rep
VALUE YEAR MONTH
----------- ----------- -----------
100 2011 1
200 2011 2
300 2011 3
400 2011 4
db2 -t -f rep.sql
1 YEAR UP_TO_MONTH
----------- ----------- -----------
100 2011 1
300 2011 2
600 2011 3
1000 2011 4