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

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

Related

Linear Interpolation in SQL

I work with crashes and mileage for the same year which is Year in table. Crashes are are there for every record, but annual mileage is not. NULLs for mileage could be at the beginning or at the end of the time period for certain customer. Also, couple of annual mileage records can be missing as well. I do not know how to overcome this. I try to do it in CASE statement but then I do not know how to code it properly. Issue needs to be resolved in SQL and use SQL Server.
This is how the output looks like and I need to have mileage for every single year for each customer.
The info I am pulling from is proprietary database and the records themselves should be untouched as is. I just need code in query which will modify my current output to output where I have mileage for every year. I appreciate any input!
Year
Customer
Crashes
Annual_Mileage
2009
123
5
3453453
2010
123
1
NULL
2011
123
0
54545
2012
123
14
376457435
2013
123
3
63453453
2014
123
4
NULL
2015
123
15
6346747
2016
123
0
NULL
2017
123
2
534534
2018
123
7
NULL
2019
123
11
NULL
2020
123
15
565435
2021
123
12
474567546
2022
123
7
NULL
Desired Results
Year
Customer
Crashes
Annual_Mileage
2009
123
5
3453453
2010
123
1
175399 (prior value is taken)
2011
123
0
54545
2012
123
14
376457435
2013
123
3
63453453
2014
123
4
34900100 (avg of 2 adjacent values)
2015
123
15
6346747
2016
123
0
3440641 (avg of 2 adjacent values)
2017
123
2
534534
2018
123
7
534534 ( prior value is taken)
2019
123
11
549985 (avg of 2 adjacent values)
2020
123
15
565435
2021
123
12
474567546
2022
123
7
474567546 (prior value is taken)
SELECT Year,
Customer,
Crashes,
CASE
WHEN Annual_Mlg IS NOT NULL THEN Annual_Mlg
WHEN Annual_Mlg IS NULL THEN
CASE
WHEN PREV.Annual_Mlg IS NOT NULL
AND NEXT.Annual_Mlg IS NOT NULL
THEN ( PREV.Annual_Mlg + NEXT.Annual_Mlg ) / 2
ELSE 0
END
END AS Annual_Mlg
FROM #table
The above code doesn't work, but I just need to start somehow and that what I have currently.
I understand what I need to do I just do not know how to code it in SQL.
After i applied row_number () function i got this output for first 2 clients and for the rest of the 4 clients row_number() function gave correct output. i have no idea why is that. I thought may be because i used "full join" before to combine mileage and crashes table?
enter image description here
Your use of #table tells me that you're using MS SQL Server (a temporary table, probably in a stored procedure).
You want to:
select all the rows in #table
joined with the matching row (if any) for the previous year, and
joined with the matching row (if any) for the next year
Then it's easy. Assuming the primary key on your #table is composed of the year and customer columns, something like this ought to do you:
select t.year ,
t.customer ,
t.crashes ,
annual_milage = coalesce(
t.annual_milage ,
( coalesce( p.annual_mileage, 0 ) +
coalesce( n.annual_mileage, 0 )
) / 2
)
from #table t -- take all the rows
left join #table p on p.year = t.year - 1 -- with the matching row for
and p.customer = t.customer -- the previous year (if any)
left join #table n on n.year = t.year + 1 -- and the matching row for
and n.customer = t.customer -- the next year (if any)
Notes:
What value you default to if the previous or next year doesn't exist is up to you (zero? some arbitrary value?)
Is the previous/next year guaranteed to be the current year +/- 1?
If not, you may have to use derived tables as the source for the
prev/next data, selecting the closest previous/next year (that sort
of thing rather complicates the query significantly).
Edited To Note:
If you have discontiguous years for each customer such that the "previous" and "next" years for a given customer are not necessarily the current year +/- 1, then something like this is probably the most straightforward way to find the previous/next year.
We use a derived table in our from clause, and assign a sequential number in lieu of year for each customer, using the ranking function row_number() function. This query, then
select row_nbr = row_number() over (
partition by x.customer
order by x.year
) ,
x.*
from #table x
would produce results along these lines:
row_nbr
customer
year
...
1
123
1992
...
2
123
1993
...
3
123
1995
...
4
123
2020
...
1
456
2001
...
2
456
2005
...
3
456
2020
...
And that leads us to this:
select year = t.year ,
customer = t.customer ,
crashes = t.crashes ,
annual_mileage = coalesce(
t.mileage,
coalesce(
t.annual_mileage,
(
coalesce(p.annual_mileage,0) +
coalesce(n.annual_mileage,0)
) / 2
),
)
from (
select row_nbr = row_number() over (
partition by x.customer
order by x.year
) ,
x.*
from #table x
) t
left join #table p on p.customer = t.customer and p.row_nbr = t.row_nbr-1
left join #table n on n.customer = t.customer and n.row_nbr = t.row_nbr+1

firebird sql order by

i got a dataset which looks like this:
customernumber year value
1 2011 500
2 2011 100
1 2010 400
3 2010 600
3 2011 300
2 2010 700
i want it to be ordered by highest value of year 2011, but the rows of each customer need to stay together.
it should look like this:
customernumber year value
1 2011 500
1 2010 400
3 2011 300
3 2010 600
2 2011 100
2 2010 700
is this even possible?
thanks in advance!
Use join to bring that value in, and then you can use it for the oder by:
select d.customernumber, d.year, d.value
from dataset d join
(select d.*
from dataset d
where d.year = 2011
) d2011
on d.customernumber = d2011.customernumber
order by d2011.value, d.customernumber, d.year desc;
In databases that support window functions, this can more easily be done as:
select d.*
from dataset d
order by max(case when year = 2011 then value end) over (partition by customernumber),
customernumber, year desc;
select customernumber, year, value from mytable
group by customernumber, year, value
order by year desc

