MSSQL order by previous 7 days - sql-server

I run this query in MSSQL to get the items, grouping by the last 7 days of the week:
SELECT COUNT(Date_Entered), DATENAME(WEEKDAY, Date_Entered)
FROM my_table
WHERE Board_Name = 'Board'
AND DATEDIFF(DAY,Date_Entered,GETDATE()) <= 7
GROUP BY DATENAME(WEEKDAY, Date_Entered)
In the result, days of the week are sorted in alphabetical order: Friday > Monday > Saturday > Sunday > Thursday > Tuesday > Wednesday
How do I sort by the normal/correct/common sense order, starting with the weekday of 7 days ago and ending with yesterday?

Ordering by MAX(Date_Entered) should work too:
SELECT
COUNT(Date_Entered),
DATENAME(WEEKDAY, Date_Entered)
FROM my_table
WHERE Board_Name = 'Board' AND DATEDIFF(DAY,Date_Entered,GETDATE()) <= 7
GROUP BY DATENAME(WEEKDAY, Date_Entered)
ORDER BY MAX(Date_Entered);
Normally you would want to order by the date ascending, but since you use an aggregate function you would need to group by the date which would ruin it, but since the max(date) in every group is the date you can do max(date) to order.

DATEPART is your friend, try it like this:
SELECT COUNT(Date_Entered), DATENAME(WEEKDAY, Date_Entered),DATEPART(WEEKDAY,Date_Entered)
FROM my_table
WHERE Board_Name = 'Board'
AND DATEDIFF(DAY,Date_Entered,GETDATE()) <= 7
GROUP BY DATEPART(WEEKDAY,Date_Entered),DATENAME(WEEKDAY, Date_Entered)
ORDER BY DATEPART(WEEKDAY,Date_Entered)

If you can't count on data being available for every week then you'd need to do something more based on date calculations. Off the top of my head I think this will be more reliable:
ORDER BY (DATEDIFF(dd, MAX(Date_Entered), CURRENT_TIMESTAMP) + 77777) % 7
EDIT: I wrote that not realizing that the data was already limited to a single week. I thought the intention was to group in buckets by day of week for a longer range of dates.
I'll also comment that to me it is more natural to do the grouping on cast(Date_Entered as date) rather than on a string value and I wouldn't be surprised if it's a more efficient query.

Related

How to find the past 4 weeks of the same weekday value starting today

I am trying to select records from today and the same day of each week for the last 4 weeks.
Today (Tuesday)
Last Tuesday
The Tuesday before that
The Tuesday before that
I need this to be tied to current date because I am going to run this query every day so I don't want to use a between or something where I manually specify the date range.
Everything I have found or tried so far has pulled the last month of data but not the last 4 weeks of the same weekday.
select *
from table
where thedatecolumn >= DATEADD(mm, -1, GETDATE())
This works but pulls everything from the last month.
If today's date is 7/10/2019 I need
Data from 7/10/2019
Data from 7/3/2019
Data from 6/26/2019
Data from 6/19/2019
Every day I will run this query, so I need it to be dynamic based on the current date.
I believe you want to look back 21 days and then filter those dates that have the same day of week:
select * from table
where thedatecolumn >= DATEADD(DAY, -21, CAST(GETDATE() AS DATE))
and DATEPART(WEEKDAY, thedatecolumn) = DATEPART(WEEKDAY, GETDATE())
You Can try using a recursive cte which starts today and repeatedly substracts 7 days - so you ensure you always land on the same weekday. Following an example:
WITH cteFromToday AS(
SELECT 0 AS WeeksBack, GETDATE() AS MyDate
UNION ALL
SELECT WeeksBack + 1 AS WeeksBack, DATEADD(d, -7, MyDate) AS MyDate
FROM cteFromToday
)
SELECT TOP 5 *
FROM cteFromToday
OPTION ( MaxRecursion 0 );
This is quite simple. Substitute CURRENT_TIMESTAMP here for any given date.
SELECT CONVERT(DATE,CURRENT_TIMESTAMP) AS Today,
DATEADD(DAY,-7,CONVERT(DATE,CURRENT_TIMESTAMP)) AS LastWeek ,
DATEADD(DAY,-14,CONVERT(DATE,CURRENT_TIMESTAMP)) AS TwoWeeksAgo,
DATEADD(DAY,-21,CONVERT(DATE,CURRENT_TIMESTAMP)) AS ThreeWeeksAgo
SO, if you want to get data for a set of ranges for one entire day with those dates:
SELECT something
WHERE
datetimecolumn >= CONVERT(DATE,CURRENT_TIMESTAMP) AND datetimecolumn < DATEADD(DAY,1, CONVERT(DATE,CURRENT_TIMESTAMP)) -- Todays range,
OR datetimecolumn >= DATEADD(DAY,-7,CONVERT(DATE,CURRENT_TIMESTAMP)) AND datetimecolumn < DATEADD(DAY,1,DATEADD(DAY,-7,CONVERT(DATE,CURRENT_TIMESTAMP)))-- LastWeek ,
OR datetimecolumn >= DATEADD(DAY,-14,CONVERT(DATE,CURRENT_TIMESTAMP)) AND datetimecolumn < DATEADD(DAY,1,DATEADD(DAY,-14,CONVERT(DATE,CURRENT_TIMESTAMP)))-- TwoWeeksAgo,
OR datetimecolumn >= DATEADD(DAY,-21,CONVERT(DATE,CURRENT_TIMESTAMP)) AND datetimecolumn < DATEADD(DAY,1, DATEADD(DAY,-21,CONVERT(DATE,CURRENT_TIMESTAMP))) -- ThreeWeeksAgo

