How to get Fiscal Year to only show the last 3 years - sql

This question is probably a duplicate but I couldn't find it after 10 mins of research. Here is the question:
I am trying to get the last 3 Fiscal years to show up in my query: so for me it would be 2018, 2019, and 2020.
SELECT distinct E.FISCALYEAR
FROM EMPLOYEES as E
INNER JOIN HR_PERIODS as H ON E.PERIOD = H.PERIOD
WHERE
E.FISCALYEAR <= year(getdate()) + 1
and E.RECORDSTATUS = 1
ORDER BY E.FISCALYEAR
The query I currently have does:
2015
2016
2017
2018
2019
2020
Also our HR Fiscal Year populates from 2015 to Present fiscal year.

You can use top (3):
SELECT distinct TOP (3) E.FISCALYEAR
FROM RPT_EMPLOYEECENSUS_ASOF E INNER JOIN
HR_PERIODS H
ON E.PERIODNUMBER = H.PERIODNUMBER
WHERE E.FISCALYEAR <= year(getdate()) + 1 AND
E.EOM_RECORDSTATUS_EFF = 1
ORDER BY E.FISCALYEAR DESC

Related

Left outer join for multiple users

I have a database for rounds of golf. I want to see how many rounds each user has per month for the last year.
To do that I created this view last_12_months with this code
SELECT date_part('month'::text, dates.date) AS month,
date_part('year'::text, dates.date) AS year
FROM ( SELECT (generate_series((now() - '1 year'::interval), now(), '1 mon'::interval))::date AS date) dates;
month
year
1
2020
2
2020
and so on to ...
12
2020
The summary view for counting the rounds is very simple also rounds_count_by_users
user_id
month
year
count_all
1
1
2020
15
1
3
2020
12
1
5
2020
10
2
4
2020
7
2
8
2020
6
2
9
2020
3
Now for what I want, querying for each user with left outer join is quite simple with
select *
from last_12_months
left outer join rounds_count_by_users
on last_12_months.month = rounds_count_by_users.month
and last_12_months.year = rounds_count_by_users.year
and user_id = 1
Which gives me all the months even when the user has no played rounds. What I would like however is to be able to do this for every user and make a materialized view for easy querying. Is there a nice and easy way of doing this? To be clear this is the final table I want.
This query doesn't work at least, that much I know.
select *
from last_12_months
left outer join rounds_count_by_users
on last_12_months.month = rounds_count_by_users.month
and last_12_months.year = rounds_count_by_users.year
where user_id = 1
user_id
month
year
count_all
1
1
2020
15
1
2
2020
null
1
3
2020
12
1
4
2020
null
1
5
2020
10
1
6
2020
null
1
7
2020
null
1
8
2020
null
1
9
2020
null
1
10
2020
null
1
11
2020
null
1
12
2020
null
2
1
2020
null
2
2
2020
null
2
3
2020
null
2
4
2020
7
2
5
2020
null
2
6
2020
null
2
7
2020
null
2
8
2020
6
2
9
2020
3
2
10
2020
null
2
11
2020
null
2
12
2020
null
I made an SQL Fiddle for this (slightly different values but same schema)
PS: I know about table aliases and data modeling and that stuff. My question is strictly about how to achieve the final result.
This query:
select * from last_12_months
where (year = year(current_date) - 1 and month >= month(current_date))
or
(year = year(current_date) and month < month(current_date))
returns the rows of last_12_months for the last 12 months (not including the current month).
This query:
select distinct user_id from rounds_count_by_users
returns all the distinct user_ids (it would be better if these ids where stored in a users table).
You must CROSS join the above queries and then LEFT join rounds_count_by_users:
select u.user_id, m.month, m.year, r.count_all
from (
select * from last_12_months
where (year = year(current_date) - 1 and month >= month(current_date))
or
(year = year(current_date) and month < month(current_date))
) m cross join (select distinct user_id from rounds_count_by_users) u
left outer join rounds_count_by_users r
on m.month = r.month and m.year = r.year and u.user_id = r.user_id
order by u.user_id, m.month
See the demo.
So I managed it to do it this way after help in the comments.
This leaves me with exactly what I wanted.
with all_rows as (
select * from view_last_12_months inner join users on true
)
select id as user_id, all_rows.year, all_rows.month, count_all
from all_rows left outer join view_rounds_count_last_year last12
on last12.month = all_rows.month
and last12.year = all_rows.year
and all_rows.id = last12.user_id

Running Total in Oracle SQL - insert missing rows

