SQL - GROUP BY Month Period Between Two Dates - sql

I have a table(contracts)the rows have a created date col that I would like to GROUP by month, but the end-user wants the month to run from the 26th to 25th.
So 11 (November) would be 2020.10.26 - 2020.11.25.

You can subtract 25 days from the date and extract the month. I would do this using apply. Here is an example:
select datefromparts(year(v.mon), month(v.mon), 1) as month, count(*)
from t cross apply
(values (dateadd(day, -25, datecol))) v(mon)
group by datefromparts(year(v.mon), month(v.mon), 1)
Note: This makes the month October rather than November. If you want it to be November, just add a month:
from t cross apply
(values (dateadd(month, 1, dateadd(day, -25, datecol)))) v(mon)

Related

SQL query to find dates of last year equivalent to today's date

I am writing a SQL query to find business working dates of last year equivalent to today's date.
In this query it should fetch :-
For e.g. if today is 5th January, 2021 and it is the second day of second week of the year. So I need to find the exact equivalent date of the second day of second week of the previous year. So it would be 7th January, 2020.
And with this, I need the business working dates of that week of 7th January 2020 (i.e. excluding Saturday & Sunday)
Which will come up as 2020-Jan-06 to 2020-Jan-10 according to the example.
So I will need the report between 6th Jan - 10th Jan, 2020.
I am trying to use this code to find date of last year equivalent to today's date (5th Jan, 2021 viz. second day of second week)
select Convert(date, (DATEADD(year, -1, getdate()+2))) ;
2021-01-05 is the 2nd day of the first week of 2021 according to ISO standards.
If you want the 2nd day of the first week of 2021, then it is either today's date minus 52 weeks or 53 weeks. Based on the Wikipedia page for ISO dates:
[53 week years are those] years in which 1 January or 31 December are Thursdays
So, we want that for the previous year. Hence, I think the following should work:
select dateadd(week,
(case when 'Thursday' in (datename(weekday, datefromparts(year(getdate()) - 1, 1, 1)),
datename(weekday, datefromparts(year(getdate()) - 1, 12, 31))
)
then -53 else -52
end),
convert(date, getdate())
)
Note that this returns 2019-12-31, which is the correct value based on ISO standards.
I have use multiple CTE to show you the step by step calculation. It should be pretty easy to follow.
Basically it find the week_no and day_no_of_week for 2021-01-05 and then use that to find the same date for 2020
declare #input_date date = '2021-01-05',
#year_offset int = -1; -- previous year
with
cte1 as
(
select input_date = #input_date,
week_no = DATEPART(WEEK, #input_date),
first_day_of_week = DATEADD(WEEK, DATEDIFF(WEEK, 0, #input_date), 0)
),
cte2 as
(
select *,
day_no_of_week = DATEDIFF(DAY, first_day_of_week, #input_date) + 1
from cte1
),
cte3 as
(
select *,
first_day_of_the_prev_year = DATEADD(YEAR, DATEDIFF(YEAR, 0, #input_date) + #year_offset, 0)
from cte2
),
cte4 as
(
select *,
first_day_of_week_prev_year = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(WEEK, week_no - 1, first_day_of_the_prev_year)), 0)
from cte3
)
select *,
DATEADD(DAY, day_no_of_week - 1, first_day_of_week_prev_year) as the_required_date
from cte4

Sum of numbers from previous year month until this years month in SQL

Being a beginner, I'm having a hard time coding this particular scenario in SQL Server 2008
As you can see,
The SUM column for July 2017 for example is equal to the following:
August to Dec 2016 + Jan to July 2017 = 4625
Similarly, the SUM column for August 2017 is equal to the following:
Sep to Dec 2016 + Jan to August 2017 = 4625
How can I automate this from month to month?
I appreciate any help I can get. Trying to code this in SQL Server 2008
Using this methodology to find the first day of the current month:
select dateadd(month, datediff(month, 0, getdate()), 0)
We can expan on it to get the first day, of the next month, a year ago... i.e. 11 months ago.
select dateadd(month, datediff(month, 0, dateadd(month,-11,getdate())), 0)
Then, we just need to use it in a where clause to limit your data...
declare #startDate = (select dateadd(month, datediff(month, 0, dateadd(month,-11,getdate())), 0))
declare #endDate = getdate()
select sum(someColumn)
from someTable
where dateColumn between #startDate and #endDate
Since you didn't provide your actual data set, just some pivoted data, I'm not sure of your column and table names
This sounds like you want window functions. Assuming your data is already summarized by month:
select t.*,
sum(numbers) over (order by yyyymm rows between 11 preceding and current row) as prev_12_sum
from t;
If the data is not already summarized, you can put this in a group by as well:
select year(date), month(date),
sum(sum(numbers)) over (order by year(date), month(date) rows between 11 preceding and current row) as prev_12_sum
from t
group by year(date), month(date)
order by min(date);

DATEADD Calculation

How the calculation happen for MONTH datepart in DATEADD()
Add Month
SELECT '2012-01-29' AS [Date], CAST(DATEADD(MONTH, 1, '2012-01-31') AS DATE) AS NextDate
UNION
SELECT '2012-01-31' AS [Date], CAST(DATEADD(MONTH, 1, '2012-01-31') AS DATE) AS NextDate
UNION
SELECT '2013-01-31' AS [Date], CAST(DATEADD(MONTH, 1, '2013-01-31') AS DATE) AS NextDate
Result
Subtract Month
SELECT '2012-02-29' AS [Date], CAST(DATEADD(MONTH, -1, '2012-02-29') AS DATE) AS PrevDate
UNION
SELECT '2012-03-01' AS [Date], CAST(DATEADD(MONTH, -1, '2012-03-01') AS DATE) AS PrevDate
Result
When I add a Month for the dates 29,30,31 of Jan'2012, I get the same result as February 29. For subtract, for the date 29 Feb'2012, it shows 29 Jan'2012. There is no way to get the dates 30 & 31 of Jan'2012.
I want to know some brief explanation.
The behaviour is explicitly documented in the documentation for DATEADD:
DATEADD (datepart , number , date )
...
If datepart is month and the date month has more days than the return month and the date day does not exist in the return month, the last day of the return month is returned. For example, September has 30 days; therefore, the two following statements return 2006-09-30 00:00:00.000:
SELECT DATEADD(month, 1, '2006-08-30');
SELECT DATEADD(month, 1, '2006-08-31');
As to why it has this behaviour, it all comes down to the fact that variable length months mean that you have to apply some form of tradeoff when performing date maths, and no one "correct" answer exists. Do you think of 31st January as being "the last day of January" or "30 days after the 1st day of January". Both of those are correct ways of thinking about the 31st. But if you change January to February, you now obtain two different dates - 28th or 29th of February for "the last day of February" or 2nd or 3rd of March for "30 days after the 1st day of February".
But functions have to return just one value.
I'm not saying that SQL Server applies either of the above interpretations. What it does do, though, is ensures that if you add, say, 1 month onto a particular date, you can be sure that the resulting date falls in the following month.

How to group daily data on weekly basis using sql

I am trying to group the number of hours that employees worked for the last 4 weeks but I want to group them on a weekly basis. For example:
WEEK HOURS
Feb 24 to March 2 55
March 3 to March 9 40
March 10 to March 16 48
March 17 to March 23 37
This is what I have so far, please help. thanks
SET DATEFIRST 1
SELECT CAST(MIN( [DT]) AS VARCHAR(20))+' TO '+CAST (MAX([DT]) AS VARCHAR(20)) AS DATE,
SUM(HOURS) AS NUM_HRS
FROM MyTable
GROUP BY DATEPART(WEEK,[DT])
HAVING COUNT(DISTINCT[DT])=7
Create a Calendar auxilliary table, with Year, Month, Week, Date columns (you can also add holidays and other interesting stuff to it, it has many potential uses) and populate it for the period of interest.
After that, it's as easy as this:
SELECT sum(hours), cast(min(date) as varchar), cast(max(date) as varchar)
FROM Calendar c
LEFT OUTER JOIN MyTable h on h.Date = c.date
GROUP BY year, week
ORDER BY year, week
SET DATEFIRST 1
SELECT DATEPART(WEEK,DT) AS WEEK,
SUM(HOURS) AS NUM_HRS
FROM MyTable
WHERE DT >= DATEADD(WEEK, -4, GetDate()),
GROUP BY DATEPART(WEEK,[DT])
Try something like
SELECT
DATEADD(DD,
CONVERT(INT, (DATEDIFF(DD, '1/1/1900', t.DT)/7)) * 7,
'1/1/1900') [WeekBeginDate],
DATEADD(DD,
(CONVERT(INT, (DATEDIFF(DD, '1/1/1900', t.DT)/7)) * 7) + 6,
'1/1/1900') [WeekEndDate],
SUM(HOURS) AS NUM_HRS
FROM MyTable t
GROUP BY CONVERT(INT, DATEDIFF(DD, '1/1/1900', t.DT)/7)
Though this is the brute force trick, I think in your case it will work.
EDIT : Modified the query a little bit, the error was caused because of the order in which DATEDIFF calculates the difference.
Also here is a SQL FIDDLE with a working example.
EDIT 2 : Updated the Fiddle with the Date Format. To customize the date format, this article would help.

Number of days in quarter, SQL Server

I want to calculate the number of days per-quarter if start date and finish dates are given.
for example, one table has two columns, start date and finish date.
start date = 1st september and finish is 14th november.
I want to calculate the number of days present in between these two days that are present in each quarter
Q3 - 30 days
Q4 - 45 days (for this scenario)
Regards.
declare #StartDate date='2012-09-01';
declare #EndDate date='2012-11-14';
select CEILING(month(dateadd(q,datediff(q,0,dateadd(dd,number ,#StartDate)),0))/3.0) as QuarterNo,
COUNT(*) as 'number of days'
from master..spt_values
where type='p'
and dateadd(dd,number ,#StartDate)<=#EndDate
group by dateadd(q,datediff(q,0,dateadd(dd,number ,#StartDate)),0)
SQL fiddle demo
You can use a recursive query to get this. This generates the list of dates between your start and end date and then gets the count of days per quarter:
;with cte (start, enddate) as
(
select startdate, enddate
from yourtable
union all
select dateadd(dd, 1, start), enddate
from cte
where dateadd(dd, 1, start) <= enddate
)
select datepart(q, start) Quarter, count(datepart(q, start)) NoDays
from cte
group by datepart(q, start)
See SQL Fiddle with Demo