Get Month from Calendar Week (SQL Server) - sql

I have the following problem:
In Microsoft's SQL Server, I have a table where a year and a calendar week (starting with Monday) is stored.
Now, I want to write a function which returns the month in which this calendar week starts (i.e. if the Monday is the 31th of July, it should return "7").
I haven't found a built-in function to achieve this task and I have no idea to implement this easily. So I hope for your help and ideas!
Thanks in advance,
Melvin

Query
DECLARE #WEEK INT;
DECLARE #YEAR INT;
SET #week = 3
SET #year = 2014
SELECT DATEPART(MM,CAST(CONVERT(CHAR(3),
DATEADD(WW,#WEEK - 1,
CONVERT(datetime,'01/01/'+CONVERT(char(4),#Year)))
,100)+ ' 1900' AS DATETIME)) AS [MONTH]
Result
╔═══════╗
║ MONTH ║
╠═══════╣
║ 1 ║
╚═══════╝

Edited: I had to reread answer and pull from M.Ali to get the full answer. You also have to know the correct year if the first week starts in the previous year. You may need to do some editing if your first week of the year doesn't necessarily include 1 Jan.
DECLARE #Week int, #Year int, #Date datetime;
SET #Week = 1
SET #Year = 2014
SET #Date = CAST(#Year as varchar(4)) + '-01-01'
SELECT #Date = DATEADD(ww, #week-1, #Date)
SELECT MONTH(DATEADD(d, (DATEPART(dw,#date)-2)*-1, #date)),
CASE WHEN #Week = 1 AND MONTH(DATEADD(d, (DATEPART(dw,#date)-2)*-1, #date)) = 12
THEN #YEAR - 1 ELSE #YEAR END as CorrectYear

Related

Getting number of days for a specific month and year between two dates SQL

I want to retrieve the number of days between two dates that overlap a specific month.
For Example :
Month = 1
Year = 2020
StartDate ='2019-11-12'
ENDDate ='2020-1-13'
Result = 13 days
13 because there are 13 days between the dates that are in the selected month: January 2020
Other Example:
Month=9
Year =2019
StartDate = '2019-8-13'
ENDDate ='2020-1-1'
Result = 30 days
30 because there are 30 days between the dates that are in the selected month: September 2019
The generic formula for the number of overlapping days in two ranges is
MAX(MIN(end1, end2) - MAX(start1, start2) + 1, 0)
In your case you have one set of Start and End dates, you must construct the other from the given month and year using datefromparts and eomonth.
Unfortunately SQL Server doesn't support LEAST and GREATEST formulas as do MySQL and Oracle, so this is a bit painful to implement. Here's an example using variables:
declare #month int;
declare #year int;
declare #startDate date;
declare #endDate date;
declare #startOfMonth date;
declare #endOfMonth date;
declare #minEnd date;
declare #maxStart date;
set #month = 1;
set #year = 2020;
set #startDate = '2019-11-12';
set #endDate = '2020-01-13';
set #startOfMonth = datefromparts(#year, #month, 1)
set #endOfMonth = eomonth(#startOfMonth)
set #minEnd = case when #endDate < #endOfMonth then #endDate
else #endOfMonth
end;
set #maxStart = case when #startDate < #startOfMonth then #startOfMonth
else #startDate
end;
select case when datediff(day, #maxStart, #minEnd) + 1 < 0 then 0
else datediff(day, #maxStart, #minEnd) + 1
end as days_in_month
Output:
13
Demo on dbfiddle; this includes other sample date ranges.
You could implement something similar using a series of CTEs if the values are derived from a table.

How to get database values by week

I just want to ask how can I achieve getting the records in a database by week in a month? using sql
What I really mean is when I input 1 it will get the records in 1st week of a month
I've done a lot of research today but it seems I can't find a good solution
Heres the code:
DECLARE #MONTH int
DECLARE #YEAR int
DECLARE #WEEK int
SET #MONTH = 9
SET #YEAR = 2013
SET #WEEK = 1
SELECT RH.RepairOrderNumber FROM dbo.RepairOrderHeader RH
WHERE MONTH(RH.DateReleased) = #MONTH AND YEAR(RH.DateReleased) = #YEAR AND WEEK(RH.DateReleased) = #WEEK
I just want to fetched the records according to month,year, and by week is there any way and precise code on how to do this?
DATEPART(WEEK,) gives you number of a week in an YEAR so you just need to calculate number of week in the month calculating difference between current date week number and the week number of the first day of the month:
WHERE MONTH(RH.DateReleased) = #MONTH
AND YEAR(RH.DateReleased) = #YEAR
AND DATEPART(WEEK,RH.DateReleased)
-DATEPART(WEEK,DATEADD(DAY,-DAY(RH.DateReleased)+1,RH.DateReleased))+1
= #WEEK
WEEK returns the week of the year, starting on a constant day of the week (i.e. each Sunday). You seem to want the week of the month, which I guess would always start on the 1st of each month.
so:
SELECT RH.RepairOrderNumber FROM dbo.RepairOrderHeader RH
WHERE MONTH(RH.DateReleased) = #MONTH AND
YEAR(RH.DateReleased) = #YEAR AND
DAY(RH.DateReleased) / 7 + 1 = #WEEK
Where possible avoid using functions on columns in the WHERE clause. You immediately lose the ability to take advantage of indices on these columns. The below solution gives the same results as the select answer, but performs much better:
DECLARE #MONTH INT = 9
DECLARE #YEAR INT = 2013
DECLARE #WEEK INT = 2
--1ST OF THE MONTH DECLARED
DECLARE #Date DATE = CAST(CAST(#Year AS VARCHAR(4)) +
RIGHT('0' + CAST(#Month AS VARCHAR(2)), 2) + '01' AS DATE)
SET #Date = DATEADD(WEEK, #Week - 1, DATEADD(DAY, 1 - DATEPART(WEEKDAY, #Date), #Date));
SELECT RH.RepairOrderNumber
FROM dbo.RepairOrderHeader RH
WHERE RH.DateReleased >= #Date
AND RH.DateReleased < DATEADD(WEEK, 1, #Date);
All this does is get the first of the month in question, then find the first day of the week that the first of the month falls in. Then adds the declared number of weeks to this date.
With an example table with 6 years of data in and 10 rows for each day (20,480 records, so not a lot) and an index on your date column the execution plans of this and the accepted answer are as follows (Accepted on top, using dates on the bottom):
Testing DDL and queries on SQL-Fiddle
This shows that when you use WEEK(DateColumn) = #Week that the index is not used at all, and the query cost is much higher. Depending on the distribution of data in your table this could make a massive difference, even in the small set of data in my example it is 800% more efficient.
I consider '2013-08-26' - '2013-09-01' the first week in september
DECLARE #MONTH int = 9
DECLARE #YEAR int = 2013
DECLARE #WEEK int = 1
DECLARE #from date= dateadd(day, datediff(day, -(#WEEK - 1)*7,
(dateadd(month, (#year-1900) * 12 + #month - 1 , 0)))/7*7 , 0)
SELECT RH.RepairOrderNumber
FROM dbo.RepairOrderHeader RH
WHERE RH.DateReleased >= #from
and RH.DateReleased < dateadd(week, 1, #from)

Date calculation in variable

I'm doing my best to set a date variable so I can compare it later. I would like something that basically says:
If the current day-of-month is less than 11, then date is 10th of LAST month
If the current day-of-month is greater-than-or-equal-to 11, then date is 10th of THIS month
Date is 11/6/2012 expected output:
#PODate = 10/10/2012
Date is 11/16/2012 expected output:
#PODate = 11/10/2012
Currently all I have is this:
DECLARE #PODate as DATETIME
Set #PODate = Convert(varchar(8),GetDate(),1)
Any tips or help would be greatly appreciated. Thank you!!
Trying to keep it as straightforward as possible:
declare #PODate datetime
select #PODate = DATEADD(month,
DATEDIFF(month,'20010110',CURRENT_TIMESTAMP) +
CASE WHEN DATEPART(day,CURRENT_TIMESTAMP) <= 10 THEN -1 ELSE 0 END,
'20010110')
The surrounding DATEADD/DATEDIFF pair are being used to normalize the date to the 10th of the current month. We then use a small CASE expression to subtract a month if the day is less than or equal to the 10th.
Whatever solution you pick, please try to avoid ones that do it as string manipulation. The usual cause of datetime related bugs in SQL is when people treat dates as strings. Keeping the data types appropriately is usually the best way to prevent these issues.
There are, admittedly, 2 strings in my solution, but these are fixed constant strings (all that matters is that they're both for the same year and month, and the second one is for the 10th of the month) and are in an unambiguous format.
Try this: SQL Fiddle
DECLARE
#PODate as DATETIME,
#LastMonth as DateTime,
#strDate as Varchar(50)
set #PODate = '11/16/2012'
set #LastMonth = DATEADD(MONTH, -1, #PODate)
if(DAY(#PODate) < 11)
SET #strDate = CAST(MONTH(#LastMonth) AS VARCHAR)+'/10/'+CAST(YEAR(#LastMonth) AS VARCHAR)
else
SET #strDate = CAST(MONTH(#PODate) AS VARCHAR)+'/10/'+CAST(YEAR(#PODate) AS VARCHAR)
Select CAST(#strDate AS DateTime)
DECLARE #PODate date = '20121116'
SELECT CASE WHEN DATEPART(day, #PODate) < 11 THEN DATEADD(mm, DATEPART(mm, GETDATE()) - DATEPART(mm, #PODate) - 1, DATEADD(day, 10 - DATEPART(day, #PODate), #PODate))
ELSE DATEADD(mm, DATEPART(mm, GETDATE()) - DATEPART(mm, #PODate), DATEADD(day, 10 - DATEPART(day, #PODate), #PODate)) END
Demo on SQLFiddle
DECLARE #currDate DATE = dbo.GetDate()
DECLARE #day INT = day(#currDate)
DECLARE #month INT
DECLARE #year INT
DECLARE #PODate DATE
IF( #day >= 11)
BEGIN
SET #month = month(#currDate)
SET #year = year(#currDate)
END
ELSE BEGIN
SET #month = month(dateadd(m,-1,#currDate))
SET #year = year(dateadd(m,-1,#currDate))
END
SET #PODate = convert(DATE,'10-' + convert(VARCHAR,#month) + '-' + convert(VARCHAR,#year))
PRINT #PODate
if #currDate = '11-jan-2013' , #PODate will be '10-jan-2013', and if #currDate = '07-jan-2013' , #PODate will be '10-Dec-2012'

T-SQL - Get start and end date based on week number.

Our business considers a week from (Monday - Sunday). I need to write a T-SQL function, which passes in year, week no as parameters and it will return the start and end date of that week. However I've seen many examples but the problem lies within the year overlapping.
e.g December 26, 2011 (Monday) - January 01, 2012 (Sunday)... << Would want to consider this as the last week of 2011.
And also in T-SQL the datepart(ww,DATE) considers Sunday as the start of the week??
Or Am I better creating my own table with the week no and storing its start and end date?
DECLARE
#Year INT,
#Week INT,
#FirstDayOfYear DATETIME,
#FirstMondayOfYear DATETIME,
#StartDate DATETIME,
#EndDate DATETIME
SET #Year = 2011
SET #Week = 52
-- Get the first day of the provided year.
SET #FirstDayOfYear = CAST('1/1/' + CAST(#YEAR AS VARCHAR) AS DATETIME)
-- Get the first monday of the year, then add the number of weeks.
SET #FirstMondayOfYear = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, #FirstDayOfYear), #FirstDayOfYear)), 0)
SET #StartDate = DATEADD(WEEK, #Week - 1, #FirstMondayOfYear)
-- Set the end date to one week past the start date.
SET #EndDate = DATEADD(WEEK, 1, #StartDate)
SELECT #StartDate AS StartDate, DATEADD(SECOND, -1, #EndDate) AS EndDate
You should create a table with the holidays and days you don´t wnat to consider in your counts.
After this count the days between the initial date and final date. (Step1)
Select in your table to check how many days are between your ini and final dates. (Step2)
Finally subtract the result of step 2 with result of step 1;
This is the sort of thing where you're better off creating a calendar table: the main issue is knowing how far into the past/future to populate it, but beyond that, have the table schema include week number and date. Depending on what other date-based queries you want to do, you might want to have additional column to break the date down into its constituant parts (e.g., have three separate columns for day/month/year/name of day, etc).
How about this one:
--DROP FUNCTION dbo.GetBusinessWeekStart
CREATE FUNCTION dbo.GetBusinessWeekStart(
#Year SMALLINT,
#Week TINYINT
)
RETURNS DATETIME
AS
BEGIN
DECLARE #FirstMonday TINYINT
DECLARE #Result DATETIME
IF ISNULL(#Week,0)<1 OR ISNULL(#Year,0)<1900
BEGIN
SET #Result= NULL;
END
ELSE
BEGIN
SET #FirstMonday=1
WHILE DATEPART(dw,CONVERT(DATETIME, '01/0' + CONVERT(VARCHAR,#FirstMonday) + '/' + CONVERT(VARCHAR,#Year)))<>2
BEGIN
SET #FirstMonday=#FirstMonday+1
END
SET #Result=CONVERT(DATETIME, '01/0' + CONVERT(VARCHAR,#FirstMonday) + '/' + CONVERT(VARCHAR,#Year))
SET #Result=DATEADD(d,(#Week-1)*7,#Result)
IF DATEPART(yyyy,#Result)<>#Year
BEGIN
SET #Result= NULL;
END
END
RETURN #Result
END
GO
--Example
SELECT dbo.GetBusinessWeekStart(2011,15) [Start],dbo.GetBusinessWeekStart(2011,15)+6 [End]

Playing with Date Time in SQL Server

I am building an SSRS report.
In the report, the week always runs from Monday - Sunday.
And I want to find out the START and END dates of prior two weeks.
For example,
If current date = Jan 1, 2011
-- current week = Dec 27, 2010 to Jan 2, 2011
-- previous week = Dec 20, 2010 to Dec 26, 2010
I have tried the following, but seems like it fails when when current day = Sunday
DECLARE #DT DATETIME
DECLARE #Offset INT
DECLARE #CM DATETIME
DECLARE #PM DATETIME
DECLARE #PS DATETIME
--SET #DT = GETDATE()
SET #DT = '11/14/2010' -- Monday
SET #Offset = (DATEPART(WEEKDAY, #DT) - 2) * -1
SET #CM = DATEADD(DAY, #Offset, #DT)
SET #PM = DATEADD(DAY, -7, #CM)
SET #PS = DATEADD(DAY, -1, #CM)
SELECT #Offset AS Offset, #DT AS Date, #CM AS Monday, #PM AS [Previous Monday], #PS AS [Previous Sunday], DATEPART(WK, #PM) AS Week
How can I fix it?
Add a [Calendar] table to your DB with all the dates you need precalculated. You can then include fields with the day name, number, holiday etc. and simply look up the values you need.
It's much simpler than playing with DATEADD
A useful article on calendar tables: Why should I consider using an auxiliary calendar table?
Not sure if this is the most elegant solution but you could do a case statement with your #offset like this:
SET #offset = CASE WHEN DATEPART(weekday, #today) >= 2
THEN -(DATEPART(weekday, #today) - 2)
ELSE -(DATEPART(weekday, #today) + 5)
END
I believe it works for all cases.