months between two dates in sql server with starting and end date of each of them in sql server - sql

i want to get months between two dates with their starting and end dates.Suppose if i enter startdate as "2017-04-01" and enddate as "2017-07-31", i want list of months i.e April,May,June,July with their starting and end date respectively.Kindly suggest me how it can be achieved.

One method is a recursive CTE:
with cte as (
select dateadd(day, 1 - day(#startdate), #startdate) as som,
eomonth(#startdate) as eom
union all
select dateadd(month, 1, som), eomonth(dateadd(month, 1, som))
from cte
where dateadd(month, 1, som) < #enddate
)
select *
from cte;
If you want the name of the month, then you can use datename(month, som).

Without recursion, using master.dbo.spt_values as a substitute for a numbers table:
declare #StartDate date = '20170401'
, #EndDate date = '20170731';
;with Months as (
select top (datediff(month,#startdate,#enddate)+1)
[Month] = dateadd(month, row_number() over (order by number) -1, #StartDate)
, MonthEnd = dateadd(day,-1,dateadd(month, row_number() over (order by number), #StartDate))
from master.dbo.spt_values
order by [Month]
)
select * from Months;
rextester demo: http://rextester.com/FXQJ4048
returns:
+------------+------------+
| Month | MonthEnd |
+------------+------------+
| 2017-04-01 | 2017-04-30 |
| 2017-05-01 | 2017-05-31 |
| 2017-06-01 | 2017-06-30 |
| 2017-07-01 | 2017-07-31 |
+------------+------------+
When generating a set or sequence in SQL Server, methods that avoid recursion and loops perform significantly better as the number of values increases.
Reference:
Generate a set or sequence without loops - 1 - Aaron Bertrand
Generate a set or sequence without loops - 2 - Aaron Bertrand
Generate a set or sequence without loops - 3 - Aaron Bertrand
To get the start and end dates of each month within a given range, when the value of the #StartDate parameter is not the first day of the month:
The first option is to truncate the #StartDate parameter to the first of the month, the second option is to adjust the expressions in the common table expression to truncate the values there:
declare #StartDate date = '20170415'
, #EndDate date = '20170715';
/* Option 1: truncate #StartDate to the beginning of the month */
--set #StartDate = dateadd(month, datediff(month, 0, #StartDate), 0);
/* Option 2: Truncate #StartDate to month in the common table expression: */
;with Months as (
select top (datediff(month,#StartDate,#EndDate)+1)
[Month] = dateadd(month
, datediff(month, 0, #StartDate) + row_number() over (order by number) -1
, 0)
, MonthEnd = dateadd(day,-1,dateadd(month
, datediff(month, 0, #StartDate) + row_number() over (order by number)
,0))
from master.dbo.spt_values
order by [Month]
)
select * from Months;

Here you go...
created the schema
create table abc(
date1 date
)
//Inserting data into it
insert into abc values(getdate()),
(DATEADD(Month, -1, getdate())),
(DATEADD(Month, -2, getdate())),
(DATEADD(Month, -3, getdate())),
(DATEADD(Month, -4, getdate()))
and finally the Select Query to fetch the data between Start date and end date:
select (datename(Month, date1)+' '+convert(varchar(2), date1, 103)) as [Date] from abc
where convert(varchar(10), date1, 120) between '2017-05-02' and '2017-07-02'
Another approach to fetch the between two dates data:
select (datename(Month, date1)+' '+convert(varchar(2), date1, 103)) as [Date] from abc
where date1 >= (DATEADD(Month, -3, getdate())) AND date1 <=getdate();
And the returned result is:
this is the Fiddle where you can test this query -> SQL FIDDLE
Simple and easy...good luck bro :)

Try this:
DECLARE #Start DATE ='2017-04-01',
#End DATE ='2017-07-31'
SELECT *, Datename(mm, date),
Dateadd(mm, Datediff(mm, 0, date), 0) AS FirstDateOfMonth,
Dateadd (dd, -1, Dateadd(mm, Datediff(mm, 0, date) + 1, 0)) as
LastDateOfMonth
FROM dbo.TableName
WHERE Cast(date AS DATE) BETWEEN #Start AND #End

If this is not just a one-off report, then I would create a calendar table, and use that to "group by". This will also let you do many other date related calculations.
You can find one at simple calendar or one here at Stackoverflow
Then your code could look like this:
SELECT top 10000 * FROM dbo.calendar DD
WHERE DD.TimeStampFrom>='2017-04-01' AND DD.TimeStampFrom <='2017-07-31'
AND DAY(DD.TimeStampFrom)=1

I created a stored procedure for that, may be you can convert that into user defined Function.
Posting that code below,
create procedure ListMonths
#date1 date,#date2 date
as
begin
create Table #tempTable
(mnth varchar(10))
while #date1<#date2
begin
insert into #tempTable
select DATENAME(month,#date1)
set #date1 = DATEADD(MONTH,1,#date1)
end
select * from #tempTable;
drop table #tempTable;
end
To execute the stored procedure:
exec ListMonths '2017-04-01','2018-01-31'
output
+------------+
| mnth |
+------------+
| April |
| May |
| June |
| July |
| August |
| September |
| October |
| November |
| December |
| January |
+------------+
result

Related

How to get week date (7days interval) from SQL Server

I have a table tbl_Customer_Reg and columns are
| ID | Cust_Name | Registration_Date
+------+-----------+------------------
| 1001 | Mr. ABC | 2021-03-13
| 1002 | Mr. MNO | 2021-02-03
| 1003 | Mr. QWE | 2021-04-12
| 1004 | Mr. XYZ | 2021-01-17
Customers are subscribed to a weekly plan. That means every week they get a message and renew every week according to Registration_Date. That means Mr. XYZ renew every week 17,24,31 January 2021 and so on.
Now I want to know the current date (2021-08-20) or before what is the week date of Mr. XYZ.
I think you should create table-function (use cross apply in query) which used cursor or CTE or while.
I write a simple with CTE(maxrecursion 200). You can try this:
CREATE OR ALTER FUNCTION dbo.fnGetWeeklyDateWithDate(#date date)
RETURNS #Result TABLE(WeekDate date, Lev int)
AS
begin
;WITH CTE
AS
(
SELECT #date WeekDate, 1 Lev
UNION ALL
SELECT DATEADD( Week, 1, WeekDate), Lev + 1
FROM CTE c
WHERE DATEADD( Week, 1, WeekDate) <= CAST(GETDATE() as date)
AND Lev < 200
)
INSERT INTO #Result
SELECT WeekDate, Lev
FROM CTE
OPTION (maxrecursion 200)
RETURN
END
result:
A little bit of date arithmetic should do the trick:
SELECT *,
LastRenewal = DATEADD(day, DATEDIFF(day, GETDATE(), Registration_Date) % 7, CAST(GETDATE() AS date)),
NextRenewal = DATEADD(day, DATEDIFF(day, GETDATE(), Registration_Date) % 7 + 7, CAST(GETDATE() AS date))
FROM YourTable;

calculate start date and end date from given quarter SQL

I want to get :
startdate and enddate from a given quarter from between dates
example :
range of dates : 2016-01-01 - 2016-12-31
1 (quarter) - will give me :
start date
2016-01-01
enddate
2016-03-31
2 (quarter) - will give me :
start date
2016-04-01
enddate
2016-06-30
and so on
I made it for only Quarter name and Year, modified it as your need
-- You may need to extend the range of the virtual tally table.
SELECT [QuarterName] = 'Q' + DATENAME(qq,DATEADD(QQ,n,startdate)) + ' ' + CAST(YEAR(DATEADD(QQ,n,startdate)) AS VARCHAR(4))
FROM (SELECT startdate = '01/Jan/2016', enddate = '31/DEC/2016') d
CROSS APPLY (
SELECT TOP(1+DATEDIFF(QQ,startdate,enddate)) n
FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) rc(n)
) x
Check below logic to get your answer.
DECLARE #Year DATE = convert(varchar(20),datepart(YEAR,getdate()))+'-01'+'-01'
DECLARE #Quarter INT = 4
SELECT DATEADD(QUARTER, #Quarter - 1, #Year) ,
DATEADD(DAY, -1, DATEADD(QUARTER, #Quarter, #Year))
SELECT DATEADD(QUARTER, d.q, DATEADD(YEAR, DATEDIFF(YEAR, 0,GETDATE()), 0))
AS FromDate,
DATEADD(QUARTER, d.q + 1, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), -1))
AS ToDate
FROM (
SELECT 0 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3
) AS d(q)

Displaying start date of week in SQL

I have a SQL query that to return the number of items per week. I have a query that returns so far this:
Number of Items | Week Number
-------------------------------
100 | 18
80 | 19
120 | 20
And would like to return the following:
Number of Items | Week Beginning
-------------------------------
100 | 1st May 2017
80 | 8th May 2017
120 | 15th May 2017
What I have so far is:
SELECT COUNT(*) AS 'Number of Items', DATEPART(WEEK, Date) FROM table
where DATEPART(Year, Date) = '2017' and DATEPART(MONTH, Date) = 5
group by DATEPART(WEEK, Date)
You are talking about the 1st day of the current week:
example: select FORMAT(dateadd(ww,datediff(ww,0,getdate()),0),'dd MMM yyyy')--if you are using SQL 2012+
answer:
SELECT COUNT(*) AS 'Number of Items', FORMAT(dateadd(ww,datediff(ww,0,date_column),0),'dd MMM yyyy')
FROM table
where DATEPART(Year, Date) = '2017' and DATEPART(MONTH, Date) = 5
group by DATEPART(WEEK, Date)
As you need Monday to be the first day of the week
select DATEPART(WEEK, MyDate),DATEADD(DAY,1,(DATEADD(DAY, 1-DATEPART(WEEKDAY, MyDate), MyDate)))
from (
select '5/3/2017' MyDate
union all select '5/10/2017'
union all select '5/14/2017')A
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Date) /7*7, 0) AS StartDateOfWeek
check this if it solves
DECLARE #WeekNum INT
, #YearNum char(4);
SELECT #WeekNum = 20
, #YearNum = 2017
-- 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;
thanks to http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=185440

Check same day in table

I have following data in my Attendance table:
Staff_ID | User_Attend_Date | Business_Day
S01 | 2013-05-01 15:18:45.537 | 2013-05-01
S01 | 2013-05-02 00:00:00.000 | 2013-05-01
S02 | 2013-05-03 06:20:30.225 | 2013-05-02
I want to display a new column to check whether the staff check-in date minus 3 hours is same day as business date.If same as business day,remain User_Attend_Date.My data type for Business_Day is DATE,other 2 column is DATETIME.The result should as below:
Staff_ID | User_Attend_Date | Business_Day | Check_Same_Day
S01 | 2013-05-01 15:18:45.537 | 2013-05-01 | 2013-05-01 15:18:45.537
S01 | 2013-05-02 00:00:00.000 | 2013-05-01 | 2013-05-01 15:18:45.537
S02 | 2013-05-03 06:20:30.225 | 2013-05-02 | 2013-05-03 06:20:30.225
I was try the following code,but fail to achieve the result.
SELECT *, Case WHEN DATEADD(hh, -3, User_Attend_Date)=Business_Day
THEN User_Attend_Date ELSE BusinessDay END As 'Check_Same_Day' FROM tblAttendance
I was try use over partition but fail too.
DATEADD(hh, -3, User_Attend_Date) OVER (PARTITION BY BusinessDay) AS 'Check_Same_Day'
Hope someone can teach me.Thanks
To change a datetime to the date it is on, use:
SELECT DATEADD(d, DATEDIFF(d, 0, GETDATE()), 0), GETDATE()
so in your case you coul use this to compare to Business_Day
SELECT Case WHEN DATEADD(d, DATEDIFF(d, 0, DATEADD(hour, -3, User_Attend_Date)), 0) = Business_Day
EDIT
You actually want to use the first login for any given day for a user, so you could use CTEs to achieve this. I have added an "expect" column (which you can easily remove), and I am assuming you must have a user_id to identify the users as well? If so then try:
http://sqlfiddle.com/#!3/c54b4/1
WITH AttendanceDate AS (
SELECT User_Id, User_Attend_Date, Business_Day, DATEADD(d, DATEDIFF(d, 0, DATEADD(hour, -3, User_Attend_Date)), 0) AS ActualAttendDate
FROM tblAttendance ),
FirstAttendance AS (
SELECT User_Id, ActualAttendDate, MIN(User_Attend_Date) FirstLogin
FROM AttendanceDate
GROUP BY User_Id, ActualAttendDate
)
SELECT TA.User_Id, TA.User_Attend_Date, TA.Business_Day, FA.FirstLogin
FROM AttendanceDate AS TA
INNER JOIN FirstAttendance AS FA
ON TA.User_Id = FA.User_Id
AND TA.ActualAttendDate = FA.ActualAttendDate
Note that the cast is a little superfluous now: http://sqlfiddle.com/#!3/c54b4/2, and to see it with multiple users: http://sqlfiddle.com/#!3/4f2c7/1. The final result, without Expect as a column is on http://sqlfiddle.com/#!3/e9e6c/1.
EDIT 2
And if you don't need the User_Id grouping, then its as simple as (http://sqlfiddle.com/#!3/0daab/1):
WITH AttendanceDate AS (
SELECT User_Attend_Date, Business_Day, DATEADD(d, DATEDIFF(d, 0, DATEADD(hour, -3, User_Attend_Date)), 0) AS ActualAttendDate
FROM tblAttendance ),
FirstAttendance AS (
SELECT ActualAttendDate, MIN(User_Attend_Date) FirstLogin
FROM AttendanceDate
GROUP BY ActualAttendDate
)
SELECT TA.User_Attend_Date, TA.Business_Day, FA.FirstLogin
FROM AttendanceDate AS TA
INNER JOIN FirstAttendance AS FA
ON TA.ActualAttendDate = FA.ActualAttendDate
Original answer (no longer relevant based on new information explaining how the expected results are put together)
In full (http://sqlfiddle.com/#!3/fa5bb/10):
SELECT User_Attend_Date, DATEADD(hour, -3, User_Attend_Date),
DATEADD(d, DATEDIFF(d, 0, DATEADD(hour, -3, User_Attend_Date)), 0) AS 'ForClarity1',
CAST(DATEADD(d, DATEDIFF(d, 0, DATEADD(hour, -3, User_Attend_Date)), 0) AS DATE) AS 'ForClarity2',
Business_Day AS 'ForClarity3',
Case WHEN
CAST(DATEADD(d, DATEDIFF(d, 0, DATEADD(hour, -3, User_Attend_Date)), 0) AS DATE) = Business_Day
THEN User_Attend_Date ELSE Business_Day END As 'Check_Same_Day'
FROM dbo.tblAttendance
I see two options:
1) DateDiff:
SELECT Case WHEN datediff(day,DATEADD(hh, -3, User_Attend_Date),Business_Day) < 1
THEN User_Attend_Date ELSE BusinessDay END As 'Check_Same_Day' FROM tblAttendance
2) Cast:
SELECT Case WHEN Cast(DATEADD(hh, -3, User_Attend_Date) as Date) = Business_Day
THEN User_Attend_Date ELSE BusinessDay END As 'Check_Same_Day' FROM tblAttendance
You are almost there, the issue is that you need to convert the result of the DATEADD to a DATE (instead of DATETIME):
DECLARE #att TABLE
(
User_Attend_Date DATETIME,
Business_Day DATE
)
INSERT INTO #att
SELECT '2013-05-01 15:18:45.537', '2013-05-01'
UNION ALL
SELECT '2013-05-02 00:00:00.000', '2013-05-01'
UNION ALL
SELECT '2013-05-03 06:20:30.225', '2013-05-02'
SELECT
User_Attend_Date,
Business_Day,
CASE
WHEN CAST(DATEADD(hh, -3, User_Attend_Date) AS DATE) = Business_Day THEN User_Attend_Date
ELSE Business_Day
END AS 'Check_Same_Day'
FROM #att
This gives the result you're wanting, showing that two of the items matched your rule.
You need to cast the first column(datetime) as date to compare with a date.
SELECT
*,
CASE WHEN CAST(DATEADD(HOUR,-4,User_Attend_Date) AS DATE)=Business_Day
THEN User_Attend_Date
ELSE Business_Day
END AS Check_Same_Day
FROM #test
I think it is displaying seconds values in row because ou have datatype DATE for Bussiness_day and datatype DATETIME for other column .
So It is possible to use DATE datatype in sql 2008 ?
SELECT USER_ATTEND_DATE,SUBSTRING(CAST(BUSINESS_DAY AS VARCHAR(20)),0,13)AS BUSINESS_DAY,
CASE
WHEN DATEADD(hh, -3, User_Attend_Date) = Business_Day
THEN Business_Day
ELSE DATEADD(hh, -3, User_Attend_Date)
END CHECK_SAME_DAY
FROM attendance

Get all dates in date range in SQL Server

I got this example from one StackOverflow question that was asked but I couldn't get it work according to my need.
WITH DateTable
AS
(
SELECT CAST('20110101' as Date) AS [DATE]
UNION ALL
SELECT DATEADD(dd, 1, [DATE]) FROM DateTable
WHERE DATEADD(dd, 1, [DATE]) < cast('20110131' as Date)
)
SELECT dt.[DATE] FROM [DateTable] dt
Input-
ID | FromDate | ToDate
=============================
1 | 2011-11-10 | 2011-11-12
2 | 2011-12-12 | 2011-12-14
Output -
SN | Dates |
==================
1 | 2011-11-10 |
2 | 2011-11-11 |
3 | 2011-11-12 |
4 | 2011-12-12 |
5 | 2011-12-13 |
6 | 2011-12-14 |
See this code works fine for static dates. But in my case I have a table containing three columns Id, FromDate, ToDate. Now I want to convert each range in the every row to individual dates.
I cannot get the above example to work in case if the range comes from the table and obviously this query has to run for every row in the range table, which is another confusing challenge.
Please help.
With a little help of a numbers table.
declare #T table
(
ID int identity primary key,
FromDate date,
ToDate date
)
insert into #T values
('2011-11-10', '2011-11-12'),
('2011-12-12', '2011-12-14')
select row_number() over(order by D.Dates) as SN,
D.Dates
from #T as T
inner join master..spt_values as N
on N.number between 0 and datediff(day, T.FromDate, T.ToDate)
cross apply (select dateadd(day, N.number, T.FromDate)) as D(Dates)
where N.type ='P'
Try on SE Data
create table Dates (Id int, FromDate date, ToDate date)
insert into Dates values (1, '2011-11-10', '2011-11-12')
insert into Dates values (2, '2011-12-12', '2011-12-14')
with DateTable as
(
select FromDate as Dt, ToDate
from Dates
union all
select DATEADD(D, 1, Dt), ToDate
from DateTable
where DATEADD(D, 1, Dt) <= ToDate
)
select ROW_NUMBER() over (order by Dt) as SN, Dt as Dates
from DateTable
order by Dt
What about this?
--DROP TABLE #Test
CREATE TABLE #Test(ID int, FromDate datetime, ToDate datetime)
INSERT INTO #Test VALUES (1, '2011-11-10', '2011-11-12')
INSERT INTO #Test VALUES (2, '2011-12-12', '2011-12-14')
;
WITH DateTable
AS
(
SELECT ID, FromDate, ToDate, 0 AS Seed FROM #Test
UNION ALL
SELECT ID, DATEADD(dd, 1, FromDate), ToDate, Seed + 1
FROM DateTable
WHERE DATEADD(dd, 1, FromDate) <= ToDate
)
SELECT --*
ROW_NUMBER() OVER (ORDER BY ID, Seed) SN, FromDate AS Dates
FROM DateTable