SQL concat case select when trying to add new column of previous month data - sql

I have a data set in which I need to concat month and year for the previous month. the problem is it spans over two years. I need to create a statement in which mean the month - 1 = 0, month becomes 12 and year-1.
Select concat(case
when month > 1 then select(month-1, year)
when month = 1 then select(12, year -1)
else select(month-1, year)
end
as monthyear
from table

You need 2 CASE statements inside concat(), for the month and the year:
select
concat(
case when month > 1 then month - 1 else 12 end,
case when month > 1 then year else year - 1 end
)

If you have month and year as separate columns and want to subtract one month, then you can use:
select (case when month > 1 then year else year - 1 end) as year,
(case when month > 1 then month - 1 else 12 end) as month

Here's a different approach (tried with SQLite3, adjust accordingly to your SQL syntax):
-- some test data first
create table t(yy,mm);
insert into t values
(2018,1),(2018,2),(2018,3),(2018,4),(2018,5),(2018,6),
(2018,7),(2018,8),(2018,9),(2018,10),(2018,11),(2018,12),
(2019,1),(2019,2),(2019,3),(2019,4),(2019,5),(2019,6),
(2019,7),(2019,8),(2019,9),(2019,10),(2019,11),(2019,12),
(2020,1),(2020,2),(2020,3),(2020,4),(2020,5),(2020,6),
(2020,7),(2020,8),(2020,9),(2020,10),(2020,11),(2020,12);
-- current year and month, year of previous month and previous month
select yy,mm,yy-((mm+10)%12+1)/12 as yy_1,(mm+10)%12+1 mm_1 from t;

Related

Tableau filter based on data from month 1 and month 1 + 1

I am trying to count number of ids which meet certain requirements:
over 1k income for month a
below 500 income for month a + 1
how do I achieve this in tableau? I tried using the following calculated filed:
If {fixed [id],month[date]: sum(income)} >=1000 and {fixed [id],month[date]+1: sum(income)} <500 then 1 else 0 end
But not sure whether such month(date) + 1 method worked here.
Just create a new Date field, shifted forward by 1 month:
DATEADD('month',1,[Order Date])
For both dates, right-click Custom-Date --> Month-Year, doing so you can create both Actaul and Shifted date.
Then you only need a calculated field like this in order to count all months with that condition:
sum(if { FIXED [Order Date (Month / Year)] : SUM([Sales])} >= 50000
and { FIXED [Order Date + 1 Month (Month / Year)] : SUM([Sales])} <= 100000
then 1
end
)

Using days of the week for aggregates in DB2

I currently have a query that reads from a table which is written to daily, so I have totals for every day.
What I'm trying to do is modify this query so that I can use days of the week as an aggregate, if that makes sense.
THe totals I get right now are correct, but I want to use Sunday through Saturday as a week and effectively say ``'If today is wednesday, sum totals for weeklyData for Sunday, MOnday and Tuesday from tableOne```
I have this query:
SELECT employee,
sum(case when category = 'Brown' then daily_total else 0 end) as DailyBrownNumbers,
sum(case when category = 'Brown' then weekly_quota else 0 end) as WeeklyBrownNumbers,
CURRENT_DATE as DATE_OF_REPORT
from dailyRecords
where date_of_report >= current_date
group by employee
which reads from that table for the daily records and that's what I need still, but I want to add a sum for daily_total based on those days of the week if possible.
Can this be done with DB2?
You can use dayofweek function. SQL gets all records starting from Sunday including current date. Second column "DailyBrownNumbers" uses case statement to restrict totals to current date records. Third column "WeeklyTotal" has totals for all records from Sunday.
SELECT employee,
sum(case when category = 'Brown' and date_of_report >= current date
then daily_total
else 0 end) as DailyBrownNumbers,
sum(case when category = 'Brown'
then daily_total
else 0 end) as WeeklyTotal,
sum(case when category = 'Brown'
then weekly_quota
else 0 end) as WeeklyBrownNumbers,
CURRENT_DATE as DATE_OF_REPORT
from dailyRecords
where date_of_report >= ( current date - ( dayofweek(current date) - 1 ) days )
group by employee

Find Distinct IDs when the due date is always on the last day of each month

I have to find distinct IDs throughout the whole history of each ID whose due dates are always on the last day of each month.
Suppose I have the following dataset:
ID DUE_DT
1 1/31/2014
1 2/28/2014
1 3/31/2014
1 6/30/2014
2 1/30/2014
2 2/28/2014
3 1/29/2016
3 2/29/2016
I want to write a code in SQL so that it gives me ID = 1 as for this specific ID the due date is always on the last day of each given month.
What would be the easiest way to approach it?
You can do:
select id
from t
group by id
having sum(case when extract(day from due_dt + interval '1 day') = 1 then 1 else 0 end) = count(*);
This uses ANSI/ISO standard functions for date arithmetic. These tend to vary by database, but the idea is the same in all databases -- add one day and see if the day of the month is 1 for all the rows.
If your using SQL Server 2012+ you can use the EOMONTH() function to achieve this:
SELECT DISTINCT ID FROM [table]
WHERE DUE_DT = EOMONTH(DUE_DT)
http://rextester.com/VSPQR78701
The idea is quite simple:
you are on the last day of the month if (the month of due date) is not the same as (the month of due date + 1 day). This covers all cases across year, leap year and so on.
from there on, if (the count of rows for one id) is the same as (the count of rows for this id which are the last day of the month) you have a winner.
I tried to write an example (not tested). You do not specify which DB so I will assume that cte (common table expression) are available. If not just put the cte as subquery.
In the same way, I am not sure that dateadd and interval work the same in all dialect.
with addlastdayofmonth as (
select
id
-- adding a 'virtualcolumn', 1 if last day of month 0 otherwise
, if(month(dateadd(due_date, interval '1' day)) != month(due_date), 1 ,0) as onlastday
from
table
)
select
id
, count(*) - sum(onlastday) as alwayslastday
from
addlastdayofmonth
group by
id
having
-- if count(rows) == count(rows with last day) we have a winner
halwayslastday = 0
MySQL-Version (credits to #Gordon Linoff)
SELECT
ID
FROM
<table>
GROUP BY
ID
HAVING
SUM(IF(day(DUE_DT + interval 1 Day) = 1, 1, 0)) = COUNT(ID);
Original Answer:
SELECT MAX(DUE_DT) FROM <table> WHERE ID = <the desired ID>
or if you want all MAX(DUE_DT) for each unique ID
SELECT ID, MAX(DATE) FROM <table> GROUP BY ID

SQL Server / SSRS: Calculating monthly average based on grouping and historical values

I need to calculate an average based on historical data for a graph in SSRS:
Current Month
Previous Month
2 Months ago
6 Months ago
This query returns the average for each month:
SELECT
avg_val1, month, year
FROM
(SELECT
(sum_val1 / count) as avg_val1, month, year
FROM
(SELECT
SUM(val1) AS sum_val1, SUM(count) AS count, month, year
FROM
(SELECT
COUNT(val1) AS count, SUM(val1) AS val1,
MONTH([SnapshotDate]) AS month,
YEAR([SnapshotDate]) AS year
FROM
[DC].[dbo].[KPI_Values]
WHERE
[SnapshotKey] = 'Some text here'
AND No = '001'
AND Channel = '999'
GROUP BY
[SnapshotDate]) AS sub3
GROUP BY
month, year, count) AS sub2
GROUP BY sum_val1, count, month, year) AS sub1
ORDER BY
year, month ASC
When I add the following WHERE clause I get the average for March (2 months ago):
WHERE month = MONTH(GETDATE())-2
AND year = YEAR(GETDATE())
Now the problem is when I want to retrieve data from 6 months ago; MONTH(GETDATE()) - 6 will output -1 instead of 12. I also have an issue with the fact that the year changes to 2016 and I am a bit unsure of how to implement the logic in my query.
I think I might be going about this wrong... Any suggestions?
Subtract the months from the date using the DATEADD function before you do your comparison. Ex:
WHERE SnapshotDate BETWEEN DATEADD(month, -6, GETDATE()) AND GETDATE()
MONTH(GETDATE()) returns an int so you can go to 0 or negative values. you need a user scalar function managing this, adding 12 when <= 0

Last three months average for each month in PostgreSQL query

I'm trying to build a query in Postgresql that will be used for a budget.
I currently have a list of data that is grouped by month.
For each month of the year I need to retrieve the average monthly sales from the previous three months. For example, in January I would need the average monthly sales from October through December of the previous year. So the result will be something like:
1 12345.67
2 54321.56
3 242412.45
This is grouped by month number.
Here is a snippet of code from my query that will get me the current month's sales:
LEFT JOIN (SELECT SUM((sti.cost + sti.freight) * sti.case_qty * sti.release_qty)
AS trsf_cost,
DATE_PART('month', st.invoice_dt) as month
FROM stransitem sti,
stocktrans st
WHERE sti.invoice_no = st.invoice_no
AND st.invoice_dt >= date_trunc('year', current_date)
AND st.location_cd = 'SLC'
AND st.order_st != 'DEL'
GROUP BY month) as trsf_cogs ON trsf_cogs.month = totals.month
I need another join that will get me the same thing, only averaged from the previous 3 months, but I'm not sure how.
This will ALWAYS be a January-December (1-12) list, starting with January and ending with December.
This is a classic problem for a window function. Here is how to solve this:
SELECT month_nr
,(COALESCE(m1, 0)
+ COALESCE(m2, 0)
+ COALESCE(m3, 0))
/
NULLIF ( CASE WHEN m1 IS NULL THEN 0 ELSE 1 END
+ CASE WHEN m2 IS NULL THEN 0 ELSE 1 END
+ CASE WHEN m3 IS NULL THEN 0 ELSE 1 END, 0) AS avg_prev_3_months
-- or divide by 3 if 3 previous months are guaranteed or you don't care
FROM (
SELECT date_part('month', month) as month_nr
,lag(trsf_cost, 1) OVER w AS m1
,lag(trsf_cost, 2) OVER w AS m2
,lag(trsf_cost, 3) OVER w AS m3
FROM (
SELECT date_part( 'month', month) as trsf_cost -- some dummy nr. for demo
,month
FROM generate_series('2010-01-01 0:0'::timestamp
,'2012-01-01 0:0'::timestamp, '1 month') month
) x
WINDOW w AS (ORDER BY month)
) y;
This is requires that no month is ever missing! Else, have a look at this related answer:
How to compare the current row with next and previous row in PostgreSQL?
Calculates correct average for every month. If only two previous moths then devide by 2, etc. If no prev. months, result is NULL.
In your subquery, use
date_trunc('month', st.invoice_dt)::date AS month
instead of
DATE_PART('month', st.invoice_dt) as month
so you can sort months over the years easily!
More info
Window function lag()
date_trunc()