Select Between two dates in sql - sql

We are facing problem when we'r trying to select records from a table between two dates.
Table structure/datatypes:
Slno (int, notnull, IsIdentity(yes))
Name (string(50))
StartDate (datetime)
EndDate (datetime)
Sample Data:
Slno Name StartDate EndDate
1 ABC 2017-02-17 00:00:00.000 2017-02-25 00:00:00.000
2 ABD 2017-02-15 00:00:00.000 2017-02-25 00:00:00.000
3 ABD 2017-02-17 00:00:00.000 2017-02-17 00:00:00.000
4 ABD 2017-02-14 00:00:00.000 2017-02-18 00:00:00.000
5 ABD 2017-02-17 00:00:00.000 2017-02-20 00:00:00.000
We tried running the below sql query:
select * from dbo.XYZ where (getdate() between StartDate and EndDate)
The output is:
Slno Name StartDate EndDate
1 ABC 2017-02-17 2017-02-25
2 ABD 2017-02-17 2017-02-20
We are not getting other three rows. What am I doing wrong?

SELECT *
FROM dbo.Table
WHERE EndDate >= GETDATE()
  AND StartDate < DATEADD(DAY, 1, GETDATE());
This will use your defined indexes as much as possible.
See bad habits to kick mishandling date range queries (Aaron Bertrand) for more on this.

Try this one, but as Todd commented why time is not included in output if data type is datetime?
SELECT *
FROM dbo.XYZ
WHERE StartDate <= GETDATE()
AND EndDate >= GETDATE()
EDIT:
Looks like you are storing date, not datetime, so try below:
SELECT *
FROM dbo.XYZ
WHERE StartDate <= CAST(GETDATE() AS DATE)
AND EndDate >= CAST(GETDATE() AS DATE)
EDIT:
Not really sure how else I can help, are you on SQL Server, Oracle etc? Below script will show you how date and datetime data types look like (SQL Server), and this is what confusing me the most as you are claiming your data type is datetime but output looks like date.
SELECT
cast(GETDATE() as datetime),
cast(GETDATE() as date)

Getdate() means currentdate and time. So the above query return 2 rows.
You can refer this query:
declare #minDate datetime
declare #maxDate datetime
select #minDate = MIN(StartDate) from dbo.XYZ
select #maxDate = MAX(EndDate) from dbo.XYZ
select * from dbo.XYZ where #minDate<= GetDate() OR #maxDate >= GetDate().
It will return all rows.

Related

Convert Single column list of dates into Multiple Date Range in SQL Server

