April 29, 2005 at 9:26 pm
I have a problem I don't know if I can do it in SQL Server. I have an "Appointment" table that contains Name, StartDateTime and StopDateTime. Each row represents one appointment. When I want to schedule a new appointment, I want to find the next N available time slot. I think this is too complex to write in an sql statement. Is it possible to write a stored prod?
The function has a few parameters:
- DateTimeToStartSearching (e.g. I want to start searching today)
- NumberOfMinutesForNewAppointment (new appointment requires 30 minutes)
- NumberOfAvailableTimeSlotsToReturn (wants 10 options to be returned)
- EarliestTime (e.g. we want the appointment to be in the afternoon, then EarliestTime is 1pm and LatestTime is say 5pm.
- LatestTime
May 1, 2005 at 9:33 pm
You can write a stored procedure that accepts parameters. You could also use dynamic SQL and create your SQL statements on the fly. The are numerous articles on the pros and cons of using dynamic SQL on the web. BOL contains information on writing stored procedures.
The complexity of the SQL statements will be the same. As the only difference will be the manner in which you insert your variables into the SELECT statement.
Your business rules will have a major impact on how complex these statements have to be to meet your requirements.
For instance a rule that states that the start time for an appointment must be on the hour or ½ hour i.e. 9 or 9:30 is ok but 9:03 or 9:24 are not valid start times will require different logic than a rule that states appointment start time and length of the appointment will be set at when the appointment is set and customers must not exceed their time length. Then an appointment could be set at 9:03 and concluded at 9:38 leaving the next available start time at 9:39 (you have to give them a whole minute to get out the door or do you ).
A little more information would be helpful.
HTH
Mike
May 2, 2005 at 8:28 am
Thank you. I ended up writing a stored proc to use cursor to iterate thru the rows and process some logic. I think even if it can be done in 1 sql (doubtful 🙂 ), it would get so complex that it is hard to maintain. You have a good point about 9:03. I missed that ... yet another rule! 🙁
May 2, 2005 at 8:34 am
Can we see the cursor code... I'd like to give a shot at the set based approach.
May 2, 2005 at 11:04 am
Pretty sure you don't need a cursor for that!
* Noel
May 6, 2005 at 5:29 am
Edit Added code to remove Lunch times from appointment times..Never Ever tell the boss he can not take a lunch break.
Set based approach.
HTH
Mike
/*
Create a table with all possible appointments (#AppointmentTimes)
For for a production app consider
pinning this table in memory to speed up access see Remi Gregoire's post
http://www.sqlservercentral.com/forums/shwmessage.aspx?forumid=8&messageid=179284&p=2
Assumptions:
Appointments are for 30 min
Workday starts at 9:00 AM and ends at 5:00PM (First appointment starts at 9 Am
last Appointment starts at 4:30 PM)
Lunch is for 1 hour from 12:00 to 1:00 PM
Business is open Monday thru Friday (no holidays in this example)
No appointment will be made more than 1 year in advance
There is an employee table where EmployeeID is PK
There is a Customer Table where CustomerID is PK
AppointmentTimes Table is a lookup table containing all possible appointments
Maintenance:
Set Cascade updates, cascade deletes linked to AppointmentsSet
Develope a stored proc triggered on the first of the month
delete rows for previous month. insert new data (previous month + 1 year)
, delete holidays (will need a table containing holiday information)UPDATE
AppointmentsSet Table from AppointmentTimes
AppointmentsSet Table Contains:
EmployeeID FK to employee table
CustomerID FK to Customer Table a value of -1 indicates that no record exist for customer
AppointmentTime FK to AppointmentTimes
AppointmentSet Value of 0 indicates open
Value > 0 indicates appointment is set
Value < 0 indicates no appointment vacation or out of office
Used to track reasons employee was not in office
SETUP:
For each employee load AppointmentsSet Table with all possible appointments
4160 records per employee.
CHECKING FOR OPEN APPOINTMENTS LOGIC:
Select 2 full days worth of open appointments between the desired date
and 180 days (6 months) after the desired date
SELECT TOP 32 DateName(dw,b.Apt)AS "Day",
Convert(Char(17),B.Apt)AS "Open Dates"
FROM #AppointmentsSet AS a INNER join #appointmentTimes AS b
ON a.AppointmentTime = B.PKNumber
WHERE a.EmployeeID = @EmployeeID
AND b.Apt BETWEEN @DesiredDate AND DateAdd(dd,180,@DesiredDate)
AND a.AppointmentSet = 0
ADDING AN APPOINTMENT FOR AN EMPLOYEE
UPDATE #AppointmentsSet
SET AppointmentSet = 1
FROM #AppointmentsSet as a INNER JOIN #AppointmentTimes as b
ON a.AppointmentTime = b.PKNumber
WHERE a.EmployeeID = @EmployeeID
AND b.Apt =@DesiredDate
*/
CREATE TABLE [#AppointmentTimes]
(
[PkNumber] [int] IDENTITY (1, 1) NOT NULL ,
[Apt] [datetime]
)
CREATE TABLE [#AppointmentsSet]
(
[PkNumber]int NULL, --
[EmployeeID] int NOT NULL, --FK to sales person
[AppointmentTime]int NULL, --FK PKNumber from AppointmentTimes table
[AppointmentSet]int,--A Value of -1 = vacation value of 1 = appointment set
[CustomerID] int NULL --FK to customer data a 0 value indicates a new customer
)
GO
--Load AppointmentTimes Table
Declare @i int
Declare @OuterLoop int
Declare @Dummy DateTime
set @i = 0
set @OuterLoop = 0
set @Dummy = 'Jan 1, 2005 08:30 AM'
While @OuterLoop < 365
begin
while @i < 16
begin
set @i = @i+1
IF (DatePart(dw,@Dummy) >1 and DatePart(dw,@Dummy) < 7)
Begin
set @Dummy =DateAdd(mi,30,@dummy)
--add logic for lunch
IF(DatePart(hh,@Dummy) <> 12)
Begin
INSERT INTO #AppointmentTimes (Apt) values(@Dummy )
End
end
end
set @OuterLoop = @Outerloop +1
set @Dummy ='Jan 1, 2005 08:30 AM'
set @Dummy = DateAdd(dd,@OuterLoop,@Dummy)
set @i=0
end
go
--Add data for employee # 1 in AppointmentsSet Table
DECLARE @Count int
Set @Count=0
While @Count < 4160
Begin
set @Count = @Count + 1
INSERT INTO #AppointmentsSet (PKNumber,AppointmentTime,EmployeeID,AppointmentSet)
Values(@Count,@Count,1,0)
End
GO
--Test
DECLARE @DesiredDate DateTime
DECLARE @EmployeeID int
Set @DesiredDate ='1/5/2005 9:00AM'
Set @EmployeeID = 1
--Add some appointments for test
UPDATE #AppointmentsSet
SET AppointmentSet = 1
FROM #AppointmentsSet as a INNER JOIN #AppointmentTimes as b
ON a.AppointmentTime = b.PKNumber
WHERE a.EmployeeID = @EmployeeID
AND b.Apt BETWEEN '1/5/2005 10:00AM' AND '1/5/2005 11:30am'
--Check for an appointment
SELECT TOP 32 DateName(dw,b.Apt)AS "Day",
Convert(Char(17),B.Apt)AS "Open Dates"
FROM #AppointmentsSet AS a INNER join #appointmentTimes AS b
ON a.AppointmentTime = B.PKNumber
WHERE a.EmployeeID = @EmployeeID
AND b.Apt BETWEEN @DesiredDate AND DateAdd(dd,180,@DesiredDate)
AND a.AppointmentSet = 0
/*
--Result set
Wednesday Jan 5 2005 9:00
Wednesday Jan 5 2005 9:30
Wednesday Jan 5 2005 12:00
.....
*/
DROP TABLE #AppointmentTimes
DROP TABLE #AppointmentsSet
May 7, 2005 at 6:03 pm
Wow, this is wonderful but I need time to digest it. 🙂 Thanks everyone for your input. Special thanks for Michael Du Bois for your thorough and well documented solution. This sure is very helpful
Viewing 7 posts - 1 through 6 (of 6 total)
You must be logged in to reply to this topic. Login to reply