Reputation: 5414
Suppose that we have a table in SQL Server that stores shifts for doctors, so instead of storing explicit dates we are using weekdays, the table looks like this
ShiftId Day DoctorId FromTime ToTime
--------------------------------------------------
1 SUN 1 08:00:00 16:00:00
2 MON 1 09:00:00.00 14:00:00
3 TUE 1 09:00:00.00 15:00:00
4 WED 1 10:00:00.00 17:00:00
5 THU 1 13:00:00.00 18:00:00
I want to create a select statement to generate explicit dates by using data stored in this table
Example
suppose I want to generate dates between Sunday 19th February 2017
to Tuesday 28th February 2017
, the output should be like this
DoctorId Date Day FromTime ToTime
------------------------------------------------------------
1 '02-19-2017' SUN 08:00:00 16:00:00
1 '02-20-2017' MON 09:00:00 14:00:00
1 '02-21-2017' TUE 09:00:00 15:00:00
1 '02-22-2017' WED 10:00:00 17:00:00
1 '02-23-2017' THU 13:00:00 18:00:00
1 '02-26-2017' SUN 08:00:00 16:00:00
1 '02-27-2017' MON 09:00:00 14:00:00
1 '02-28-2017' TUE 09:00:00 15:00:00
Explanation
Times generated for these dates correspond to days stored in our table for example the time generated for '02-19-2017'
in the first row is 08:00:00
16:00:00
because '02-19-2017'
is Sunday, and the time generated for '02-20-2017'
in the second row is 09:00:00 14:00:00
because '02-20-2017'
is Monday and so on.
As you may notice no dates generated for 24th February FRI
and 25th February SAT
, because we don't store Friday
and Saturday
in our table
Can we write a query in T-SQL that returns this result?
Upvotes: 3
Views: 94
Reputation: 6656
You can try this
DECLARE @STARTDate date = '02-19-2017', @ToDate date = '02-27-2017'
;WITH myCTE AS
(
SELECT @STARTDate AS MyDate, 1 AS iID
UNION ALL
SELECT DATEADD(DAY,1,MyDate), iID + 1
FROM myCTE
WHERE MyDate < @ToDate
)
SELECT * FROM YourTable
JOIN myCTE on myCTE.iID = YourTable.ID
Upvotes: 0
Reputation: 1269803
You should probably have a calendar table in your database. For this purpose, you can use recursive CTEs or a number table to almost the same effect:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master.spt_values
)
select dd.doctorid, d.dte, s.day, s.fromtime, s.totime
from (select dateadd(day, n.n, @startdate) as dte
from n
where dateadd(day, n.n, @startdate) <= @enddate
) d cross join
(select distinct doctorid from shifts
) dd join
shifts s
on upper(left(datename(wd, d.dte), 3)) = s.day and
ss.doctorid = d.doctorid;
This is a bit tricky. The idea is to generate the dates (which is pretty easy using the number table). Then do a cross join
with the doctors, so all doctors have all dates. Finally, join back to the shifts
table, to get the matching records (if any) and the appropriate times.
Note that this version will work for multiple doctors.
Upvotes: 0
Reputation: 419
I think that using SQL for this kind of task is a bad idea due to using a lot of logical operations. Using a C# function should be more efficient.
Using SQL or C# would be the same logical solution:
1 - Create a temporary table that will keep your result.
2 - Loop over your date from starting date to end date. For each date get the DAY part. From the DAY part, get your field FromTime and ToTime. Insert the result on your temporary table.
3 - Return the temporary table (Or Select and destroy it).
Upvotes: 0
Reputation: 7392
I would use your From/To input parameters to generate a calendar on the fly in a recursive CTE, then join that to your shift table based on the weekday value.
DECLARE @Shift TABLE (ShiftID INT, [Day] VARCHAR(3), DoctorId INT, FromTime TIME, ToTime TIME)
INSERT INTO @Shift
VALUES (1,'SUN',1,'08:00:00','16:00:00'),
(2,'MON',1,'09:00:00.00','14:00:00'),
(3,'TUE',1,'09:00:00.00','15:00:00'),
(4,'WED',1,'10:00:00.00','17:00:00'),
(5,'THU',1,'13:00:00.00','18:00:00')
DECLARE @D1 DATE, @D2 DATE
SET @D1 = '2017-02-19'
SET @D2 = '2017-02-28'
;WITH Calendar AS
(
SELECT @D1 AS [DateVal], LEFT(DATENAME(WEEKDAY,@D1),3) AS [DWName]
UNION ALL
SELECT DATEADD(DAY,1,DateVal), LEFT(DATENAME(WEEKDAY,DATEADD(DAY,1,DateVal)),3)
FROM Calendar
WHERE DateVal<@D2
)
SELECT S.DoctorId, C.DateVal, S.[Day], S.FromTime, S.ToTime
FROM @Shift S
JOIN Calendar C ON S.[Day]=C.DWName
Upvotes: 2
Reputation: 11195
use a calendar CTE, join to it
with DateCTE as
(
select @DateFrom as Date1
union
select dateadd('DD',1,Date1)
from DateCTE
where Date1 < @DateTo
)
select D1.*,
datename('DW',Date1) as Day
FromTime,
ToTime
from DateCTE D1
left join MyTable M2
on datename('DW',Date1) like M2.Day
where datename('DW',Date1) not like 'Fri%'
and datename('DW',Date1) not like 'Sat%'
Upvotes: 0