I had a business requirement which i need to convert the list dates into date range
Logic will be to create Date range upto continuous date if date is not in continuity then new range
will be created
Below is the sample table
ID Module Employeeid Date
--------------------------------------------
11 M1 9 2019-10-01 00:00:00.000
12 M1 9 2019-10-02 00:00:00.000
13 M1 9 2019-10-03 00:00:00.000
14 M2 9 2019-10-04 00:00:00.000
15 M2 9 2019-10-05 00:00:00.000
16 M2 9 2019-10-08 00:00:00.000
17 M2 9 2019-10-09 00:00:00.000
Requried Output
Module Employeeid Start Date End Date
---------------------------------------------------
M1 9 2019-10-01 2019-10-03
M1 9 2019-10-04 2019-10-05
M1 9 2019-10-08 2019-10-09
Below is the query which i have tried and it is working but we need to add from date and to date filter
so after adding filter OutPut is not proper
WITH mycte
AS (SELECT *,
Dateadd(day, -Row_number()
OVER (
partition BY [[module]
ORDER BY [Date]), [Date]) AS grp
FROM [to_shiftschedule]
WHERE employeeid = 535
)
SELECT Min([Date]) AS[StartDate],
Max([Date]) AS[EndDate],
[employeeid],
[module]
FROM mycte
where CONVERT(VARCHAR, Date, 103) >= CONVERT(VARCHAR, '09/01/2020', 103)
AND CONVERT(VARCHAR, Date, 103) <= CONVERT(VARCHAR, '23/01/2020', 103)
GROUP BY[employeeid], [module], grp
ORDER BY[startdate] DESC;
If the problem is with date filter, which is this section, I assume
where CONVERT(VARCHAR, Date, 103) >= CONVERT(VARCHAR, '09/01/2020', 103)
AND CONVERT(VARCHAR, Date, 103) <= CONVERT(VARCHAR, '23/01/2020', 103)
, the reason might be in comparing varchars instead of plain dates.
I suggest you use the following condition:
where Date >= '2020-01-09' and Date <= '2020-01-23'
By the way, I also advise you to edit your final SQL clause, because it is unclear which column you named as "Date".
Please let me know whether it helped or not.

Find all instances of a specific day of the week between two dates in SQL

I'm trying to find all instances of a day of a week between two given dates. The day of the week can change. I've seen a similar question posted on here, but it doesn't seem to work for variables
SET DATEFIRST 1;
DECLARE #earliestStartDate DATETIME = '2016-08-01 00:00:00.000';
DECLARE #latestStartDate DATETIME = '2016-09-30 00:00:00.000';
DECLARE #weeklyCoursesStartDay INT = 1;
DECLARE #maxCourses INT = 30;
CREATE TABLE #Dates(CourseDate DATETIME);
WITH CTE(dt)
AS
(SELECT #earliestStartDate
UNION ALL
SELECT DATEADD(day, #weeklyCoursesStartDay, dt) FROM CTE--SELECT DATEADD(day, #weeklyCoursesStartDay, dt) FROM CTE
WHERE dt < #latestStartDate)
INSERT INTO #Dates(CourseDate)
SELECT TOP(#maxCourses) dt
FROM CTE
WHERE DATEPART(DW, dt) = #weeklyCoursesStartDay
OPTION (MAXRECURSION 0);
SELECT * FROM #Dates
DROP TABLE #Dates
This returns the below, as expected.
2016-08-01 00:00:00.000
2016-08-08 00:00:00.000
2016-08-15 00:00:00.000
2016-08-22 00:00:00.000
2016-08-29 00:00:00.000
2016-09-05 00:00:00.000
2016-09-12 00:00:00.000
2016-09-19 00:00:00.000
2016-09-26 00:00:00.000
But when the #weeklyCourseStartDay is changed to say, 5 (indicating a friday) it only returns two results:
2016-08-26 00:00:00.000
2016-09-30 00:00:00.000
The line
SELECT DATEADD(day, #weeklyCoursesStartDay, dt) FROM CTE
should actually be:
SELECT DATEADD(day, 1, dt) FROM CTE
This means that the recursive CTE will generate every day between #earliestStartDate and #latestStartDate, and, at a later stage, the WHERE DATEPART(DW, dt) = #weeklyCoursesStartDay clause will make sure only the days with the correct week day will remain.

SQL - creating a list of custom dates between two dates

I am having trouble compiling a query than can do the following:
I have a table which has a startDate and endDate [tblPayments]
I have a column which stores a specific paymentDay [tblPayments]
Data
paymentID startDate endDate paymentDay
1 2016-01-01 2016-12-31 25
2 2015-06-01 2016-06-30 16
I am trying to generate a SELECT query which will split this specific table into separate lines based on the amount of months between these two dates, and set the paymentDay as the day for these queries
Example Output
paymentID expectedDate
1 2016-01-25
1 2016-02-25
1 2016-03-25
1 2016-04-25
1 2016-05-25
1 2016-06-25
1 2016-07-25
1 2016-08-25
1 2016-09-25
1 2016-10-25
1 2016-11-25
1 2016-12-25
2 2015-06-16
2 2015-07-16
2 2015-08-16
2 2015-09-16
2 2015-10-16
2 2015-11-16
2 2015-12-16
2 2016-01-16
2 2016-02-16
2 2016-03-16
2 2016-04-16
2 2016-05-16
I have found a query which will select the months between these dates but its adapting it to my table above, and multiple startDates and endDates I am struggling with
spliting the months
declare #start DATE = '2015-01-01'
declare #end DATE = '2015-12-31'
;with months (date)
AS
(
SELECT #start
UNION ALL
SELECT DATEADD(MM,1,date)
from months
where DATEADD(MM,1,date)<=#end
)
select Datename(MM,date) from months
This query is limited to just one startDate and endDate, so I haven't expanded it to change the DAY of the date.
Use a date table and a simple inner join
DECLARE #tblPayments table (paymentID int identity(1,1), startDate date, endDate date, paymentDay int)
INSERT #tblPayments VALUES
('2016-01-01', '2016-12-31', 25),
('2015-06-01', '2016-06-30', 16)
;WITH dates AS -- Build date within the range of startDate and endDate
(
SELECT MIN(startDate) AS Value, MAX(endDate) AS MaxDate FROM #tblPayments
UNION ALL
SELECT DATEADD(DAY, 1, Value), MaxDate
FROM dates WHERE DATEADD(DAY, 1, Value) <= MaxDate
)
SELECT pay.paymentID, dates.Value AS expectedDate
FROM
#tblPayments pay
INNER JOIN dates ON
dates.Value BETWEEN pay.startDate AND pay.endDate
AND DAY(dates.Value) = paymentDay
OPTION (maxrecursion 0)
I would create an in memory calendar table and then perform a simple query by joining to that:
-- Create a table with all the dates between the min and max dates in the
-- data table
DECLARE #Calendar TABLE
(
[CalendarDate] DATETIME
)
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SELECT #StartDate = MIN(startdate), #EndDate = MAX(enddate) FROM YourDataTable
WHILE #StartDate <= #EndDate
BEGIN
INSERT INTO #Calendar (CalendarDate)
SELECT #StartDate
SET #StartDate = DATEADD(dd, 1, #StartDate)
END
-- Join to return only dates between the start and end date that match the Payment Day
SELECT D.PaymentId, C.CalendarDate FROM YourDataTable D
INNER JOIN #Calendar C ON C.CalendarDate BETWEEN D.StartDate AND D.EndDate
AND DATEPART(day, C.CalendarDate) = D.PaymentDay

Get weekStart and weekEnd dynamically?

I have spent so much time trying to figure out how this can be done but could not do it. So please help me out.
I have this data:
ID employee_id worked_date start_time finish_time
1 1 2013-09-25 09:00:00 17:30:00
2 1 2013-09-26 07:00:00 17:00:00
8 1 2013-10-01 09:00:00 17:00:00
9 1 2013-10-04 09:00:00 17:00:00
12 1 2013-10-07 09:00:00 17:00:00
13 1 2013-10-10 09:00:00 17:00:00
14 1 2013-10-11 09:00:00 17:00:00
My first day of the week is Wednesday. The base date is 2013-09-25 which is Wednesday. I need to be able to get the weekStart and weekEnd dynamically. For example, based on the data I have above, there are three weeks between 2013-09-25 to 2013-10-11. I order my data with the latest worked_date first. If the user requested for week3, then the weekStart should be 2013-09-25 and weekEnd should be 2013-10-01. If the user requested for week2, then the weekStart should be 2013-10-02 to 2013-10-08 and so on.
The parameter week will be passed dynamically. Thanks for your help.
Here is the result I want to achieve when the requested week is 3:
ID employee_id worked_date start_time finish_time weekStart weekEnd
1 1 2013-09-25 09:00:00 17:30:00 2013-09-25 2013-10-01
2 1 2013-09-26 07:00:00 17:00:00 2013-09-25 2013-10-01
8 1 2013-10-01 09:00:00 17:00:00 2013-09-25 2013-10-01
Using this query will give me the weekStart and weekEnd for each record
SELECT *, dateadd(week, datediff(day,'20000105',worked_date) / 7, '20000105') AS WeekStart ,
dateadd(week, datediff(day,'20000105',worked_date) / 7, '20000105') + 6 AS WeekEnd
FROM Timesheet
But I do not want this. I want only a particular week that the user requested.
It sounds like you might need a calendar table.
create table calendar (
calendarId int identity(1,1) primary key,
year int,
month int,
week int,
startDate date,
endDate date)
then you could say 'give me all the records for week 2'
declare #week int = 2
declare #year int = 2013
;with employeeCalendar as (
select
employee.employeeid
,startDate
,endDate
from
employee
cross apply
calendar
where
calendar.week = #week )
select
employeeCalendar.EmployeeId
,employeeShift.[date]
,start_time
,finish_time
,startDate
,endDate
from
employeeCalendar
left join
employeeShift
on employeeShift.employeeid = employeeCalendar.employeeid
and employeeShift.worked_date
between employeeCalendar.startDate and employeeCalendar.endDate
You could populate that table with a query like this:
;with calendarCte as (
select
1 as week
,convert(date,'2013-09-25') as startDate
,convert(date,'2013-10-01') as endDate
union all
select
week + 1
,dateadd(week,1,startDate)
,dateadd(week,1,endDate)
from
calendarCte
where
calendarCte.startDate < convert(date,'2043-09-25') )
insert into calendar( [year],[month],[week],startdate,stopdate)
select
datepart(year,startDate) as [year]
,datepart(month,startDate) as [month]
,week as [week]
,startDate
,endDate
from calendarCte option (maxrecursion 0)
You said you are looking for a stored procedure. Are you looking for something like this?
CREATE PROCEDURE ProcedureName #week int AS
SELECT * FROM Timesheet
WHERE worked_date >= dateadd(week, #week, '2013-09-25')
AND worked_date < dateadd(day,7,dateadd(week, #week, '2013-09-25'))
If you want your weekStart and weekEnd you could add them in to the SELECT as you did before.
If you want this in descending order from the current week backwards you could write it as follows.
CREATE PROCEDURE ProcedureName #week int AS
SELECT * FROM Timesheet
WHERE worked_date >= dateadd(week, -#week, '2013-10-16')
AND worked_date < dateadd(day,7,dateadd(week, -#week, '2013-10-16'))
You could also parameterize the Date you are passing into the stored procedure as the current week will always be changing.

Convert Date Range to Individual Days

A table called VolumeRequest stores the volume requests by accounts for a date range.
AccountId StartDate EndDate DailyVolume
670 2013-07-01 00:00:00.000 2013-07-31 00:00:00.000 10
670 2013-07-01 00:00:00.000 2013-07-31 00:00:00.000 1050
670 2013-07-10 00:00:00.000 2013-07-10 00:00:00.000 -350
670 2013-07-24 00:00:00.000 2013-07-26 00:00:00.000 -350
673 2013-06-01 00:00:00.000 2013-07-31 00:00:00.000 233
I need to display the requests on daily basis where volume is summed by day by account for a given date range like for month of July the report is like below. The date start and end dates of the volume requests need to be trimmed for the given report dates
AccountId Date Volume
670 2013-07-01 00:00:00.000 1060
670 2013-07-02 00:00:00.000 1060
.
.
670 2013-07-10 00:00:00.000 710
.
.
670 2013-07-24 00:00:00.000 710
670 2013-07-25 00:00:00.000 710
670 2013-07-26 00:00:00.000 710
.
.
670 2013-07-31 00:00:00.000 1060
673 2013-07-01 00:00:00.000 233
.
.
673 2013-07-31 00:00:00.000 233
Right now I am using table Variables and loops to achieve it which I know is not a good way to code.
DECLARE #sDate DATETIME, #eDate DATETIME , #volume DECIMAL (10, 4), rstartdate DATETIME, #renddate DATETIME , #loopcount INT
SET #sdate = '4/1/2013'
SET #edate = '4/30/2013'
DECLARE #VolumeRequest TABLE
(
ID INT IDENTITY (1, 1) PRIMARY KEY,
Aid INT,
Startdate DATETIME,
Enddate DATETIME,
volume DECIMAL (14, 4)
)
DECLARE #DailyRequest TABLE
(
ID INT IDENTITY (1, 1) PRIMARY KEY,
Accountid INT,
ReadDate DATETIME,
Volume DECIMAL (14, 4)
)
INSERT INTO #VolumeRequest
SELECT Accountid,
( CASE
WHEN #sdate > startdate THEN #sdate
ELSE startdate
END ),
( CASE
WHEN #edate < enddate THEN #edate
ELSE enddate
END ),
dailyvolume
FROM VolumeRequest
WHERE Startdate <= #edate
AND Enddate >= #sdate
AND isnull (deprecated, 0) != 1
--loop to breakdown the volume requests into daily requests
SET #loopcount = 1
WHILE #loopcount <= (SELECT MAX(ID)
FROM #VolumeRequest)
BEGIN
SELECT #volume = volume,
#rstartdate = Startdate,
#renddate = Enddate
FROM #VolumeRequest
WHERE ID = #loopcount
WHILE #rstartdate <= #renddate
BEGIN
INSERT INTO #DailyRequest
SELECT #currentaid,
#rstartdate,
#volume
SET #rstartdate = DATEADD(day, 1, #rstartdate)
END
SET #LoopCount = #LoopCount + 1
END
I am looking for ways which don't involve loops or cursors. I found a Similar Question. The answers there didn't help me.
I like to use a Dates table such as
CREATE TABLE #Dates(
DateId INT,
CalendarDate DATETIME)
filled with dates for whatever range you need. I use this table to join to tables such as VolumeRequest to retrieve the output you requested.
SELECT
v.AccountId,
d.CalendarDate,
SUM(v.DailyVolume)
FROM
#Dates d INNER JOIN
VolumeRequest v ON
d.CalendarDate >= v.StartDate AND
d.CalendarDate <= v.EndDate
group by
d.CalendarDate,
v.AccountId
to fill the #Dates table, I use something like this:
declare #startdate datetime = '6/1/13', #enddate datetime = '7/31/13'
create table #Dates(CalendarDate datetime)
insert into #Dates(CalendarDate)
select
dateadd(dd, rid-1, #startdate) as calendardate
from (
select
ROW_NUMBER() over(order by o.object_id) as rid
From
sys.objects o cross apply
sys.objects o2
) dates
where
dateadd(dd, rid-1, #startdate) >= #startdate and dateadd(dd, rid-1, #startdate) <= #enddate
Modify to meet your date range needs.
SQLFiddle demo
Using WITH clause and recursion we generate Days table with all days between MIN and MAX dates.
Then generate table Accounts with distinct AccountID.
Finally JOIN all these tables and group all with SUM.
WITH MINMAX as
( SELECT MIN(StartDate) as MinDate,
MAX(EndDate) as MaxDate
from T
),
DAYS as
( SELECT MinDate as D from MINMAX
UNION ALL
SELECT D+1 as D FROM DAYS WHERE D+1<=
(
SELECT MaxDate FROM MINMAX
)
),
Accounts as
(
select distinct AccountID from T
)
select A.AccountId,Days.D,sum(T.DailyVolume) from Days
CROSS JOIN Accounts A
JOIN T on A.AccountID=T.AccountID
AND
Days.D between T.StartDate and T.EndDate
GROUP BY A.AccountId,Days.D
ORDER BY A.AccountId,Days.D
OPTION (MAXRECURSION 10000)