SQL Server Parameter Date Range Issues

I am using the following query to get the difference between two dates. The date ranges are tolling 12 months interval.
CY stand for Current year while PY stands for Previous Year. The dates in Current year are used to calculate the previous year dates
When I execute my query I have the following output, where the month is 11 and day 364. But I want my months to be twelve and the day 365 or (366 for leap year).
DECLARE #CY_StartDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-13, 0) AS DATE),
#CY_EndDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1) AS DATE); --- Rolling 12 months
DECLARE #PY_startDate date =DATEADD(YEAR,-1,#CY_StartDate),
#PY_EndDate date =DATEADD(YEAR,-1,#CY_EndDate)
SELECT
#CY_StartDate AS CY_Start,
#CY_EndDate AS CY_End,
#PY_StartDate AS PY_Start,
#PY_EndDate AS PY_End,
DATEDIFF(year, #CY_StartDate, #CY_EndDate) AS yr,
DATEDIFF(month, #CY_StartDate, #CY_EndDate) AS month,
DATEDIFF(day, #CY_StartDate, #CY_EndDate) AS day
Current Output
CY_Start CY_End PY_Start PY_End yr month day
2017-10-01 2018-09-30 2016-10-01 2017-09-30 1 11 364
Expected output
CY_Start CY_End PY_Start PY_End yr month day
2017-10-01 2018-09-30 2016-10-01 2017-09-30 1 12 365
The values you are getting make sense. DATEDIFF counts the ticks between 2 dates, where a tick is the value of the first parameter. So, for example: DATEDIFF(MONTH, '20180101','20180228') will return 1, as only 1 tick has occured (2 - 1 = 1). Seems, here, you simply need to add 1:
DECLARE #CY_StartDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-13, 0) AS DATE),
#CY_EndDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1) AS DATE); --- Rolling 12 months
DECLARE #PY_startDate date =DATEADD(YEAR,-1,#CY_StartDate),
#PY_EndDate date =DATEADD(YEAR,-1,#CY_EndDate)
select
#CY_StartDate as CY_Start,
#CY_EndDate AS CY_End,
#PY_StartDate AS PY_Start,
#PY_EndDate AS PY_End,
DATEDIFF(year,#CY_StartDate,DATEADD(DAY,1,#CY_EndDate)) as yr,
DATEDIFF(month,#CY_StartDate,DATEADD(DAY,1,#CY_EndDate)) as month,
DATEDIFF(day,#CY_StartDate,DATEADD(DAY,1,#CY_EndDate)) as day
The reason I used a further DATEADD is because this makes it consistent with every expression. The value of yr was correct, however, for dates like 20170101 and 20171231, the value of yr would be 0. Hence adding 1 day the the value of #CY_EndDate makes this far more reliable, should the dates move.
Common sense. How many numbers are there between 1 and 10 including both? You might say that there are 10 - 1 = 9 which is incorrect. The correct answer is (10 - 1) + 1 = 10.
Likewise, if you have two inclusive dates e.g. 2017-10-01 and 2018-09-30 you add one to DATEDIFF(DAY, '2017-10-01', '2018-09-30') to get 365 instead of 364.
However, as suggested in the other answer, it is much better to the end date exclusive (not counted) which makes date calculations straight forward. In your example, you should add 1 day to the last date so that you have [2017-10-01, 2018-10-01) and DATEDIFF will produce desired results.

SET DATEFIST = 1 not working correctly

I'm having trouble to make SET DATEFIRST to work properly on some simple queries I working on at the moment.
Here is my first example:
SET DATEFIRST = 1
SELECT count(Distinct ID)
FROM Products
WHERE
Location in (12)
and YEAR (CREATED) = '2018'
Group by datepart(wk, created), year(created)
The above code gives me some results correct and some others wrong. It's basically counting from Monday to Monday, but I want it to count from Monday to Sunday. I still don't understand why it's counting 8 days instead of 7 days on some of the weeks.
Also I have multiple selections with SET DATEFIRST = 1 which also doesn't work:
SET DATEFIRST = 1
--Products finished this week
SELECT count(Distinct ID)
FROM Products
WHERE
Created >= dateadd(day, 1-datepart(dw, getdate()), CONVERT(date,getdate()))
AND Created < dateadd(day, 8-datepart(dw, getdate()), CONVERT(date,getdate()))
AND Location in (15,16,17) AND (Location IS NOT NULL OR Location NOT IN(18))
UNION ALL
--------------------------------------------
-- Products received this week
SELECT count(Distinct ID)
FROM Products
WHERE
Created >= dateadd(day, 1-datepart(dw, getdate()), CONVERT(date,getdate()))
AND Created < DATEADD(DAY,0,DATEDIFF(DAY,0,dateadd(day, 8-datepart(dw, getdate()),getdate())))
AND Location in(1)
The above code is not reacting to SET DATEFIRST = 1
It's not counting from Monday to sunday, instead it's counting from Sunday to sunday (8 days)
Here is your issue:
DATEADD(day, 8-datepart(DW, GETDATE()), CONVERT(DATE,GETDATE())))
You are comparing a DATETIME from the database with a TIME to a DATE with no TIME. So the DATE portions on next Monday will be equal and your < will fail.
This will not work because the DB field is a DATETIME and you are comparing to a DATE.
SET #D= '06/25/2018 01:20:23 AM' --Monday next week with a time
IF(#D < DATEADD(day, 8-datepart(DW, GETDATE()), CONVERT(GETDATE()))) --<--The whole day
SELECT 1 -- '06/25/2018 12:00:23 AM' < (DATE)2018-06-25
ELSE
SELECT 2
Returns 2
If you do want to include everything through Sunday then you need to set the cutoff to 12:00:00 AM Monday morning. One way to do that would be something like:
DATEADD(DAY,0,DATEDIFF(DAY,0,dateadd(day, 8-datepart(dw, getdate()),getdate())))

Decision based on what day of the week a date falls on

For any given date I need to determine what day of the week it is.
I already know how to get the day of the week with this DATENAME(dw,MyDate.Field)and the number of the day with this DATEPART(dw,MyDate.Field).
Once I've got the day of the week if it is before Wednesday I want to return that Wednesday's date.
If the day of the week is Wednesday or after then I want to return next Wednesdays date.
Monday is day 1 in my system.
Use DATEPART() to determine day of the week.
Use CASE() for the different cases.
Calculate the day delta. Hint: It's either 3-dw or 7+3-dw.
Use DATEADD() to get from the current day (returned by DATEPART) to Wednesday or Wednesday of next week.
the easiest way, without any calculating:
set datefirst 1;
with dates as (
select CAST('20170906' as datetime) d
union all
select DATEADD(day, 1, dates.d)
from dates
where DATEADD(day, 1, dates.d) <= '20170930'
)
select dates.d, DATEADD(day, v.valueToAdd, dates.d) nextWed
from dates
join (values(1,2),(2,1),(3,7),(4,6),(5,5),(6,4),(7,3))v(dayOfWeek,valueToAdd) on v.dayOfWeek = DATEPART(weekday, dates.d)
order by 1

MSSQL Date Range Report - Count of days per month

I have a table of instances that have a Start Date and an End Date column. Here is a simple example:
ID StartDate EndDate
1 1/8/2015 1/10/2015
2 1/8/2015 1/15/2015
3 2/6/2015 3/2/2015
4 1/6/2015 2/20/2015
5 3/18/2015 4/2/2015
I'm trying to write a query to find out how many unique days occur for a given month, but some of the instances overlap and span multiple months which is making it difficult. The results I want would look something like this:
Month # of days
January 26 (earliest is ID 4 starting 1/6)
February 28 (entire month because of ID 3 and 4)
March 16 (2 days from ID 3, 14 days from ID 5)
April 2 (first 2 days of the month from ID 5)
May 0
Any help would be greatly appreciated. Thanks!!
J,
Please check my SQL script below.
Before you run the script you will realize that I've used a SQL Dates table actually a SQL function which returns a temporary dates table.
You can find the source codes at given tutorial
I also used multiple CTE queries
;with dates as (
select
cast(date as date) date
from [dbo].[DateTable]('1/1/2015','12/31/2015')
), cte as (
select
distinct date
from instances, dates
where dates.date between instances.startdate and instances.enddate
)
select
year(date) year, month(date) month, count(*) dayscount
from cte
group by year(date), month(date)
By the way the March returns 16 days, 2 from one and 14 from other.
I hope the Select statement is useful,
The problem is too complicated;
In my opinion, you need to write a function that counts the number of "unique" days of ranges mentioned in the records.
I didn't write the function, but the design of this new function, "num", is like this:
1- It should get the month and year (named aMonth and aYear).
2- It Finds all records that have at least a day in aYear/aMonth:
(month(startDate)=aMonth and year(startDate)=aYear)
or
(month(endDate)=aMonth and year(endDate)=aYear)
or
(
((month(startDate)<aMonth and year(startDate)=aYear) or (year(startDate)<aYear))
and
((month(endDate)>aMonth and year(endDate)=aYear) or (year(endDate)>aYear))
)
3- Over these records, it should open a cursor, and process the records one by one.
4- While processing each records of the cursor, you can count the days of the month and store them in an array (or 28-31 character string of 0/1, for example).
5- count the number of 1's of this array (or string) and return it.
Having written this function ("num"), The high level of the answer will be like this:
Select 'January', dbo.num(1, 2015) as days
union all
Select 'February', dbo.num(2, 2015) as days
union all
Select 'March', dbo.num(3, 2015) as days
union all
Select 'April', dbo.num(4, 2015) as days
union all
Select 'May', dbo.num(5, 2015) as days
union all
Select 'June', dbo.num(6, 2015) as days
union all
Select 'July', dbo.num(7, 2015) as days
union all
Select 'August', dbo.num(8, 2015) as days
union all
Select 'September', dbo.num(9, 2015) as days
union all
Select 'October', dbo.num(10, 2015) as days
union all
Select 'November', dbo.num(11, 2015) as days
union all
Select 'December', dbo.num(12, 2015) as days
If you count the days for the same year only, you can try this. I only build the code for two months but it's easy to extend it.
SELECT
(SELECT SUM(CASE WHEN sdate>='2015-2-1' OR edate<'2015-1-1' THEN 0
WHEN edate>='2015-2-1' THEN datediff(day, sdate, '2015-2-1')
ELSE datediff(day,sdate,edate) END)
FROM a1)
AS Jan_Days,
(SELECT SUM(CASE WHEN sdate>='2015-3-1' OR edate<'2015-2-1' THEN 0
WHEN edate>='2015-3-1' THEN datediff(day, sdate, '2015-3-1')
ELSE datediff(day,sdate,edate) END)
from a1 )
AS Feb_Days,
...
It's far from efficient. It will be more efficient to use a script or stored procedure running through your records and calculate the results.

Resources