SQL Query for getting nth Weekday date between two dates - sql

My scenario is as below:
#StartDate = 13th of current month
#EndDate = 12th of next month.
I want to get all the date with the day-name for Mondays, Tuesdays, Wednesdays, Thursdays, Fridays, Saturdays and Sundays lying between the start and end date.

Try this:
declare #startDate datetime = '2016-01-13'
declare #endDate datetime = '2016-02-12'
;with dateRange as
(
select [Date] = dateadd(dd, 1, #startDate)
where dateadd(dd, 1, #startDate) < #endDate
union all
select dateadd(dd, 1, [Date])
from dateRange
where dateadd(dd, 1, [Date]) < #endDate
)
select [Date], datename(dw,[Date])
from dateRange

To count the number of each day as per your comment (this should be part of the question really), change the last part of James' answer to this:
select datename(dw,[Date]) as day_name, count([Date]) as number_days
from dateRange group by datename(dw,[Date]), datepart(DW,[Date])
order by datepart(DW,[Date]);

You can try something like this :
DECLARE #StartDate DATETIME
DECLARE #StartDateFixed DATETIME
DECLARE #EndDate DATETIME
DECLARE #NumberOfDays int
SET #StartDate = '2016/01/01'
SET #EndDate = '2016/01/02'
SET #NumberOfDays = DATEDIFF(DAY,#StartDate,#EndDate) + 1
SET #StartDateFixed = DATEADD(DD,-1,#StartDate)
SELECT WeekDay , COUNT(WeekDay)
FROM (
SELECT TOP (#NumberOfDays) WeekDay = DATENAME(DW , DATEADD(DAY,ROW_NUMBER() OVER(ORDER BY spt.name), #StartDateFixed))
FROM [master].[dbo].[spt_values] spt
) A
GROUP BY WeekDay
The output will be
WeekDay
------------------------------ -----------
Friday 1
Saturday 1
(2 row(s) affected)

In case if you need to get current and next date from date number specified such as 13 and 12
Current Month
DECLARE #cur_mont INT = (SELECT MONTH(GETDATE()))
Current Year
DECLARE #cur_year INT = (SELECT YEAR(GETDATE()))
Next Month
DECLARE #nxt_mont INT = (SELECT MONTH(DATEADD(month, 1, GETDATE())))
Next Month year (In case of December year change)
DECLARE #nxt_year INT = (SELECT YEAR(DATEADD(month, 1, GETDATE())))
Create start date
DECLARE #startDate DATETIME = (SELECT CAST(CAST(#cur_year AS varchar) + '-' + CAST(#cur_mont AS varchar) + '-' + CAST(13 AS varchar) AS DATETIME))
Create end date
DECLARE #endDate DATETIME = (SELECT CAST(CAST(#nxt_year AS varchar) + '-' + CAST(#nxt_mont AS varchar) + '-' + CAST(12 AS varchar) AS DATETIME))
Dates between start and end date
SELECT TOP (DATEDIFF(DAY, #startDate, #endDate) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #startDate) AS Date,
DATENAME(DW, DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #startDate)) AS Day
FROM sys.all_objects a CROSS JOIN sys.all_objects b;

DECLARE #dayStart int = 13, --The day of current month
#dayEnd int = 12, --The day of another month
#howManyMonth int = 1, --How many month to take
#dateStart date,
#dateEnd date
--Here we determine range of the dates
SELECT #dateStart = CONVERT (date,
CAST(DATEPART(Year,GETDATE()) as nvarchar(5))+ '-' +
CASE WHEN LEN(CAST(DATEPART(Month,GETDATE()) as nvarchar(5))) = 1
THEN '0'+CAST(DATEPART(Month,GETDATE()) as nvarchar(5))
ELSE CAST(DATEPART(Month,GETDATE()) as nvarchar(5)) END + '-' +
CAST (#dayStart as nvarchar(5))),
#dateEnd = CONVERT (date,
CAST(DATEPART(Year,DATEADD(Month,#howManyMonth,GETDATE())) as nvarchar(5))+ '-' +
CASE WHEN LEN(CAST(DATEPART(Month,DATEADD(Month,#howManyMonth,GETDATE())) as nvarchar(5))) = 1
THEN '0'+CAST(DATEPART(Month,DATEADD(Month,#howManyMonth,GETDATE())) as nvarchar(5))
ELSE CAST(DATEPART(Month,DATEADD(Month,#howManyMonth,GETDATE())) as nvarchar(5)) END + '-' +
CAST (#dayEnd as nvarchar(5)))
;WITH cte AS (
SELECT #dateStart as date_
UNION ALL
SELECT DATEADD(day,1,date_)
FROM cte
WHERE DATEADD(day,1,date_) <= #dateEnd
)
--Get results
SELECT DATENAME(WEEKDAY,date_) as [DayOfWeek],
COUNT(*) as [DaysCount]
FROM cte
GROUP BY DATEPART(WEEKDAY,date_),
DATENAME(WEEKDAY,date_)
ORDER BY DATEPART(WEEKDAY,date_)
OPTION (MAXRECURSION 0)
Output:
DayOfWeek DaysCount
----------- -----------
Sunday 4
Monday 4
Tuesday 4
Wednesday 5
Thursday 5
Friday 4
Saturday 4
(7 row(s) affected

Related

How to get date of a beginning of first work weekday even if the Monday is from last month?

My goal to is get get query that will return weekdays in a month. I can get the days of the month but I need to get dates starting from monday through Friday even if the Monday may be in the preceding month.
Example April 1st is a wednesday so I would need to bring back March 30th and 31st. And the last date returned would be by May 1st as that is the last friday that contains some April days..
If interested in a helper function, I have TVF which generates a calendar.
Example
Select * from [dbo].[tvf-Date-Calendar-Wide]('2020-04-01')
Returns
So, with a little tweak, we get can
Select WeekNr = RowNr
,B.*
From [dbo].[tvf-Date-Calendar-Wide]('2020-04-01') A
Cross Apply ( values (Mon)
,(Tue)
,(Wed)
,(Thu)
,(Fri)
) B(Date)
Which Returns
WeekNr Date
1 2020-03-30
1 2020-03-31
1 2020-04-01
1 2020-04-02
1 2020-04-03
2 2020-04-06
2 2020-04-07
2 2020-04-08
...
5 2020-04-29
5 2020-04-30
5 2020-05-01
The Function If Interested
CREATE FUNCTION [dbo].[tvf-Date-Calendar-Wide] (#Date1 Date)
Returns Table
Return (
Select RowNr,[Sun],[Mon],[Tue],[Wed],[Thu],[Fri],[Sat]
From (
Select D
,DOW=left(datename(WEEKDAY,d),3)
,RowNr = sum(Flg) over (order by D)
From (
Select D,Flg=case when datename(WEEKDAY,d)= 'Sunday' then 1 else 0 end
From (Select Top (42) D=DateAdd(DAY,-7+Row_Number() Over (Order By (Select Null)),#Date1) From master..spt_values n1 ) A
) A
) src
Pivot (max(d) for DOW in ([Sun],[Mon],[Tue],[Wed],[Thu],[Fri],[Sat]) )pvg
Where [Sun] is not null
and [Sat] is not null
)
-- Select * from [dbo].[tvf-Date-Calendar-Wide]('2020-04-01')
You first need to find the start of the week for the first day of the month, then the date for the end of the week that contains the last day of the month:
e.g.
SELECT WeekStart = DATEADD(DAY, -(DATEPART(WEEKDAY, '20200401')-1), '20200401'),
WeekEnd = DATEADD(DAY, 7-(DATEPART(WEEKDAY, '20200430')), '20200430');
Gives:
WeekStart WeekEnd
------------------------------
2020-03-29 2020-05-02
You wouldn't want to hard code the first and the last of the month, but these are fairly trivial things to get from a date:
DECLARE #Date DATE = '20200415';
SELECT MonthStart = DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0),
MonthEnd = EOMONTH(#Date);
Which returns
MonthStart MonthEnd
------------------------------
2020-04-01 2020-04-30
You can then just substitute this into the first query for week starts:
DECLARE #Date DATE = '20200401';
SELECT WeekStart = DATEADD(DAY, -(DATEPART(WEEKDAY, DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0))-1), DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0)),
WeekEnd = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(#Date))), EOMONTH(#Date));
Which gives the same output as the first query with hard coded dates. This is very clunky though, so I would separate this out into a further step:
DECLARE #Date DATE = '20200401';
-- SET DATE TO THE FIRST OF THE MONTH IN CASE IT IS NOT ALREADY
SET #Date = DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0);
SELECT WeekStart = DATEADD(DAY, -(DATEPART(WEEKDAY, #Date)-1), #Date),
Weekend = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(#Date))), EOMONTH(#Date));
Again, this gives the same output (2020-03-29 & 2020-05-02).
The next step is to fill in all the dates between that are weekdays. If you have a calendar table this is very simple
DECLARE #Date DATE = '20200415';
-- SET DATE TO THE FIRST OF THE MONTH IN CASE IT IS NOT ALREADY
SET #Date = DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0);
DECLARE #Start DATE = DATEADD(DAY, -(DATEPART(WEEKDAY, #Date)-1), #Date),
#End DATE = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(#Date))), EOMONTH(#Date));
SELECT [Date], DayName = DATENAME(WEEKDAY, [Date])
FROM Calendar
WHERE Date >= #Start
AND Date <= #End
AND IsWeekday = 1
ORDER BY [Date];
If you don't have a calendar table, then I suggest you create one, but if you can't create one you can still generate this on the fly, by generating a set series numbers and adding these numbers to your start date:
DECLARE #Date DATE = '20200415';
-- SET DATE TO THE FIRST OF THE MONTH IN CASE IT IS NOT ALREADY
SET #Date = DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0);
DECLARE #Start DATE = DATEADD(DAY, -(DATEPART(WEEKDAY, #Date)-1), #Date),
#End DATE = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(#Date))), EOMONTH(#Date));
-- GET NUMBERS FROM 0 - 50
WITH Dates (Date) AS
( SELECT TOP (DATEDIFF(DAY, #Start, #End))
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY n1.n) - 1, #Start)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n1 (n)
CROSS JOIN (VALUES (1),(1),(1),(1),(1)) n2 (n)
)
SELECT [Date], DayName = DATENAME(WEEKDAY, [Date])
FROM Dates
WHERE ((DATEPART(WEEKDAY, [Date]) + ##DATEFIRST) % 7) NOT IN (0, 1);
Just generate all possible dates -- up to 6 days before the month begins. Take the valid weekdays after the first Monday:
with dates as (
select dateadd(day, -6, convert(date, '2020-04-01')) as dte
union all
select dateadd(day, 1, dte)
from dates
where dte < '2020-04-30'
)
select dte
from (select d.*,
min(case when datename(weekday, dte) = 'Monday' then dte end) over () as first_monday
from dates d
) d
where datename(weekday, dte) not in ('Saturday', 'Sunday') and
dte >= first_monday;
declare #dateVal datetime = GETDATE(); --assign your date here
declare #monthFirstDate datetime = cast(YEAR(#dateVal) as varchar(4)) + '-' + DATENAME(mm, #dateVal) + '-' + cast(01 as varchar(2))
declare #monthLastDate datetime = DAteADD(day, -1, DATEADD(month, 1, cast(YEAR(#dateVal) as varchar(4)) + '-' + DATENAME(mm, #dateVal) + '-' + cast(01 as varchar(2))))
declare #startDate datetime = DATEADD(DAY, 2 - DATEPART(WEEKDAY, #monthFirstDate), CAST(#monthFirstDate AS DATE))
declare #enddate datetime = DATEADD(DAY, 6 - DATEPART(WEEKDAY, #monthLastDate), CAST(#monthLastDate AS DATE))
Select #startDate StartDate, #enddate EndDate
****Result**
--------------------------------------------------------------
StartDate | EndDate
-----------------------------|--------------------------------
2020-03-02 00:00:00.000 | 2020-04-03 00:00:00.000
-----------------------------|---------------------------------**

check is there any weekday inside time given sql server

I have been wondering is there any operator/function in sql server to tell about weekday between given day.
Example :11-19-2016 to 11-29-2016 I want check if there is tuesday between that day?
Try This:
declare #start varchar(100)='11-19-2016'
declare #end varchar(100)='11-29-2016'
;with dateRange as
(
select date = dateadd(dd, 1, #start)
where dateadd(dd, 1, #start) < #end
union all
select dateadd(dd, 1, date)
from dateRange
where dateadd(dd, 1, date) < #end
)
select date,DATENAME(dw,CAST(DATEPART(m, GETDATE()) AS VARCHAR)
+ '/'+ CAST(DATEPART(d, date) AS VARCHAR)
+ '/'+ CAST(DATEPART(yy, getdate()) AS VARCHAR)) as 'Day'
from dateRange where (DATENAME(dw,CAST(DATEPART(m, GETDATE()) AS VARCHAR)
+ '/'+ CAST(DATEPART(d, date) AS VARCHAR)
+ '/'+ CAST(DATEPART(yy, getdate()) AS VARCHAR)))='Tuesday'
The query below uses a recursive query to unfold the date range.
Then uses DATEPART to select only the Tuesdays from it.
But one could also use DATENAME instead.
declare #StartDate DATE = '2016-11-19';
declare #EndDate DATE = '2016-11-29';
--SET DATEFIRST 7;
-- The dw for tuesday is 3 when ##datefirst = 7 (default setting)
-- Since it depends on a usersetting, lets calculate it anyway.
declare #TuesdayWeekday INT = (7-##datefirst + 2)%7+1;
;with DATES as
(
select #startdate as [Date]
union all
select dateadd(day, 1, [Date]) from DATES
where [Date] < #enddate
)
select [Date], datepart(dw,[Date]) as day_of_week, datename(dw,[Date]) as day_of_weekname
from DATES
where datepart(dw,[Date]) = #TuesdayWeekday;

How to get first day and last day for every week in a month?

How can i get the first day(Monday) and last day(Sunday) for EVERY week in a month?
Example: July 2015
Week 1
First: 29-Jun
Last: 5-Jul
Week 2
First: 6-Jul
Last: 12-Jul
Week 3
First: 13-Jul
Last: 19-Jul
Week 4
First: 20-Jul
Last: 26-Jul
Week 5
First: 27-Jul
Last: 2-Aug
Here you go:
SELECT DATEADD(DAY, 2-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP); -- first day of week
SELECT DATEADD(DAY, 2-DATEPART(WEEKDAY, CURRENT_TIMESTAMP) + 6, CURRENT_TIMESTAMP); -- last day of week
SQLFiddle Demo
Try this:
SET DATEFIRST 1;
GO
declare #Month date = '150701';
declare #Index int = 1;
declare #StartWeek date;
declare #MonthWeek table ([Week] char(6), StartDate date, EndDate date)
set #StartWeek = DATEADD(dd, -(DATEPART(dw, #Month)-1), #Month)
insert into #MonthWeek([Week], StartDate, EndDate)
select 'Week ' + cast(#Index as char), #StartWeek, DATEADD(D, 6, #StartWeek);
select #StartWeek = DATEADD(D, 7, #StartWeek), #Index = #Index + 1;
while DATEPART(M,#StartWeek) = DATEPART(M,#Month)
begin
insert into #MonthWeek([Week],StartDate,EndDate)
select 'Week ' + cast(#Index as char), #StartWeek, DATEADD(D, 6, #StartWeek);
select #StartWeek = DATEADD(D, 7, #StartWeek), #Index = #Index + 1
end;
select * from #MonthWeek;
i think it Will Help You
declare #sdate date='20150601'
declare #edate date=dateadd(dd,-1,dateadd(month,1,#sdate))
; with cte as
(
select #sdate as startdate
union all
select dateadd(dd,1,startdate) from cte where startdate < #edate
)
select convert(varchar(20),startdate,106)as Date,datename(WEEKDAY,startdate),day(startdate)/7+1 as WEEKNO from cte
where datename(WEEKDAY,startdate) in ('Monday','Sunday')
option (MaxRecursion 32665)
here is Demo SQL FIDDLE

How can I rewrite this as a select statement using group by instead of using a loop

I am revisiting some old code I wrote for a report when I was still very new to SQL (MSSQL). It does what it is supposed to but its not the prettiest or most efficient.
The dummy code below mimics what I currently have in place. Here I am trying to get counts for the number of contracts that are open over the last 5 weeks. For this example a contract is considered open if the start date of the contract happens before of during the given week and the end date happens during or after the given week.
dbo.GetWeekStart(#Date DATETIME, #NumOfWeeks INT, #FirstDayOfWeek CHAR(3)) is a function that will return the first day of each week based on the date provided for a specified number of weeks. ie SELECT * FROM dbo.GetWeekStart('20120719', -2, 'MON') will return the 2 mondays prior to July 19, 2012.
How can I simplify this? I think there is someone to do this without a loop but I have not been able to figure it out.
DECLARE #RunDate DATETIME,
#Index INT,
#RowCount INT,
#WeekStart DATETIME,
#WeekEnd DATETIME
DECLARE #Weeks TABLE
(
WeekNum INT IDENTITY(0,1),
WeekStart DATETIME,
WeekEnd DATETIME
)
DECLARE #Output TABLE
(
WeekStart DATETIME,
OpenContractCount INT
)
SET #RunDate = GETDATE()
INSERT INTO #Weeks (WeekStart, WeekEnd)
SELECT WeekStart,
DATEADD(ss,-1,DATEADD(ww,1,WeekStart))
FROM dbo.[GetWeekStart](#RunDate, -5, 'MON')
SET #RowCount = (SELECT COUNT(*) FROM #Weeks)
SET #Index = 0
WHILE #Index < #RowCount
BEGIN
SET #WeekStart = (SELECT WeekStart FROM #Weeks WHERE WeekNum = #Idx)
SET #WeekEnd = (SELECT WeekEnd FROM #Weeks WHERE WeekNum = #Idx)
INSERT INTO #Output (WeekStart, OpenContractCount)
SELECT #WeekStart,
COUNT(*)
FROM Contracts c
WHERE c.StartDate <= #WeekEnd
AND ISNULL(c.EndDate, GETDATE()) >= #WeekStart
SET #Index = #Index + 1
END
SELECT * FROM #Output
I see no reason why this wouldn't work:
DECLARE #RunDate DATETIME = GETDATE()
SELECT WeekStart, COUNT(*)
FROM Contracts c
INNER JOIN dbo.[GetWeekStart](#RunDate, -5, 'MON')
ON c.StartDate < DATEADD(WEEK, 1, WeekStart)
AND (c.EndDate IS NULL OR c.EndDate >= #WeekStart)
GROUP BY WeekStart
I am not sure how you are generating your dates within your function, just in case you are using a loop/recursive CTE I'll include a query that doesn't use loops/cursors etc.
DECLARE #RunDate DATETIME = GETDATE()
-- SET DATEFIRST AS 1 TO ENSURE MONDAY IS THE FIRST DAY OF THE WEEK
-- CHANGE THIS TO SIMULATE CHANGING YOUR WEEKDAY INPUT TO db
SET DATEFIRST 1
-- SET RUN DATE TO BE THE START OF THE WEEK
SET #RunDate = CAST(DATEADD(DAY, 1 - DATEPART(WEEKDAY, #RunDate), #RunDate) AS DATE)
;WITH Weeks AS
( SELECT TOP 5 -- CHANGE THIS TO CHANGE THE WEEKS TO RUN
DATEADD(WEEK, 1 - ROW_NUMBER() OVER(ORDER BY Object_ID), #RunDate) [WeekStart]
FROM sys.All_Objects
)
SELECT WeekStart, COUNT(*)
FROM Contracts c
INNER JOIN Weeks
ON c.StartDate < DATEADD(WEEK, 1, WeekStart)
AND (c.EndDate IS NULL OR c.EndDate >= #WeekStart)
GROUP BY WeekStart
Did this quick but it should work
/*CTE generates Start & End Dates for 5 weeks
Start Date = Sunday of week # midnight
End Date = Sunday of next week # midnight
*/
WITH weeks
AS ( SELECT DATEADD(ww, -4,
CAST(FLOOR(CAST(GETDATE() - ( DATEPART(dw,
GETDATE()) - 1 ) AS FLOAT)) AS DATETIME)) AS StartDate
UNION ALL
SELECT DATEADD(wk, 1, StartDate)
FROM weeks
WHERE DATEADD(wk, 1, StartDate) <= GETDATE()
)
SELECT w.StartDate ,
COUNT(*) AS OpenContracts
FROM dbo.Contracts c
LEFT JOIN weeks w ON c.StartDate < DATEADD(d, 7, w.StartDate)
AND ISNULL(c.EndDate, GETDATE()) >= w.StartDate
GROUP BY w.StartDate

t-sql select get all Months within a range of years

I need a select to return Month and year Within a specified date range where I would input the start year and month and the select would return month and year from the date I input till today.
I know I can do this in a loop but I was wondering if it is possible to do this in a series selects?
Year Month
---- -----
2010 1
2010 2
2010 3
2010 4
2010 5
2010 6
2010 7
and so on.
Gosh folks... using a "counting recursive CTE" or "rCTE" is as bad or worse than using a loop. Please see the following article for why I say that.
http://www.sqlservercentral.com/articles/T-SQL/74118/
Here's one way to do it without any RBAR including the "hidden RBAR" of a counting rCTE.
--===== Declare and preset some obviously named variables
DECLARE #StartDate DATETIME,
#EndDate DATETIME
;
SELECT #StartDate = '2010-01-14', --We'll get the month for both of these
#EndDate = '2020-12-05' --dates and everything in between
;
WITH
cteDates AS
(--==== Creates a "Tally Table" structure for months to add to start date
-- calulated by the difference in months between the start and end date.
-- Then adds those numbers to the start of the month of the start date.
SELECT TOP (DATEDIFF(mm,#StartDate,#EndDate) + 1)
MonthDate = DATEADD(mm,DATEDIFF(mm,0,#StartDate)
+ (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1),0)
FROM sys.all_columns ac1
CROSS JOIN sys.all_columns ac2
)
--===== Slice each "whole month" date into the desired display values.
SELECT [Year] = YEAR(MonthDate),
[Month] = MONTH(MonthDate)
FROM cteDates
;
I know this is an old question, but I'm mildly horrified at the complexity of some of the answers. Using a CTE is definitely the simplest way to go for selecting these values:
WITH months(dt) AS
(SELECT getdate() dt
UNION ALL
SELECT dateadd(month, -1, dt)
FROM months)
SELECT
top (datediff(month, '2017-07-01' /* start date */, getdate()) + 1)
YEAR(months.dt) yr, MONTH(months.dt) mnth
FROM months
OPTION (maxrecursion 0);
Just slap in whichever start date you'd like in place of the '2017-07-01' above and you're good to go with an efficient and easily-integrated solution.
Edit: Jeff Moden's answer quite effectively advocates against using rCTEs. However, in this case it appears to be a case of premature optimization - we're talking about 10's of records in all likelihood, and even if you span back to 1900 from today, it's still a minuscule hit. Using rCTEs to achieve code maintainability seems to be worth the trade if the expected result set is small.
You can use something like this: Link
To generate the equivalent of a numbers table using date ranges.
But could you please clarify your inputs and outputs?
Do you want to input a start date, for example, '2010-5-1' and end date, for example, '2010-8-1' and have it return every month between the two? Do you want to include the start month and end month, or exclude them?
Here's some code that I wrote that will quickly generate an inclusive result of every month between two dates.
--Inputs here:
DECLARE #StartDate datetime;
DECLARE #EndDate datetime;
SET #StartDate = '2010-1-5 5:00PM';
SET #EndDate = GETDATE();
--Procedure here:
WITH RecursiveRowGenerator (Row#, Iteration) AS (
SELECT 1, 1
UNION ALL
SELECT Row# + Iteration, Iteration * 2
FROM RecursiveRowGenerator
WHERE Iteration * 2 < CEILING(SQRT(DATEDIFF(MONTH, #StartDate, #EndDate)+1))
UNION ALL
SELECT Row# + (Iteration * 2), Iteration * 2
FROM RecursiveRowGenerator
WHERE Iteration * 2 < CEILING(SQRT(DATEDIFF(MONTH, #StartDate, #EndDate)+1))
)
, SqrtNRows AS (
SELECT *
FROM RecursiveRowGenerator
UNION ALL
SELECT 0, 0
)
SELECT TOP(DATEDIFF(MONTH, #StartDate, #EndDate)+1)
DATEADD(month, DATEDIFF(month, 0, #StartDate) + A.Row# * POWER(2,CEILING(LOG(SQRT(DATEDIFF(MONTH, #StartDate, #EndDate)+1))/LOG(2))) + B.Row#, 0) Row#
FROM SqrtNRows A, SqrtNRows B
ORDER BY A.Row#, B.Row#;
Code below generates the values for the range between 21 Jul 2013 and 15 Jan 2014.
I usually use it in SSRS reports for generating lookup values for the Month parameter.
declare
#from date = '20130721',
#to date = '20140115';
with m as (
select * from (values ('Jan', '01'), ('Feb', '02'),('Mar', '03'),('Apr', '04'),('May', '05'),('Jun', '06'),('Jul', '07'),('Aug', '08'),('Sep', '09'),('Oct', '10'),('Nov', '11'),('Dec', '12')) as t(v, c)),
y as (select cast(YEAR(getdate()) as nvarchar(4)) [v] union all select cast(YEAR(getdate())-1 as nvarchar(4)))
select m.v + ' ' + y.v [value_field], y.v + m.c [label_field]
from m
cross join y
where y.v + m.c between left(convert(nvarchar, #from, 112),6) and left(convert(nvarchar, #to, 112),6)
order by y.v + m.c desc
Results:
value_field label_field
---------------------------
Jan 2014 201401
Dec 2013 201312
Nov 2013 201311
Oct 2013 201310
Sep 2013 201309
Aug 2013 201308
Jul 2013 201307
you can do the following
SELECT DISTINCT YEAR(myDate) as [Year], MONTH(myDate) as [Month]
FROM myTable
WHERE <<appropriate criteria>>
ORDER BY [Year], [Month]
---Here is a version that gets the month end dates typically used for accounting purposes
DECLARE #StartDate datetime;
DECLARE #EndDate datetime;
SET #StartDate = '2010-1-1';
SET #EndDate = '2020-12-31';
--Procedure here:
WITH RecursiveRowGenerator (Row#, Iteration)
AS ( SELECT 1, 1
UNION ALL
SELECT Row# + Iteration, Iteration * 2
FROM RecursiveRowGenerator
WHERE Iteration * 2 < CEILING(SQRT(DATEDIFF(MONTH, #StartDate, #EndDate)+1))
UNION ALL SELECT Row# + (Iteration * 2), Iteration * 2
FROM RecursiveRowGenerator
WHERE Iteration * 2 < CEILING(SQRT(DATEDIFF(MONTH, #StartDate, #EndDate)+1)) )
, SqrtNRows AS ( SELECT * FROM RecursiveRowGenerator
UNION ALL SELECT 0, 0 )
SELECT TOP(DATEDIFF(MONTH, #StartDate, #EndDate)+1)
DateAdd(d,-1,DateAdd(m,1, DATEADD(month, DATEDIFF(month, 0, #StartDate) + A.Row# * POWER(2,CEILING(LOG(SQRT(DATEDIFF(MONTH, #StartDate, #EndDate)+1))/LOG(2))) + B.Row#, 0) ))
Row# FROM SqrtNRows A, SqrtNRows B ORDER BY A.Row#, B.Row#;
DECLARE #Date1 DATE
DECLARE #Date2 DATE
SET #Date1 = '20130401'
SET #Date2 = DATEADD(MONTH, 83, #Date1)
SELECT DATENAME(MONTH, #Date1) "Month", MONTH(#Date1) "Month Number", YEAR(#Date1) "Year"
INTO #Month
WHILE (#Date1 < #Date2)
BEGIN
SET #Date1 = DATEADD(MONTH, 1, #Date1)
INSERT INTO #Month
SELECT DATENAME(MONTH, #Date1) "Month", MONTH(#Date1) "Month Number", YEAR(#Date1) "Year"
END
SELECT * FROM #Month
ORDER BY [Year], [Month Number]
DROP TABLE #Month
declare #date1 datetime,
#date2 datetime,
#date datetime,
#month integer,
#nm_bulan varchar(20)
create table #month_tmp
( bulan integer null, keterangan varchar(20) null )
select #date1 = '2000-01-01',
#date2 = '2000-12-31'
select #month = month(#date1)
while (#month < 13)
Begin
IF #month = 1
Begin
SELECT #date = CAST( CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,0,#date1))-1),DATEADD(mm,0,#date1)),111) + ' 00:00:00' as DATETIME )
End
ELSE
Begin
SELECT #date = CAST( CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,#month -1,#date1))-1),DATEADD(mm,#month -1,#date1)),111) + ' 00:00:00' as DATETIME )
End
select #nm_bulan = DATENAME(MM, #date)
insert into #month_tmp
select #month as nilai, #nm_bulan as nama
select #month = #month + 1
End
select * from #month_tmp
drop table #month_tmp
go