Let's assume I have following set of data in Oracle SQL database:
Product Year Month Revenue
A 2016 1 7
A 2016 5 15
After creating running totals with following code
select Product, Year, Month, Revenue,
sum(Revenue) over (partition by Product, Year order by Month) Revenue_Running
from exemplary_table
I receive following result:
Product Year Month Revenue Revenue_Running
A 2016 1 7 7
A 2016 5 15 22
Is there any way that I can get this:
Product Year Month Revenue Revenue_Running
A 2016 1 7 7
A 2016 2 (null) 7
A 2016 2 (null) 7
A 2016 4 (null) 7
A 2016 5 15 22
You need a calendar table and Left join with your exemplary_table
SELECT p.product,
c.year,
c.month,
COALESCE(revenue, 0),
Sum(revenue)OVER (partition BY p.product, c.year ORDER BY c.month) Revenue_Running
FROM calendar_table c
CROSS JOIN (SELECT DISTINCT product
FROM exemplary_table) p
LEFT JOIN exemplary_table e
ON c.year = e.year
AND e.month = c.month
WHERE c.dates >= --your start date
AND c.dates <= --your end date

SQL: add missing months from different years

SQL SERVER
[CreatedOn] - DATETIME
I get this table:
Year Month Count
2009 7 1
2009 9 1
2010 1 2
2010 3 13
From query:
SELECT
YEAR ([CreatedOn]) AS 'Year',
MONTH ([CreatedOn]) AS 'Month',
COUNT ([CreatedOn]) AS 'Count'
FROM xxx
GROUP BY YEAR ([CreatedOn]), MONTH ([CreatedOn])
How can I get table like this (with missed months and Count 0):
Year Month Count
2009 7 1
2009 8 0
2009 9 1
2009 10 0
2009 11 0
2009 12 0
2010 1 2
2010 2 0
2010 3 13
Syntax says you are using MSSQL. Use Recursive CTE to generate the calender table then do a Left outer join with XXX table
DECLARE #maxdate DATE = (SELECT Max([CreatedOn])
FROM xxx);
WITH calender
AS (SELECT Min([CreatedOn]) dates,
FROM xxx
UNION ALL
SELECT Dateadd(mm, 1, dates)
FROM cte
WHERE dates < #maxdate)
SELECT Year(dates) [YEAR],
Month(dates) [month],
Count ([CreatedOn]) AS 'Count'
FROM calender a
LEFT OUTER JOIN xxx b
ON Year(dates) = Year ([CreatedOn])
AND Month(dates) = Month ([CreatedOn])
GROUP BY Year(dates),
Month(dates)
Note : Instead of Recursive CTE create a physical calender table
This will use a build in table to create the calendar:
;WITH limits as
(
SELECT min([CreatedOn]) mi, max([CreatedOn]) ma
FROM xxx
), months as(
SELECT
dateadd(mm, number, mi) m
FROM
master..spt_values v
JOIN
limits l
ON
number between 0 and datediff(mm, l.mi, l.ma)
WHERE
v.type = 'P'
)
SELECT
year(months.m) year,
month(months.m) month,
count(qry.[CreatedOn]) cnt
FROM
xxx qry
RIGHT JOIN
months
ON
months.m = dateadd(mm, datediff(mm, 0, qry.[CreatedOn]), 0)
GROUP BY
year(months.m),
month(months.m)

Parameter month selection: Make a query that shows previous month, 12 months ago and last 12 months average

I wanted to play around with my Total_Sales table.
This is how the data looks like (using SQL Server 2008 R2)
Name Year Month Sales
------ ---- ----- -----
Alfred 2011 1 100
Alfred 2011 2 200
Alfred 2011 3 300
Alfred 2011 4 400
Alfred 2011 5 500
Alfred 2011 6 600
Alfred 2011 7 700
Alfred 2011 8 800
Alfred 2011 9 900
Alfred 2011 10 500
Alfred 2011 11 500
Alfred 2011 12 500
The SQL query I want to create should display the data like this:
Name Year Month Sales Prev_Month Month_Last_Year_Sales Last_12_Month_AVG
------ ---- ----- ----- ---------- --------------------- -----------------
Alfred 2011 1 100 NULL (year 2010, month 1) (2010_01 to 2011_01)/(12)
Alfred 2011 2 200 100 (year 2010, month 2) (2010_02 to 2011_02)/(12)
Alfred 2011 3 300 200 (year 2010, month 3) (2010_03 to 2011_03)/(12)
Alfred 2011 4 400 300 (year 2010, month 4) (2010_04 to 2011_04)/(12)
Alfred 2011 5 500 400 (year 2010, month 5) (2010_05 to 2011_05)/(12)
Alfred 2011 6 600 500 (year 2010, month 6) (2010_06 to 2011_06)/(12)
Alfred 2011 7 700 600 (year 2010, month 7) (2010_07 to 2011_07)/(12)
Alfred 2011 8 800 700 (year 2010, month 8) (2010_08 to 2011_08)/(12)
Alfred 2011 9 900 800 (year 2010, month 9) (2010_09 to 2011_09)/(12)
Alfred 2011 10 500 900 (year 2010, month 10) (2010_10 to 2011_10)/(12)
Alfred 2011 11 500 500 (year 2010, month 11) (2010_11 to 2011_11)/(12)
Alfred 2011 12 500 500 (year 2010, month 12) (2010_12 to 2011_12)/(12)
To copy the prior month I am using this: Copy prior month value and insert into new row
SELECT
TS.name,
TS.year,
TS.month,
TS.sales,
COALESCE(TS2.sales, 0) AS prior_month_sales
FROM
TotalSales TS
LEFT OUTER JOIN TotalSales TS2 ON
TS2.name = TS.name AND
(
(TS2.year = TS.year AND TS2.month = TS.month - 1) OR
(TS.month = 1 AND TS2.month = 12 AND TS2.year = TS.year - 1)
)
The NULL in Prev_Month is to show that the start of the Total_Sales was in year 2011 month 1, so no prior data for this example.
I am planning to use a parameter, where you select a month.
Thanks for any help!
SELECT
[this_month].*,
[last_month].Sales AS [prev_month_sales],
[last_year].Sales AS [month_last_year_sales],
[yearly].AverageSales AS [last_12_month_average]
FROM
Total_Sales AS [this_month]
LEFT JOIN
Total_Sales AS [last_month]
ON [last_month].Name = [this_month].Name
AND (
([last_month].Year = [this_month].Year AND [last_month].Month = [this_month].Month - 1)
OR ([last_month].Year = [this_month].Year - 1 AND [last_month].Month = 12 AND [this_month].Month = 1)
)
LEFT JOIN
TotalSales AS [last_year]
ON [last_year].Name = [this_month].Name
AND [last_year].Year = [this_month].Year - 1
AND [last_year].Month = [this_month].Month
CROSS APPLY
(
SELECT
AVG(Sales) AS AverageSales
FROM
Total_Sales
WHERE
Name = [this_month].Name
AND (
(Year = [this_month].Year AND Month <= [this_month].Month)
OR (Year = [this_month].Year - 1 AND Month > [this_month].Month)
)
)
AS [yearly]
The Average isn't the value divided by 12, as there are not always 12 months worth of data in the preceding year. But the AVG() function takes care of that for you.
Also, I'd highly reccomend against using YEAR and MONTH fields. Instead I would recommend using a DATETIME field to represent the "Month Start" and using SQL Server's Date functions...
Last Month : MonthStart = DATEADD(MONTH, -1, ThisMonth)
A Year Ago : MonthStart = DATEADD(YEAR, -1, ThisMonth)
Last Year : MonthStart > DATEADD(YEAR, -1, ThisMonth) AND MonthStart <= ThisMonth
Another answer that I have no idea is faster or not...
WITH sales AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Year, Month) AS month_id,
*
FROM
yearly_sales
)
SELECT
Name = [this_month].Name,
Year = MAX([this_month].Year),
Month = MAX([this_month].Month),
Sales = MAX([this_month].Sales),
Last_Month = MAX(CASE WHEN [13_months].month_id = [this_month].month_id - 1 THEN [13_months].Sales END),
Last_Year = MAX(CASE WHEN [13_months].month_id = [this_month].month_id - 12 THEN [13_months].Sales END),
Yearly_AVG = AVG(CASE WHEN [13_months].month_id > [this_month].month_id - 12 THEN [13_months].Sales END)
FROM
Sales AS [this_month]
INNER JOIN
Sales AS [13_months]
ON [13_months].Name = [this_month].Name
AND [13_months].month_id <= [this_month].month_id
AND [13_months].month_id >= [this_month].month_id - 12
GROUP BY
[this_month].Name
From AceAlfred -
One problem I have run into, maybe you know a quick fix? When a employee has not booked his sales for a previous month there is no data to display for this individual. Is there a way to add a row with the missing employee, where the "sales" is set to 0 and still pull the data for the other rows? Ex. Year 2012 -- Month 1 -- Name Alfred -- Sales 0 -- Prev 500
One approach is to "fix" your data, ensuring it always has values in it. I'd recommend doing that in whatever system is populating your data. Or as a nightly batch that checks for people that didn't enter their data and sticks in 0's for you (To be updated if/when the real data arrives). But if you can't...
CREATE TABLE agent (id INT, name NVARCHAR(128), start_date DATETIME, leave_date DATETIME);
-- populate with your agents
CREATE TABLE calendar (year DATETIME, month DATETIME, day DATETIME);
-- populate with all dates you want to report on
CREATE TABLE sales (agent_id INT, month_start DATETIME, total INT);
-- populate with your data
WITH new_raw_data AS
(
SELECT
agent.id AS [agent_id],
calendar.month AS [month_start],
COALESCE(sales.total, 0) AS [total]
FROM
agent
INNER JOIN
calendar
ON calendar.month_start >= COALESCE(DATEADD(month, -1, agent.start_date), '2000 Jan 01')
AND calendar.month_start <= COALESCE(agent.leave_date, '2079 Dec 31')
LEFT JOIN
sales
ON sales.agent_id = agent.id
AND sales.month_start = calendar.month_start
WHERE
calendar.month_start = calendar.day -- Only use records for the start of each month
)
,
<your other queries, using the nicely cleaned data, go here.>

