Datepart into previous year based on week - sql

I am learning SQL at the moment and needed a query that would return the previous 12 weeks (excluding the current week) and the below worked just fine - that was until we moved in to 2019!
My table has 4 columns, BuildWeek, BuildYear, Info1, Info2, all are int.
select *
from Dashboard
where BuildWeek in (datepart(week, getdate()) - 1,
datepart(week, getdate()) - 2,
datepart(week, getdate()) - 3,
datepart(week, getdate()) - 4,
datepart(week, getdate()) - 5,
datepart(week, getdate()) - 6,
datepart(week, getdate()) - 7,
datepart(week, getdate()) - 8,
datepart(week, getdate()) - 9,
datepart(week, getdate()) - 10,
datepart(week, getdate()) - 11,
datepart(week, getdate()) - 12,
datepart(week, getdate()) - 13)
and BuildYear = datepart(year, getdate())
order by
BuildWeek desc
I know this is not the cleanest query so I'm quite open to being educated, I have tried a few things (using dateadd to no avail) but cannot seem to get it to function how I wish. I'm guessing the above stems from the query perhaps looking into a minus (current date -1 would be 0, -2 would be -1 so no results would be found?) but I'm not sure how to make it look backwards to return those extra weeks.

Another solution would be to generate a date from BuildWeek and BuildYear (say, the first day of the week), that can be used in the WHERE clause.
The advantage with this approach is that is guaranteed to return the records of the last 12 weeks, even if there is not exactly 12 of them (for example if you have gaps in the weeks series), or if future records exist.
SELECT d.*
FROM Dashboard d
WHERE
DATEADD( wk, DATEDIFF( wk, 7, CAST( d.BuildYear AS NVARCHAR(100) ) ) + (d.BuildWeek-1) , 7 )
BETWEEN DATEADD( week, -12, GETDATE() ) AND GETDATE()
ORDER BY
d.BuildYear DESC,
d.BuildWeek DESC
Tested in this db fiddle.

Assuming you have one row per week:
select top (12) d.*
from Dashboard d
order by d.year desc, d.BuildWeek desc;
To avoid future weeks:
select top (12) d.*
from Dashboard d
where year < year(getdate()) or
(year = year(getdate()) and buildweek <= datepart(week, getdate())
order by d.year desc, d.BuildWeek desc;
Or, if you want to use a where and years have 52 weeks:
select d.*
from dashboard d
where (y.year * 52 + buildweek) >= year(getdate()) * 52 + datepart(week, getdate());
This method becomes a bit trickier if years can have 53 weeks.

Related

Getting the week number as alias in sql analysis

this is my first question here. Hopefully I´m clear enough what I´m searching for.
My problem is following:
On this analysis I want to get from the last 7 weeks, the summarized prices of each week. Its working with out any problems, but now I would like to add the weeks number of each week as alias.
In my tests I was using for example something like this:
DECLARE #week7 varchar(10)
SET #week7 = DATEPART(wk, GetDate())
One of my problems is, that I´m not allowed to work with "EXEC".
This is just an example of my analysis:
SELECT DISTINCT(
SELECT SUM(Price)
FROM tblBookingdata
WHERE(Datum BETWEEN DATEADD(wk, -7, DATEADD(DAY, 1 - DATEPART(WEEKDAY, GETDATE()), DATEDIFF(dd, 0, GETDATE()))) AND DATEADD(wk, -6, DATEADD(DAY, 1 - DATEPART(WEEKDAY, GETDATE()), DATEDIFF(dd, 0, GETDATE()))))) AS '7 weeks ago', (
SELECT SUM(Price)
FROM tblBookingdata
WHERE(Datum BETWEEN DATEADD(wk, -6, DATEADD(DAY, 1 - DATEPART(WEEKDAY, GETDATE()), DATEDIFF(dd, 0, GETDATE()))) AND DATEADD(wk, -5, DATEADD(DAY, 1 - DATEPART(WEEKDAY, GETDATE()), DATEDIFF(dd, 0, GETDATE()))))) AS '6 weeks ago'
I would like the column name to show the week number from each sub select. That the output would be for example for this week: 40 (as column name) and 900 as price summary.
So I tried to work here with DECLARE and assign #week7 for example with the current week number. But here I got stuck, due it seems like I need to work here with EXEC.
Is this only possible with "EXEC" or are there any other solutions to solve this? I was looking in the www, but currently I´m stucking a bit. Thankful for every help! :)
I think the DateDiff function is your friend here. Are you using SQL Server? This won't display a row for the week if there are zero records in that week, but this should be close to what you want.
select WeeksAgo, sum(Price) as Price from (
select
Price
,Datediff(wk, Datum, getDate()) as WeeksAgo
,Datum --not used
from
tblBookingdata
)DataByWeek
where WeeksAgo between 0 and 7 --should this be 0-6?
group by WeeksAgo
I think you're looking for something like this. The prior 7 weeks are calculated from GETDATE based on a numbers table with 1, 2, 3, ... 7. Then the booking Prices are summarized by week where the Datum is within the prior 7 weeks. This will display NULL in price_sum if there were no sales that week.
drop table if exists #tblBookingdata;
go
create table #tblBookingdata(
Datum date not null,
Price int not null);
go
;with
weeks_cte(wk) as (
select datepart(wk, dateadd(wk, w*-1, getdate()))
from (values (1),(2),(3),(4),(5),(6),(7)) v(w)),
bookings_cte(wk, price_sum) as (
select datepart(wk, Datum), sum(Price)
from #tblBookingdata
where Datum>dateadd(wk, -7, getdate())
group by datepart(wk, Datum))
select *
from weeks_cte wc
left join bookings_cte b on wc.wk=b.wk;

