How to get all the dates in a full calendar month - sql

In a calendar control, we can see some dates from the previous month and next month also. Sample image below
(ie Apr-2016: Starts from Mar-28 and ends in May-08
Mar-2016: Starts from Apr Feb-29 and ends in Apr-10)
Here, i need to generate a list of all the dates in a calendar control for a particular year month. My week start is Monday.
Here is the tsql script i have tried so far.
DECLARE #V_DATE DATE = GETDATE()
;WITH CTE_DATE AS (
SELECT DATEADD(dd,-(DAY(#V_DATE)-1),#V_DATE) CDATE
UNION ALL
SELECT DATEADD(dd,1,CDATE)
FROM CTE_DATE
WHERE DATEADD(dd,1,CDATE) <= DATEADD(dd,-(DAY(DATEADD(mm,1,CDATE))),DATEADD(mm,1,CDATE))
)
SELECT * FROM CTE_DATE
Result Is:
2016-04-01
2016-04-02
.
.
2016-04-29
2016-04-30
It will list all the days from a inputted year month, but i need to include the
missing dates from the previous month as well as next month.
Expected result for Apr-2016
2016-03-28
2016-03-29
.
2016-04-15
.
2016-05-07
2016-05-08
Expected result for May-2016
2016-04-25
2016-04-26
.
2016-05-15
.
2016-06-04
2016-06-05
Note:- The calendar control is always showing 42 days.

since your week is starts on Monday,you can take referece to date 0 '1900-01-01' which is a Monday. Adding 41 days would gives you your end date
select
date_fr = dateadd(day, datediff(day, 0, '2016-05-01') / 7 * 7, 0),
date_to = dateadd(day, datediff(day, 0, '2016-05-01') / 7 * 7, 41)
the following gives you date 1900-01-01 and Monday
select convert(datetime, 0), datename(weekday, 0)

Have you considered creating a dates table in your database. You would have columns for dates and a column for week number. Linking to this table you could find the week number for your start and end dates, you could then re-link to the table to find the first date of your start week and the last date of your end week. This would probably be more efficient than calculations at each step each time, it is a simple link.

I have create done script for this. This is working as per my expectation, may be helpful for future reference. (Thanks #Squirrel for the logic).
DECLARE #V_ST_DATE DATE = GETDATE()
SET #V_ST_DATE = DATEADD(DAY,-(DAY(#V_ST_DATE)-1),#V_ST_DATE)
SET #V_ST_DATE = DATEADD(WEEK,DATEDIFF(WEEK, 0, #V_ST_DATE) ,0) +
(CASE WHEN DATEADD(WEEK,DATEDIFF(WEEK, 0, #V_ST_DATE) ,0) > #V_ST_DATE THEN -7 ELSE 0 END)
;WITH CTE_DATE AS (
SELECT #V_ST_DATE CDATE,0 TDAYS
UNION ALL
SELECT DATEADD(DAY,1,CDATE) , DATEDIFF(DAY,#V_ST_DATE, DATEADD(DAY,1,CDATE))
FROM CTE_DATE
WHERE DATEDIFF(DAY,#V_ST_DATE, DATEADD(DAY,1,CDATE)) < 42
)
SELECT * FROM CTE_DATE

Related

52weeks rolling with running week data

Can someone suggest me how do we consider week to start on Sunday and end on Saturday, while numbering them backwards in a 52 week rolling report like week1, week2.. week52
I want to count my current week as Week1 starting on Sunday, so even if its partial week its still week1 and last week Sunday-Saturday is week2 and so on until 52nd week last year (that would roughly be in September counting backwards). I need this as I am working on a daily report that will look for sales for current week and past 51 (full) weeks. My report should also return any week without sales '0' without skipping it.
Here is a way. Note I created the recursive CTE to populate some dates. You won't have to do this step, and real only need the YourWeekOrder = ... part.
declare #startDate date = dateadd(year,-1,getdate())
declare #endDate date = getdate()
;with cte as(
select #startDate as TheDate
union all
select dateadd(day,1,TheDate)
from cte
where TheDate < #endDate)
select
TheDate
,TheWeekOfYear = datepart(week,TheDate)
,YourWeekOrder = dense_rank() over (order by cast(datepart(year,TheDate) as char(4)) + case when len(datepart(week,TheDate)) = 1 then '0' + cast(datepart(week,TheDate) as char(2)) else cast(datepart(week,TheDate) as char(2)) end desc)
from cte
order by
TheDate
option(maxrecursion 0)
SEE IT IN ACTION HERE

Last Wednesday of every month

I need to work out the last Wednesday for each month in a range of dates.
I have the code for calculating the last Wednesday, but my cte doesn't seem to step through the months correctly.
Current code:
declare #S_Date date
declare #E_Date date
set #S_Date='2016-01-31' --eomonth of first month
set #E_Date='2016-06-15' --ides of last month ( can actually be any day that is
--not equal to or after the last Wednesday of the month)
;with LW(D_Date) as
(
select dateadd(dd,datediff(dd,0,#S_Date)/7*7+2,0)
union all
select dateadd(dd,datediff(dd,0,eomonth(D_Date,1))/7*7+2,0)
from LW
where D_date<#E_Date
)
select d_date
from LW
changing the eomonth to
select dateadd(dd,datediff(dd,0,dateadd(mm,1,D_Date))/7*7+2,0)
doesn't seem to work either
Expected results:
2016-01-27
2016-02-24
2016-03-30
2016-04-27
2016-05-25
2016-06-29
Here's a way to do it with EOMONTH() and DATEFROMPARTS():
declare #S_Date date
declare #E_Date date
set #S_Date='2016-01-31' --eomonth of first month
set #E_Date='2016-06-15' --ides of last month ( can actually be any day that is
--not equal to or after the last Wednesday of the month)
;With Date (Date) As
(
Select DateFromParts(Year(#S_Date), Month(#S_Date), 1) Union All
Select DateAdd(Day, 1, Date)
From Date
Where Date < EOMonth(#E_Date)
)
Select Max(Date) As LastWednesday
From Date
Where DatePart(WeekDay, Date) = 4
Group By Year(Date), Month(Date)
Option (MaxRecursion 0)
Output
LastWednesday
2016-01-27
2016-02-24
2016-03-30
2016-04-27
2016-05-25
2016-06-29
Here a slightly different approach without the partitioning... result should be the same:
declare #S_Date date
declare #E_Date date
set #S_Date='2016-01-31' --eomonth of first month
set #E_Date='2016-06-15' --ides of last month ( can actually be any day that is
--not equal to or after the last Wednesday of the month)
;With Date (Date, WD) As
(
Select DateFromParts(Year(#S_Date), Month(#S_Date), 1), DatePart(WeekDay, DateFromParts(Year(#S_Date), Month(#S_Date), 1)) AS wd
Union All
Select DateAdd(Day, 1, Date), DatePart(WeekDay, DateAdd(Day, 1, Date)) AS wd
From Date
Where Date < EOMonth(#E_Date)
)
Select Max(Date) As LastWednesday
From Date
Where wd = 4
group by MONTH(Date)
Option (MaxRecursion 0)
You need a calendar table ..I have a calendar table and below screenshot shows you how simple it is check or get last wednesday of each month..
query used:
select * from dbo.calendar where year='2013' and wkdname='Wednesday'
and last=1
Output of above query which you can modify as per your requirement:
This is rather straightforward solution with eomonth.
declare #s_date date='2016-01-01',#e_date date='2016-12-31' --date range
;with eom_tbl as (--CTE
--anchor from start date
select EOMONTH(#s_date,0) eom,datepart(weekday,EOMONTH(#s_date,0)) dw
union all
--recursive query
select EOMONTH(eom,1) eom,datepart(weekday,EOMONTH(eom,1)) dw
from eom_tbl where eom<#e_date
)
select eom,dw,
--calculate last Wednesday (4)
case when dw<4 then dateadd(dd,-3-dw,eom)
else dateadd(dd,4-dw,eom) end lastWed,
--extra check
datepart(dw,case when dw<4 then dateadd(dd,-3-dw,eom)
else dateadd(dd,4-dw,eom) end ) ddw
from eom_tbl

SQL Server : is there a way for me to get weeks per month using Month and Year?

I'm not really good when it comes to database...
I'm wondering if it is possible to get the weeks of a certain month and year..
For example: 1 (January) = month and 2016 = year
Desired result will be:
week 1
week 2
week 3
week 4
week 5
This is what I have tried so far...
declare #date datetime = '01/01/2016'
select datepart(day, datediff(day, 0, #date) / 7 * 7) / 7 + 1
This only returns the total of the weeks which is 5.
declare #MonthStart datetime
-- Find first day of current month
set #MonthStart = dateadd(mm,datediff(mm,0,getdate()),0)
select
Week,
WeekStart = dateadd(dd,(Week-1)*7,#MonthStart)
from
( -- Week numbers
select Week = 1 union all select 2 union all
select 3 union all select 4 union all select 5
) a
where
-- Necessary to limit to 4 weeks for Feb in non-leap year
datepart(mm,dateadd(dd,(Week-1)*7,#MonthStart)) =
datepart(mm,#MonthStart)
Got the answer in the link: http://www.sqlservercentral.com/Forums/Topic1328013-391-1.aspx
Here is one way to approach this:
A month has a minimum of 29 or more days, and a max of 31 or less. Meaning there are almost always 5 weeks a month, with the exception of a non-leap year's feburary, and in those cases, 4 weeks a month.
You can refer to this to find out which years are "leap".
Check for leap year
Hope this helps!
The following code will allow you to select a start and end date, and output one row per week, with numbered weeks, between those dates:
declare #start date = '1/1/2016'
declare #end date = '5/1/2016'
;with cte as (select #start date
, datename(month, #start) as month
union all
select dateadd(dd, 7, date)
, datename(month, dateadd(dd, 7, date))
from CTE
where date <= #end
)
select *, 'week '
+ cast(row_number() over (partition by month order by date) as varchar(1))
from CTE
order by date

SQL Server 2008 - date functions and formulas

I want to return results where if the date falls on 10 & 28 of each month, but if either is a weekend return the result for Friday (first working day before).
Eg. if the following lines to be returned are
10 Oct 2010 Sunday
28 Oct 2010 Thursday
In the table I have
LineId Date
1 08 Oct 2010
2 28 Oct 2010
so, because 10 October is a Sunday, therefore won't be in the table, it will return LineID 1 as the is first working day before.
Thank you.
DATEPART(WEEKDAY and DATEPART(DW are dependant on the DATEFIRST setting. To avoid incorrect results the ##DATEFIRST function can be used.
WITH T(D) AS
(
SELECT CAST('20111008' AS DATE) UNION ALL
SELECT CAST('20111009' AS DATE) UNION ALL
SELECT CAST('20111010' AS DATE) UNION ALL
SELECT CAST('20111011' AS DATE) UNION ALL
SELECT CAST('20111012' AS DATE) UNION ALL
SELECT CAST('20111013' AS DATE) UNION ALL
SELECT CAST('20111014' AS DATE)
)
SELECT CASE
WHEN ( ##DATEFIRST + DATEPART(WEEKDAY, D) ) % 7 > 1 THEN D
ELSE DATEADD(DAY, -( 1 + ( ##DATEFIRST + DATEPART(WEEKDAY, D) ) % 7 ), D)
END AS WeekDayOrPrecedingFriday
FROM T
Select Case
When DatePart(dw,SampleData.[Date]) = 1 Then DateAdd(d,-2,SampleData.[Date])
When DatePart(dw,SampleData.[Date]) = 7 Then DateAdd(d,-1,SampleData.[Date])
Else SampleData.[Date]
End
From (
Select Cast('2010-10-10' As datetime) As [Date]
Union All Select '2010-10-28' As [Date]
) As SampleData
You may find that it is easier to have a Calendar table with one row for all days you need where you indicate whether the given day is a "working" day. In this way, you can easily account for holidays and the actual day off for holidays (e.g. if July 4th, in the US, is on a Saturday, mark the preceding Friday as a day off.).
If you are really worried about dealing with DateFirst, just set it prior to running your query:
Set DateFirst 7;
The above is the US default setting which is that Sunday is the first day of the week.
I really like to use a calendar table for queries like these.
-- For convenience, I'll use a view. The view "weekdays" is a proper
-- subset of the table "calendar".
create view weekdays as
select * from calendar
where day_of_week in ('Mon', 'Tue', 'Wed', 'Thu', 'Fri');
Having done that, the query is not only dead simple, it can easily be seen to be right.
select max(cal_date)
from weekdays
where cal_date <= '2010-10-10' -- Returns 2011-10-08
Doesn't account for holidays that might fall on the 10th or 28th, but that's easy enough to remedy.

How to get Monday, Sunday based on a Week Number in SQL Server?

I have a week number (e.g. 23) and I'd like to get the dates for Monday and Sunday of that week.
I am assuming Monday as the first day of the week (e.g. I have SET DATEFIRST 1 in my script).
DECLARE #startweek1 datetime
SET DATEFIRST 1
--first monday of year
SELECT #startweek1 = DATEADD(day, 8-DATEPART(weekday, '2011-01-01'), '2011-01-01')
--day based
SELECT
DATEADD(day, 22 * 7, #startweek1) AS MondayWeek23,
DATEADD(day, 23 * 7 -1 , #startweek1) AS SundayWeek23
--week based
SELECT
DATEADD(week, 22, #startweek1) AS MondayWeek23,
DATEADD(day, -1, DATEADD(week, 23 , #startweek1)) AS SundayWeek23
Edit:
This solution works if week 1 does not start on day 1 as Andomar said
Edit 2:
According to Wikipedia: 2008-12-29 is day 1 of week 1 of 2009 in ISO.
And week numbers vary as Andomar said
Cyberkiwi mentioned this code is wrong for 2007: it's wrong far more often than that. The same applies to his code too which matches 2007 but is equally wrong for the rest.
Declare #StartDate datetime;
Set #StartDate = '20110101';
With StartOfWeek As
(
Select DateAdd(
week
, 23
, DateAdd(
d
, -(DatePart(dw, #StartDate) - 1)
, #StartDate
) ) As Sunday
)
Select Sunday, DateAdd(d,1,Sunday) As Monday
From StartOfWeek
We can use the DATEFIRST variable to set the start day of the week. By default, the week starts on Sunday(7). In order to start with Monday, we will set its value to 1.
# Printing default value
Select ##DATEFIRST; -- This will give 7 value i.e. default value
# Changing first day of the week to Monday.
Declare #CurrentWeekNumber int
SET DATEFIRST 1
set #CurrentWeekNumber= (SELECT DATEPART(WEEK, GETDATE()))
select #CurrentWeekNumber
PS: Changes to DATEFIRST are valid for current session i.e. it doesn't impact other DB queries which might rely on different week start day.
A calendar table makes this kind of date query pretty simple. A calendar table just has the relevant calendar details calculated and stored at load time instead of calculated at run time. Depending on the application, that can speed things up a lot. This way is dbms agnostic; it just uses literals in the WHERE clause.
select cal_date from calendar
where iso_year = 2011
and iso_week = 23
and cal_dow in ('Mon', 'Sun');
And another way that relies only on having a table of dates.
select cal_date from calendar
where extract(isoyear from cal_date) = 2011
and extract(week from cal_date) = 23
and (extract(isodow from cal_date) = 1 or
extract(isodow from cal_date) = 7);
EXTRACT() is standard SQL, but I'm not sure whether the names of the subfields isoyear, week, isodow are standard SQL.
Note: Any answer that starts with assuming year is 2011 might as well take a static first date, i.e. by replacing #firstMon in my last query below with just the static date '20101227'.
SET DATEFIRST 1
declare #targetYear int
declare #targetWeek int
select #targetYear = 2011, #targetWeek = 23
declare #firstMon datetime
set #firstMon = dateadd(d,1-datepart(dw, right(#targetYear,4)+'0101'),right(#targetYear,4)+'0101')
select
MonOfTargetWeek = dateadd(week, #targetWeek-1, #firstMon),
SunOfTargetWeek = dateadd(week, #targetWeek-1, #firstMon+6)
And the real, final query - which returns a Mon/Sun of NULL when it doesn't exist in that week, such as Mon of partial-week-one or Sun of partial-week-53
;with tmp(Mon, Sun) as (
select
MonOfTargetWeek = dateadd(week, #targetWeek-1, #firstMon),
SunOfTargetWeek = dateadd(week, #targetWeek-1, #firstMon+6)
)
select
RealMonday = case when Year(Mon)=#targetYear then Mon end,
RealSunday = case when Year(Sun)=#targetYear then Sun end
from tmp
For GBN's claim that this answer is wrong (within his answer), I submit below the proof of correctness
I have turned the code into a function.
CREATE function dbo.getMonSunForWeek(#targetYear int, #targetWeek int)
returns table as return
with pre(firstMon) as (
select dateadd(d,1-datepart(dw, right(#targetYear,4)+'0101'),right(#targetYear,4)+'0101'))
, tmp(Mon, Sun) as (
select
dateadd(week, #targetWeek-1, firstMon),
dateadd(week, #targetWeek-1, firstMon+6)
from pre
)
select
Mon, Sun,
RealMonday = case when Year(Mon)=#targetYear then Mon end,
RealSunday = case when Year(Sun)=#targetYear then Sun end
from tmp
GO
And below I present the ENTIRE range of years and weeks (1-53) for the years from 1950 through 2047. In EVERY SINGLE case, when the Monday/Sunday has been determined, and working backwards using DATEPART(week), SQL Server agrees with the week numbering from the function.
set datefirst 1;
select
[Year] = years.number,
[Week] = weeks.number,
[Mon] = fun.realmonday,
[Sun] = fun.realsunday,
[WeekX] = isnull(datepart(week, fun.realmonday), datepart(week, fun.realsunday)),
[MonX] = fun.Mon,
[SunX] = fun.Sun
from master..spt_values years
inner join master..spt_values weeks on weeks.type='P' and weeks.number between 1 and 53
cross apply dbo.getMonSunForWeek(years.number, weeks.number) fun
where years.type='P' and years.number between 1950 and 2047
order by [year], [Week]
Output around the 2005-2006 changeover
when including prior/next year
Year Week Mon-of-week Sun-of-week datepart(week) Mon -and- Sun
2005 52 2005-12-19 2005-12-25 52 2005-12-19 2005-12-25
2005 53 2005-12-26 NULL 53 2005-12-26 2006-01-01
2006 1 NULL 2006-01-01 1 2005-12-26 2006-01-01
2006 2 2006-01-02 2006-01-08 2 2006-01-02 2006-01-08
You might try some dateadd logic...
dateadd(week, 23, '2011-01-01')
dateadd(day, 7, dateadd(week, 23, '2011-01-01'))
UPDATE:
select dateadd(day, 23, dateadd(week, 23, '2011-01-01')) , Datename(weekday,dateadd(day, 23, dateadd(week, 23, '2011-01-01')));
// RETURNS 7/4/2011, Monday
select Datename(weekday,dateadd(day, 23, dateadd(week, 23, '2011-01-01')));
// RETURNS Monday
EXAMPLE FROM: http://msdn.microsoft.com/en-us/library/ms186819.aspx
DECLARE #datetime2 datetime2 = '2007-01-01 13:10:10.1111111'
SELECT 'year', DATEADD(year,1,#datetime2)
UNION ALL
SELECT 'quarter',DATEADD(quarter,1,#datetime2)
UNION ALL
SELECT 'month',DATEADD(month,1,#datetime2)
UNION ALL
SELECT 'dayofyear',DATEADD(dayofyear,1,#datetime2)
UNION ALL
SELECT 'day',DATEADD(day,1,#datetime2)
UNION ALL
SELECT 'week',DATEADD(week,1,#datetime2)
UNION ALL
SELECT 'weekday',DATEADD(weekday,1,#datetime2)
UNION ALL
SELECT 'hour',DATEADD(hour,1,#datetime2)
UNION ALL
SELECT 'minute',DATEADD(minute,1,#datetime2)
UNION ALL
SELECT 'second',DATEADD(second,1,#datetime2)
UNION ALL
SELECT 'millisecond',DATEADD(millisecond,1,#datetime2)
UNION ALL
SELECT 'microsecond',DATEADD(microsecond,1,#datetime2)
UNION ALL
SELECT 'nanosecond',DATEADD(nanosecond,1,#datetime2);
/*
Year 2008-01-01 13:10:10.1111111
quarter 2007-04-01 13:10:10.1111111
month 2007-02-01 13:10:10.1111111
dayofyear 2007-01-02 13:10:10.1111111
day 2007-01-02 13:10:10.1111111
week 2007-01-08 13:10:10.1111111
weekday 2007-01-02 13:10:10.1111111
hour 2007-01-01 14:10:10.1111111
minute 2007-01-01 13:11:10.1111111
second 2007-01-01 13:10:11.1111111
millisecond 2007-01-01 13:10:10.1121111
microsecond 2007-01-01 13:10:10.1111121
nanosecond 2007-01-01 13:10:10.1111111
*/