Get end week date function - sql

The start week is Saturday and end week is Friday, I'd like to write a function to return the end week of a date.
e.g.
'2014-6-26' (Thursday) would return '2014-6-27' (Friday)
'2014-6-27' (Friday) would return '2014-6-27' (same day)
'2014-6-28' (Sat) would return '2014-7-04' (next Friday)
Because I cannot SET DATEFIRST in functions, I am struggling to create this function.
This is what I got so far. It works in all scenarios except when I enter a Saturday it returns the previous Friday (which is wrong). e.g. '2014-6-28' returns '2014-6-27'
CREATE FUNCTION [GetEndWeek](
#Date DATETIME
)
RETURNS DATETIME
WITH SCHEMABINDING, RETURNS NULL ON NULL INPUT
AS
BEGIN
RETURN DATEADD(DAY,(13 - (##DATEFIRST + DATEPART(WEEKDAY, #Date )))%7,#Date )
END
Any help appreciated.
I do not want to SET DATEFIRST anywhere else (i.e. in procs) as I'm calling this function a lot.
Thanks!

Assuming that the language is English, you should use datename() rather than datepart(). datepart() is subject to system values. I think this logic does what you want:
return dateadd(day,
(case datename(weekday, #Date)
when 'Saturday' then 6
when 'Sunday' then 5
when 'Monday' then 4
when 'Tuesday' then 3
when 'Wednesday' then 2
when 'Thursday' then 1
when 'Friday' then 0
end),
#Date);

I'm not sure the existing function is correct, it gave me the column "prev_func" below, so for Sat Jun 28 I would expect the following Friday (July 4), like this:
| ADATE | MODULUS_7 | NEW_METHOD | PREV_FUNC |
|-------------------------|-----------|-----------------------|-----------------------|
| Thursday, June 26, 2014 | 1 | Friday, June 27, 2014 | Friday, June 27, 2014 |
| Friday, June 27, 2014 | 0 | Friday, June 27, 2014 | Friday, June 27, 2014 |
| Saturday, June 28, 2014 | -1 | Friday, July 04, 2014 | Friday, June 27, 2014 |
You don't have to use ##datefirst or change datefirst to arrive at predictable day of week calculations. The date zero in sql server, 1900-01-01 is a Monday, so datediff(day,0,[date-here]) % 7 is predictable with zero (no remainder) being Monday, and Friday is 4.
I believe the calculation you need is in the query below, column "new_method" :
select
format(adate,'D') as adate
, 4 - (datediff(day,0,adate) % 7) as modulus_7
, format(
case when 4 - datediff(day,0,adate) % 7 < 0
then dateadd(day,4 - (datediff(day,0,adate) % 7) + 7,adate)
else dateadd(day,4 - datediff(day,0,adate) % 7,adate)
end
,'D') as new_method
, format(dbo.getendweek(adate),'D') as prev_func
from table1
;
see: http://sqlfiddle.com/#!6/0903f/16

Related

sql query every week start date and end date calculated by its start date of every month

I need to calculate the start date and its end date base on month start
Week
date
1
1-Jan
2
5-Jan
3
12-Jan
4
18-Jan
5
26-Jan
6
31-Jan
if my month 1st week start on 1st jan and its end 2jan than my start date comes 1/jan and end date 2/jan. beause mon comes 3rd jan and my week calc. started on 1st jan sat.
like below example of cliender
Mon
Tue
Wed
Thu
Fri
Sat
Sun
Weeks
1
2
1st Week
3
4
5
6
7
8
9
2nd Week
10
11
12
13
14
15
16
3rd Week
17
18
19
20
21
22
23
4th Week
24
25
26
27
28
29
30
5th Week
31
6th Week
I need 2 column
week start date
week end date
expected data like below
Week
date
week start date
week end date
1
1-Jan
1-Jan
2-Jan
2
5-Jan
3-Jan
9-Jan
3
12-Jan
10-Jan
16-Jan
4
18-Jan
17-Jan
23-Jan
5
26-Jan
24-Jan
30-Jan
6
31-Jan
31-Jan
31-Jan
For MSSql and Sql Server:
If you send your date value as #yourdatevalue parameter to belowed query, it should work fine:
DECLARE #yourdatevalue datetime = GETDATE();
-- strip the time part from your date
DECLARE #date datetime = CONVERT(date, #yourdatevalue);
-- do the day of week math
DECLARE #start datetime = DATEADD(d, 1 - DATEPART(w, #date), #date),
#end datetime = DATEADD(d, 8 - DATEPART(w, #date), #date);
SELECT #yourdatevalue AS [date], #start AS [WeekStart], #end AS [WeekEnd];
Also:
You can set #WeekNum and #YearNum to whatever you want - in this example they are derived from the #datecol variable, which is set to GETDATE() for purposes of illustration. Once you have those values- you can calculate the date range for a week by using the following:
DECLARE #datecol datetime = GETDATE();
DECLARE #WeekNum INT
, #YearNum char(4);
SELECT #WeekNum = DATEPART(WK, #datecol)
, #YearNum = CAST(DATEPART(YY, #datecol) AS CHAR(4));
-- once you have the #WeekNum and #YearNum set, the following calculates the date range.
SELECT DATEADD(wk, DATEDIFF(wk, 6, '1/1/' + #YearNum) + (#WeekNum-1), 6) AS StartOfWeek;
SELECT DATEADD(wk, DATEDIFF(wk, 5, '1/1/' + #YearNum) + (#WeekNum-1), 5) AS EndOfWeek;

Calculate number of months between 2 dates SQL

I want to calculate number of full months between 2 dates
declare #startdate date='2021-03-03';
declare #enddate date='2021-05-02';
select datediff(mm,#startdate,#enddate)
This is giving me output as 2 but I want it to be 1 as it should count full months only. So from 03/03/2021 to 03/04/2021 is 1 month and from 03/04/21 to 02/05/21 is still not full month. So, the answer should be 1 in this example. How do I achieve this?
One way is
declare #startdate date='2021-03-03';
declare #enddate date='2021-05-02';
select datediff(mm,#startdate,#enddate) - case when dateadd(mm, datediff(mm,#startdate,#enddate), #startdate) > #enddate then 1 else 0 end
Full month is a tricky thing in some cases.
If I start on Jan 31 and end on Feb 28 is that a full month?
If I start on Feb 28, 2019 and end on Feb 28, 2020 is that 12 full months?
If I start on Feb 29, 2020 and end on Feb 28, 2021 is that 12 full months?
If you did not care about these cases and just wanted a simplistic rule like "it's a full month if the EndDay>=StartDay you could adjust the difference with a "Case When Day(Enddate)<Day(Startdate) Then 1 Else 0 End"

Teradata SQL Week Number - Week 1 starting 1st Jan with weeks aligned to specific day of the week

my first post on here so please be gentle...
I'm trying to create a week number variable in Teradata (SQL) that does the following:
Week 1 always starts on 1st January of the given year
Week numbers increment on the specified day of the week
For example: If Saturday was the specified day of the week:
2019-01-01 would be the start of week 1, 2019, changing to week 2 on 2019-01-05
2020-01-01 would be the start of week 1, 2020, changing to week 2 on 2020-01-04
I have come up wit the following based on an Excel function however it doesn't quite work as expected:
ROUND(((DATE_SPECIFIED - CAST(EXTRACT(YEAR FROM DATE_SPECIFIED) || '-01-01' AS DATE) + 1) - ((DATE_SPECIFIED - DATE '0001-01-06') MOD 7 + 1) + 10) / 7) AS REQUIRED_WEEK
The last digit of the section - DATE '0001-01-06' deals with the specified day of the week, where '0001-01-01' would be Monday.
This works in some cases however for some years, the first week number is showing as 0 where it should be 1, e.g. 1st Jan 2018 / 2019 are fine whereas 1st Jan 2020 is not.
Any ideas to correct this would be gratefully received.
Many thanks,
Mike
You can apply NEXT_DAY for both the specified date and Jan 1st of that year, e.g. for Saturday as week start:
(Next_Day(DATE_SPECIFIED,'SAT') - Next_Day(Trunc(DATE_SPECIFIED,'yyyy'),'SAT')) / 7 +1
Hmmm . . . I'm a bit week on Teradata functions. But the idea is to get the start of the second week. This follows the rule:
Jan 1 weekday (TD) 2nd week
Sunday 1 01-02
Monday 2 01-08
Tuesday 3 01-07
Wednesday 4 01-06
Thursday 5 01-05
Friday 6 01-04
Saturday 7 01-03
I think the following logic calculates this:
select t.*,
(case when td_day_of_week(cast(extract(year from DATE_SPECIFIED) || '-01-01' as date) ) = 1
then cast(extract(year from DATE_SPECIFIED) + '-01-02' as date)
else extract(year from DATE_SPECIFIED) + 10 - cast(td_day_of_week(cast(extract(year from DATE_SPECIFIED) || '-01-01') as date)
from t;
Then do you your week calculate either from the second week or subtract one more week to get when the first week really starts.

date_format() unexpected results with <= or <= in where clause

We are using a built in calendar table dm_reference.dim_date with a sequence of dates:
select * from dm_reference.dim_date limit 10;
calendar_date date_name, day_of_week
1999-01-01 January 1, 1999 1 5 Friday
1999-01-02 January 2, 1999 2 6 Saturday
1999-01-03 January 3, 1999 3 7 Sunday
1999-01-04 January 4, 1999 4 1 Monday
1999-01-05 January 5, 1999 5 2 Tuesday
1999-01-06 January 6, 1999 6 3 Wednesday
1999-01-07 January 7, 1999 7 4 Thursday
1999-01-08 January 8, 1999 8 5 Friday
1999-01-09 January 9, 1999 9 6 Saturday
1999-01-10 January 10, 1999 10 7 Sunday
I wanted to filter this to only include dates between August 2014 and the current year month.
If I select min(date_format(calendar_date, "YYYYMM")) from dm_reference.dim_date I get returned 199901
So, I tried the following query to format my calendar_date field as year and month and then to filter to include dates between august 14 now:
select
distinct date_format(calendar_date, "YYYY-MMM") as year_month
, date_format(calendar_date, "YYYYMM") as year_month_num -- for ordering in asc
from dm_reference.dim_date
where date_format(calendar_date, "YYYYMM") <= 201408
and date_format(calendar_date, "YYYYMM") <= date_format(from_unixtime(unix_timestamp()), "YYYYMM")
order by year_month_num;
This returns dates going all the way back to 1999 whereas I expected the earliest date in this query result to be august 2014.
Any idea why this is happening? How can I query our calendar to only include a filtered date range?
I think you are complicating the query. You can simply filter for the required date range using
select *
from dm_reference.dim_date
where calendar_date >= '2014-08-01' and calendar_date < trunc(current_date,'MM')
This outputs all dates on or after August 2014 until the end of last month. If you need data until today, use the ending condition as calendar_date <= current_date.
The reason your query doesn't return expected results is because the condition is year month of calendar_date <= '201408' and year month of calendar_date <= '201808', which is everything before 201408.

Need list of weeknumbers between two dates

I need a resultset of weeknumers, year and startdate of all weeks between two dates. I need it to match other search results to the weeks. Since the report will span just over a year, I need it to match the calendars.
We're in Europe, so weeks start on MONDAY.
I use SQL Server via a JDBC connection. I cannot use the calander table.
I've come across various solutions, but none does just what I need it to. This is the kind of list I need, but somehow the results are not correct. I can't find my mistake:
WITH mycte AS
(
SELECT DATEADD(ww, DATEDIFF(ww,0,CAST('2010-12-01' AS DATETIME)), 0) DateValue
UNION ALL
SELECT DateValue + 7
FROM mycte
WHERE DateValue + 7 < '2016-12-31'
)
SELECT DATEPART(wk, DateValue) as week, DATEPART(year, DateValue) as year, DateValue
FROM mycte
OPTION (MAXRECURSION 0);
I used --SET DATEFIRST 1; to make sure weeks start on monday.
The result looks like:
week year DateValue
----------- ----------- -------------------------
49 2010 2010-11-29 00:00:00.0
50 2010 2010-12-06 00:00:00.0
51 2010 2010-12-13 00:00:00.0
52 2010 2010-12-20 00:00:00.0
53 2010 2010-12-27 00:00:00.0
2 2011 2011-01-03 00:00:00.0
3 2011 2011-01-10 00:00:00.0
4 2011 2011-01-17 00:00:00.0
5 2011 2011-01-24 00:00:00.0
6 2011 2011-01-31 00:00:00.0
The problem is obvious. 2010 hasn't 53 weeks, and week 1 is gone.
This hapens for other years as well. Only 2015 has 53 weeks.
(note: in iso weeks (Europe) there only 52 weeks in 2010, see wiki: https://en.wikipedia.org/wiki/ISO_week_date)
The following 71 years in a 400-year cycle (add 2000 for current
years) have 53 weeks (leap years, with February 29, are emphasized),
years not listed have 52 weeks: 004, 009, 015, 020, 026, 032, 037,
043, 048, 054, 060, 065, 071, 076, 082, 088, 093, 099,105, 111, 116,
122, 128, 133, 139, 144, 150, 156, 161, 167, 172, 178, 184, 189,
195,201, 207, 212, 218, 224, 229, 235, 240, 246, 252, 257, 263, 268,
274, 280, 285, 291, 296,303, 308, 314, 320, 325, 331, 336, 342, 348,
353, 359, 364, 370, 376, 381, 387, 392, 398.
The dates are correct though. 2012-12-27 is a monday and so is 2011-01-03.
But in Europe we always have a full week 1 (so there is always a monday with weeknumber 1)
Any ideas what hapend to week 1 or why there are so many years with 53 (which is wrong)?
Use iso_week in DATEPART:
ISO 8601 includes the ISO week-date system, a numbering system for
weeks. Each week is associated with the year in which Thursday occurs.
For example, week 1 of 2004 (2004W01) ran from Monday 29 December 2003
to Sunday, 4 January 2004. The highest week number in a year might be
52 or 53. This style of numbering is typically used in European
countries/regions, but rare elsewhere.
WITH mycte AS
(
SELECT DATEADD(ww, DATEDIFF(ww,0,CAST('2010-12-01' AS DATETIME)), 0) DateValue
UNION ALL
SELECT DateValue + 7
FROM mycte
WHERE DateValue + 7 < '2016-12-31'
)
SELECT DATEPART(iso_week, DateValue) as week, DATEPART(year, DateValue) as year,
DateValue
FROM mycte
OPTION (MAXRECURSION 0);
LiveDemo
You can also consider changing recursive CTE with tally table.
The reason you're seeing 53 weeks in 2010 is simply because, there are 53 weeks in 2010.
Let's take a closer look at how the weeks break down in that year:
Declare #FromDate Date = '2010-01-01',
#ToDate Date = '2011-01-03'
;With Date (Date) As
(
Select #FromDate Union All
Select DateAdd(Day, 1, Date)
From Date
Where Date < #ToDate
)
Select Date, DatePart(Week, Date) WeekNo, DateName(WeekDay, Date) WeekDay
From Date
Option (MaxRecursion 0)
SQL Fiddle
Here's how the beginning of the year is:
Date WeekNo WeekDay
---------- ----------- ------------------------------
2010-01-01 1 Friday
2010-01-02 1 Saturday
2010-01-03 2 Sunday
2010-01-04 2 Monday
2010-01-05 2 Tuesday
2010-01-06 2 Wednesday
2010-01-07 2 Thursday
2010-01-08 2 Friday
2010-01-09 2 Saturday
2010-01-10 3 Sunday
Since the year begins in the middle of a week, there are only two days for Week 1. This causes the year to have 53 total weeks.
Now, to answer your question for why you don't see a Week 1 value for 2011, let's look at how that year ends:
Date WeekNo WeekDay
---------- ----------- ------------------------------
2010-12-26 53 Sunday
2010-12-27 53 Monday
2010-12-28 53 Tuesday
2010-12-29 53 Wednesday
2010-12-30 53 Thursday
2010-12-31 53 Friday
2011-01-01 1 Saturday
2011-01-02 2 Sunday
2011-01-03 2 Monday
You are selecting your dates in 7-day increments. The last date that you pulled for 2010 was 2010-12-27, which was accurately being displayed as being in Week 53. But the beginning of the next year occurs within this week on the Saturday, making Saturday Week 1 of 2011, with the following day starting Week 2.
Since you are not selecting a new date until Monday, 2011-01-03, it will effectively skip the dates in the first week of 2011, and begin with Week 2.
SQL Server is using standard week numbers, which match with Outlook (and while 53 sounds odd it is valid).
Of course you could always create your own custom week numbers. All you need to do is to pick a starting Monday and calculate the weeknumber you would assign. Your CTE can then increment its own week count, which resets to 1 each time the year changes. But even this will return week 53 (Dec 31st 2012 was a Monday, making it the 53rd week of that year, even though the rest of the week feel in 2013).
Worth mentioning; your week numbers are unlikely to match those from other systems/processes. This could cause you problems further down the line.
SET DATEFIRST 1;
WITH [Week] AS
(
SELECT
CAST('2010-11-29' AS DATE) AS [Date],
48 AS WeekNumber
UNION ALL
SELECT
DATEADD(DAY, 7, [Date]) AS [Date],
CASE
-- Reset the week number count when the year changes.
WHEN YEAR([Date]) <> YEAR(DATEADD(DAY, 7, [Date])) THEN 1
ELSE WeekNumber + 1
END AS WeekNumber
FROM
[Week]
WHERE
[Date] < GETDATE()
)
SELECT
*
FROM
[Week]
OPTION
(MAXRECURSION 0)
;