Get records of last 2 months (current year), and last month(last year)

I need to get records of last 2 months and last month(last year) based on my table field paidDate, using SQL server 2016.
Suppose, I run the query on Feb 1st/2nd, 2020. I need the monthly data from December 2019, January 2020, as well as January 2019.
What's the SQL query for this? Is it possible to club all of these scenario into one?
Then for the previous 2 months the paidDate would be :
A) Higher or equal than the first day of 2 months ago
B) Lower than the first day of the current month.
Similar for the month of a year ago.
So try something like this:
SELECT *
FROM YourTable
WHERE
(
paidDate >= DATEADD(month, -2, DATEADD(month, DATEDIFF(month, 0, GetDate()), 0))
AND paidDate < DATEADD(month, DATEDIFF(month, 0, GetDate()), 0)
)
OR
(
paidDate >= DATEADD(month, -13, DATEADD(month, DATEDIFF(month, 0, GetDate()), 0))
AND paidDate < DATEADD(year, -1, DATEADD(month, DATEDIFF(month, 0, GetDate()), 0))
)
LukStorm has the better answer in terms of performance (and I've upvoted it). But if you want complete months and don't care about indexing, then I would suggest datediff():
where datediff(month, paiddate, getdate()) in (1, 2, 13)
This gets the complete months that are 1 month, 2 months, and 13 months in the past.
You can try the logic as below-
SELECT *
FROM your_table
WHERE
(
YEAR(paidDate) = YEAR(DATEADD(MM,-1, getdate()))
AND
MONTH(paidDate) = MONTH(DATEADD(MM,-1, getdate()))
)
OR
(
YEAR(paidDate) = YEAR(DATEADD(MM,-2, getdate()))
AND
MONTH(paidDate) = MONTH(DATEADD(MM,-2, getdate()))
)
OR
(
YEAR(paidDate) = YEAR(DATEADD(MM,-13, getdate()))
AND
MONTH(paidDate) = MONTH(DATEADD(MM,-13, getdate()))
)

How can I select the past seven days and its corresponding week in the past year

How can I select
The past week
Its corresponding days in the year before
This is needed for a dashboard, I would like to show a chart with results from the past seven days. It displays green if our call-center handles 98% of their phone calls within a certain time-span, red if we go over 98%. As a reference I would like to create a chart below with the corresponding seven days in the year before. This is challenging, because weekdays really influence the workload. That means I can't compare a Tuesday with a Sunday or Monday.
For instance, today is Saturday 21st Dec 2019, I would like to report the following timespans:
2019-12-13 00:00:00 -> 2019-12-20 23:59:59
and
2018-12-14 00:00:00 -> 2018-12-21 23:59:59
I made the following code (used within a select statement):
case when cs.ReachedAt between (getdate() - 7) and getdate() then 1 else 0 end as Is_PastWeek
case when cs.ReachedAt between (convert(datetime, convert(varchar(50), convert(date, dateadd(d, -1, dateadd(wk, -52, getdate())))) + ' 23:59:59')) and (convert(datetime, convert(varchar(50), convert(date, dateadd(d, -8, dateadd(wk, -52, getdate())))) + ' 00:00:00')) then 1 else 0 end as Is_SameWeekLastYear
It works, but isn't perfect. I just select the corresponding weekday in the same week as 52 weeks ago. Which means I sometimes end up selecting a matching weekday, but not the nearest. How can I do this better?
EDIT
To clarify what I mean by "picking the nearest corresponding weekday in the year before", i made the following example:
with cte1 as (
select row_number() over (order by (select 1)) - 1 as incrementor
from master.sys.columns sc1
cross join master.sys.columns sc2
), cte2 as (
select dateadd(day, cte1.incrementor, '2000-01-01') as generated_date
from cte1
where dateadd(day, cte1.incrementor, '2000-01-01') < getdate()
), cte3 as (
select convert(date, generated_date) as generated_date
, convert(date, getdate()) as now_date
from cte2
), cte4 as (
select *
, convert(date, dateadd(YEAR, -1, now_date)) as year_back
from cte3
)
select now_date
, generated_date
from cte4
where 1=1
and datepart(week, year_back) = datepart(week, generated_date)
and datepart(DW, year_back) = datepart(DW, generated_date)
This will result in:
For the grey values, I would rather take the weekday of one week later. That way I pick "the nearest corresponding weekday in the year before".
Please note that the above is an example to show what I mean, my ultimate goal is to start with this date, select the whole week before... And all (if possible) neatly within a where clause.
The expression datepart(week, getdate()) will deliver you the calendar week. With this, you can go further.
This is too long for a comment.
What difference does it make? If you are looking for the past week, just look at the same 7 days from the previous year. In one case the week might start on a Tuesday and in the other on a Wednesday. But in both cases, each weekday occurs once.
The logic would be:
where cs.ReachedAt >= datefromparts(year(getdate() - 7) - 1, month(getdate() - 7), day(getdate() - 7) and
cs.ReachedAt < datefromparts(year(getdate()), month(getdate()), day(getdate()))
The logic for the current year:
where cs.ReachedAt >= convert(date, getdate() - 7) and
cs.ReachedAt < convert(date, getdate())

Roll weekend counts into monday counts

I have a query like this:
select date, count(*)
from inflow
where date >= dateadd(year, -2, getdate())
group by date
order by date
I need to exclude Saturday and Sunday dates, and instead add their counts into the following Monday. What would be the best way to do this? Do I need to exclude Saturday, Sunday, and Mondays, then add them on with a join to a different query? The query above is a simplified version, this is a relatively big query, so I need to keep efficiency in mind.
Well, this is a somewhat brute-force approach:
select date,
(case when datename(weekday, date) = 'Monday')
then cnt + cnt1 + cnt2
else cnt
end) as cnt
from (select date, count(*) as cnt,
lag(count(*), 1, 0) over (order by date) as prev_cnt,
lag(count(*), 2, 0) over (order by date) as prev_cnt2
from inflow
where date >= dateadd(year, -2, getdate())
group by date
) d
where datename(weekday, date) not in ('Saturday', 'Sunday')
order by date;
Note: This is assuming English-language settings so the datename() logic works.
An alternative method without subqueries;
select v.dte, count(*) as cnt
from inflow i cross apply
(values (case when datename(weekday, i.date) = 'Saturday'
then dateadd(day, 2, i.date)
when datename(weekday, i.date) = 'Sunday'
then dateadd(day, 1, 9.date)
else i.date
end)
) v.dte
where i.date >= dateadd(year, -2, getdate())
group by v.dte
order by date;
You state for performance, however without knowing the full picture it's quite hard to understand how to optimise the query.
While I've been working on this, I noticed Gordon Linoff's answer, however I'll continue to write my version up as well, we both following the same path, but get to the answer a little different.
WITH DateData (date, datetoapply)
AS
(
SELECT
[date],
CASE DATEPART(w, [date])
WHEN 5 THEN DATEADD(d, 2, [date])
WHEN 6 THEN DATEADD(d, 1, [date])
ELSE date
END as 'datetoapply'
FROM inflow
WHERE [date] >= dateadd(year, -2, getdate())
)
SELECT datetoapply, count(*)
FROM DateData
GROUP BY datetoapply
ORDER BY datetoapply
While I could not get Gordon's query working as expected, I can confirm that "DATEPART(w, [date])" performs much better than "DATENAME(weekday, [date])", which if replaced in the query above increases the server processing time from 87ms to 181ms based on a table populated with 10k rows in Azure.

Looking for a getdate/dateadd statement to do 1 year and +/- 15 days

-- This is my current code which will allow for me to see all our work orders that have been submitted within the past week, and lets me know if any of the same work orders have appear 6 months ago.
SELECT
A.tagnumber,
count(*) AS CountTotal
FROM
v_workorder A
WHERE
--Date range Within Today and 6 months ago
wo_requestDate BETWEEN DATEADD(month, -6, GETDATE()) AND GETDATE()
AND
EXISTS
( -- Date range Within Today and 7 days ago
select
tagnumber
FROM
v_workorder
WHERE
wo_requestDate BETWEEN DATEADD(DAY,-7,GETDATE()) AND GETDATE()
)
AND
A.wc_description = 'Corrective'
AND
A.itemtype_name = 'Building'
GROUP BY A.tagnumber
ORDER BY CountTotal DESC
--However, Now I would like for my first variable of the getdate/adddate. To check back 1 year ago, +/- 15 days. So essentially 1 year and 15 days back instead of 6 months.
For past 1 year +/- 15 Days
SELECT A.tagnumber, count(*) AS CountTotal
FROM v_workorder A
WHERE wo_requestDate BETWEEN DATEADD(day, -15, DATEADD(year, -1, GETDATE())) AND DATEADD(day, 15, DATEADD(year, -1, GETDATE()))
AND
EXISTS ( select tagnumber FROM v_workorder WHERE wo_requestDate BETWEEN DATEADD(DAY,-7,GETDATE()) AND GETDATE() )
AND
A.wc_description = 'Corrective' AND A.itemtype_name = 'Building'
GROUP BY A.tagnumber
ORDER BY CountTotal DESC
For 1 year
SELECT ...
FROM ...
WHERE wo_requestDate BETWEEN DATEADD(year, -1, GETDATE()) AND GETDATE()
AND...;
For 15 Days
SELECT ...
FROM ...
WHERE wo_requestDate BETWEEN DATEADD(day, -15, GETDATE()) AND GETDATE()
AND...;
Other possible options of DATEADD()
year
quarter
month
dayofyear
day
week
weekday
hour
minute
second
millisecond
See more here.
.
To eliminate issues with the time component of datetime:
CAST(GETDATE() AS DATE
Find the date from a year ago:
SELECT DATEADD(YEAR, -1, CAST(GETDATE() AS DATE));
From there, subtract 15 days and add 15 days in your end points.
...
WHERE
wo_requestDate >= DATEADD(DAY, -15, DATEADD(YEAR, -1, CAST(GETDATE() AS DATE)))
AND
wo_requestDate < DATEADD(DAY, 15, DATEADD(YEAR, -1, CAST(GETDATE() AS DATE)))
I prefer >= and < to BETWEEN, especially with dates, just to avoid any ambiguity with the time component, so you may want to add 16 days to the last parameter if you want the range to include the 15th day out.