select columns between month and year

I have table with columns:
id month year
1 10 2011
2 1 2012
3 4 2011
4 3 2012
I Want select ids where (month=10 and year=2011) and (month=1 and year=2012), it's possible?
This is a basic SQL SELECT:
SELECT id FROM myTable WHERE (month = 10 AND year = 2011) OR (month = 1 AND year = 2012);
To search for rows between any two dates, the simplest solution may be to combine the month and year into a single number and then use numeric comparison:
SELECT id
FROM myTable
WHERE year*100 + month BETWEEN 201110 AND 201201
A misfeature of this solution is that it can't take advantage of indexes, so it will be slow on very large tables.

Getting a variable end of year date and value from MS Access table using SQL

I have some data is that is daily (day on day) closing figures for a tracked supply and is in one MS Access table that has 2 columns - Dates (the date), PXLast(the day's closing figure)).
I have daily data from Jan 1991 to Aug 2013 and I wanted to get the percentage change of PXLast at every year end compared to last year year end as follows:
Year | Percentage Change of PXLast(Year on Year)
1991 | 15.2%
1992 | 9.2%
The year end date varies (not always 31st ) and I am going about getting the last PXLast value by:
1.Get the max date in Dec every year: results in MyYear, MyMonth, MyDay
2.Combine it using DateSerial(MyYear, MyMonth, MyDay)
3.Join the resulting query to the table and inner join on the date column
4.Get the PXLast value
SELECT EndDates.EndDates, NSE20.PX_LAST AS LookPoint
FROM NSE20 INNER JOIN
(SELECT DateSerial([MyYear],[MyMonth],[MyDay])
AS EndDates FROM (SELECT 12 AS MyMonth, MyDay, MyYear FROM
(SELECT Max(Day([Dates])) AS MyDay, Year([Dates]) AS MyYear
FROM NSE20 WHERE (((Month([Dates]))=12))
GROUP BY Year([Dates])) AS EndYearValues)
AS EndValueDates)
AS EndDates ON NSE20.Dates = EndDates.EndDates;
Could anyone assist me get the corresponding value using a query for previous year end
eg for 29 Dec 2006, it should show the current value and show the value for 31 Dec 2005
in the same row ie
Year | Current Year End| Previous Year End
2005 | 3449.00 | 4611.19
2006 | 9.2% |3449.00
Any help is appreciated.
Any suggestions to a better way of doing this is very very welcome....
Let's assume that you have some test data in a table named [NSE20] that looks like this
Dates PXLast
---------- ------
2010-07-01 131
2010-12-31 130
2011-11-12 123
2011-12-30 125
2012-01-03 127
2012-12-31 129
I'd start by creating a saved query in Access named [NSE20_year_ends] that identifies the year-end dates by (calendar) year:
SELECT Year(Dates) AS CalendarYear, Max(Dates) AS YearEndDate
FROM NSE20
GROUP BY Year(Dates)
That will produce
CalendarYear YearEndDate
------------ -----------
2010 2010-12-31
2011 2011-12-30
2012 2012-12-31
Then I'd create another saved query named [NSE20_year_end_balances] to extract the closing balances for each year:
SELECT NSE20_year_ends.CalendarYear, NSE20.PXLast
FROM
NSE20
INNER JOIN
NSE20_year_ends
ON NSE20.Dates = NSE20_year_ends.YearEndDate
That will give us
CalendarYear PXLast
------------ ------
2010 130
2011 125
2012 129
Now we can do a self-join on that query to calculate the percentage change
SELECT
y1.CalendarYear,
(y1.PXLast - y0.PXLast) / y0.PXLast * 100 AS PctChange
FROM
NSE20_year_end_balances y1
INNER JOIN
NSE20_year_end_balances y0
ON y0.CalendarYear = y1.CalendarYear - 1
resulting in
CalendarYear PctChange
------------ -----------------
2011 -3.84615384615385
2012 3.2

How to inserting an intermediate row?

I have the following table:
Year Line January Febraury March .... December
2011 B1 5 10 20
2012 B1 10 15 25 ...
2011 A1 4 8 10 ...
And I want to insert a subtotal row each two lines (if exists), in particular each time year and Line changing: so
Year Line January Febraury March .... December
2011 B1 5 10 20
2012 B1 10 15 25 ...
--- B1 +100% +50% +25% ..
2011 A1 4 8 10 ...
How can I do this in T-SQL ?
Maybe using cursor ?
Are you certain that you want to insert a new row? Or just be able to calculate that subtotal when you query the data?
Query Version
SELECT
Year,
Line,
SUM(January) AS January,
SUM(February) AS February,
...
SUM(December) AS December
FROM
yourTable
GROUP BY
Year,
Line
WITH
ROLLUP
ORDER BY
Year,
Line
Insert Version
If you just one one level of summary, remove the WITH ROLLUP
INSERT INTO
yourTable
SELECT
Year,
NULL,
SUM(January) AS January,
SUM(February) AS February,
...
SUM(December) AS December
FROM
yourTable
GROUP BY
Year
WITH
ROLLUP
EDIT Follow question edit
I strongly suggest that you mean a query, not a change to the actual data. I also suggest that you either build these lines in your reporting environment, or you put the % values to the right of each record...
SELECT
this_year.Year,
this_year.Line,
this_year.January,
CAST(this_year.January AS DECIMAL(8,2)) / CAST(last_year.January AS DECIMAL(8,2)) AS January_Change,
...
FROM
yourTable AS this_year
LEFT JOIN
yourTable AS last_year
ON last_year.year = this_year.year-1
AND last_year.line = this_year.line