How to get month and year in single column and grouping the data for all the years and months?

For the below query (sdate is column name and table name is storedata)
Collapse
WITH TotalMonths AS (SELECT T1.[Month], T2.[Year]
FROM ((SELECT DISTINCT Number AS [Month]
FROM MASTER.dbo.spt_values WHERE [Type] = 'p' AND Number BETWEEN 1 AND 12) T1 CROSS JOIN
(SELECT DISTINCT DATEPART(year, sdate) AS [Year]
FROM storedata) T2))
SELECT CTE.[Year], CTE.[Month], ISNULL(T3.[Sum], 0) areasum
FROM TotalMonths CTE LEFT OUTER JOIN (
SELECT SUM(areasft) [Sum], DATEPART(YEAR, sdate) [Year], DATEPART(MONTH, sdate) [Month]
FROM storedata
GROUP BY DATEPART(YEAR, sdate) ,DATEPART(MONTH, sdate)) T3
ON CTE.[Year] = T3.[Year] AND CTE.[Month] = T3.[Month] WHERE CTE.[Year]>'2007'
ORDER BY CTE.[Year], CTE.[Month]
I am getting result set like below.
YEAR MONTH AREASUM
2008 1 0
2008 2 1193
2008 3 4230
2008 4 350
2008 5 2200
2008 6 4660
2008 7 0
2008 8 6685
2008 9 0
2008 10 3051
2008 11 7795
2008 12 2940
2009 1 1650
2009 2 3235
2009 3 2850
2009 4 6894
2009 5 3800
2009 6 2250
2009 7 1000
2009 8 1800
2009 9 1550
2009 10 2350
2009 11 0
2009 12 1800
But I have to combine both month and year in single column. The reult set should like below.
JAN/08 O
FEB/08 1193
.. ..
.. ..
DEC/O9 1800
How can I modify my query? (I should display for all the years and months even if there is no area for a month)
Regards,
N.SRIRAM
Try:
SELECT CONVERT(VARCHAR(3), DATENAME(MONTH, CTE.Month), 7) + '/' + RIGHT(CTE.Year, 2)
instead of using your first 2 columns from your SELECT.
You seem to be saying that you're getting the right data from your original query, but the wrong format. So
Make a view out of the query you originally posted.
Build a SELECT query based on that view to give you the format you want.
Let's say you do this:
CREATE VIEW wibble AS <your original query goes here>
Then you can just query wibble to correct the formatting.
select
case
when month = 1 then 'Jan/'
when month = 2 then 'Feb/'
when month = 3 then 'Mar/'
when month = 4 then 'Apr/'
when month = 5 then 'May/'
when month = 6 then 'Jun/'
when month = 7 then 'Jul/'
when month = 8 then 'Aug/'
when month = 9 then 'Sep/'
when month = 10 then 'Oct/'
when month = 11 then 'Nov/'
when month = 12 then 'Dec/'
else 'Err'
end || substring(cast(year as CHAR(4)), 3, 2) as yearmonth,
areasum from wibble;