我正在创建一个数据库,我希望在给定的日期和时间范围内每天为随机的2小时窗口安排任务。例如,Task1可以在凌晨5点到下午5点之间的任何时间从1月1日到1月12日运行。因此,数据库将在每个日期安排任务为随机的2小时窗口(不早于凌晨5点开始,不迟于晚上5点停止)。它可能会为Task1抛出类似的东西:
Date Start_Time Stop_Time
01 Jan 06:32 08:32
02 Jan 14:24 16:24
03 Jan 08:05 10:05
04 Jan 12:17 14:17
05 Jan 11:23 13:23
06 Jan 12:53 14:53
07 Jan 09:11 11:11
08 Jan 05:27 05:27
09 Jan 12:46 14:46
除了每个任务的条件(必须在日期范围内和给定时间范围内每天安排)之外,在任何一天的任何给定点上不得超过2个任务重叠,并且任何任务都不会遇到第二天(因此他们不能在晚上10点后开始)。
到目前为止,我的数据库执行此操作,虽然速度很慢,所以我想知道我使用的方法是否效率最高。
对于表格,我有一个(tblWindows),它基本上只包含一个名为WindowStart的列,该列填充了当天的每一分钟,从00:00开始到23:59结束。从字面上看,有1440条记录 - 每天每分钟有一条记录。
我有另一个表(tblTaskConfigs),我有每个要安排的任务的配置。这是我为每个要安排的任务指定开始/停止日期和开始/停止时间的地方。
最后,我的tblSchdTasks表会跟踪计划任务的时间。
关于操作,它是这样的:
Open tblTaskConfigs recordset. For each TaskConfig record:
1) Save the following into variables:
- StartDate
- StopDate
- StartTime
- StopTime
2) For each date the task is to be scheduled on:
A) Using DCount on tblSchdTasks, check if that task has already been scheduled for that date:
- Yes: Skip to the next date
- No:
I) Open a query recordset (qryAvailWin) that contains available windows for that date that fall within the TaskConfig's start/stop times (times from tblWindows in which there are no more than 1 task that overlaps those times).
II) Choose a random record from qryAvailWin to determine the start time of the Task to be scheduled.
III) Open a tblSchdTasks recordset and create a new record for the task and it's randomly-selected time for that day.
所以,我打开了tblTaskConfigs记录集,并循环遍历每条记录。对于每个记录,每天都要安排任务,我打开另外2个记录集(qryAvailWin& tblSchdTasks)来检查可用时间并实际安排任务。
对于持续56天的1项任务,此操作大约需要108-113秒。我怀疑是因为它打开和关闭了总共113个记录集(1 +(56 x 2))。另外,qryAvailWin有三个参数(CurrDate,StartTime和StopTime),我需要在每次打开之前设置这些参数,以便它只显示与该日期和TaskConfig相关的可用窗口。
你能想到一种更有效的方法吗?
答案 0 :(得分:0)
有一些时间,所以我编写了解决问题的方法。 既然你没有给我很多关于细节的工作,你将不得不填写很多空白,例如变量名和WHERE标准。
首先,2个辅助函数用于选择随机数和日期:
Public Function RandomRange(Lower As Double, Upper As Double, Optional IncludeDecimals As Boolean = True) As Double
'Returns a random number between Lower and Upper, either with or without decimals
RandomRange = Lower + (Rnd() * (Upper - Lower + IIf(IncludeDecimals, 0, 1)))
If IncludeDecimals = True Then Exit Function
RandomRange = Int(RandomRange)
End Function
Public Function RandomTime(Lower As Date, Upper As Date) As Date
'Returns a random time between Lower and Upper
Dim randomTimeDbl As Double
randomTimeDbl = CDbl(Lower) + (Rnd() * CDbl(Upper - Lower))
RandomTime = CDate(randomTimeDbl)
End Function
然后,计划任务间隔天的实际逻辑:
Public Function ScheduleTasksBetweenDays(StartDate As Date, EndDate As Date, TaskIdentifier As Variant)
'Open the scheduled tasks table
Dim rsSchdTasks As Recordset
Set rsSchdTasks = CurrentDb.OpenRecordset("SELECT * FROM tblSchdTasks SORT BY Date ASC, Start_Time ASC")
'Second recordset for tasks only on a specific date
Dim rsFiltered As Recordset
'Create a collection to save valid start and end dates
Dim startEndDates As Collection
'And create a collection for overlapping times
Dim overlappingTimes As Collection
'Create two arrays to save start-end date pairs
Dim startEndDatePair(2) As Date
Dim previousTaskStartEndTime(2) As Date
'Create an int to hold random values
Dim randomInt As Integer
'Make an iterator for a while loop, and one for the for loop
Dim dateIterator As Date
Dim i As Integer
dateIterator = StartDate
'Iterate through all the dates
While dateIterator < EndDate
'Set rsFiltered to all records of that date
rsSchdTasks.Filter = "Date = #" & Format(dateIterator, "yyyy\/mm\/dd") & "#"
'Open the filtered recordset
Set rsFiltered = rsSchdTasks.OpenRecordset()
'Use the already instantiated recordset instead of a DCount, if the task is already set, next iteration
rsFiltered.FindFirst "TaskIdentifier = " & TaskIdentifier
If Not rsFiltered.NoMatch Then GoTo NextDate
'Start at the first already set taks, iterate through them
rsFiltered.MoveFirst
'Clean up the overlappingTimes collection
Set overlappingTimes = New Collection
'Initialize the first task as the previous one
If Not rsFiltered.EOF Then
previousTaskStartEndTime(1) = rsFiltered.Fields("Start_Time")
previousTaskStartEndTime(2) = rsFiltered.Fields("End_Time")
rsFiltered.MoveNext
End If
Do While Not rsFiltered.EOF
'If the start time of the next task is less than the end time
If previousTaskStartEndTime(2) > rsFiltered.Fields("Start_Time") Then
'The overlapping segment is from end of previous to start of new
startEndDatePair(1) = rsFiltered.Fields("Start_Time")
startEndDatePair(2) = previousTaskStartEndTime(2)
'Add those that interval to the collection of invalid intervals
overlappingTimes.Add startEndDatePair
End If
previousTaskStartEndTime(1) = rsFiltered.Fields("Start_Time")
previousTaskStartEndTime(2) = rsFiltered.Fields("End_Time")
rsFiltered.MoveNext
Loop
'Now, we have a collection of all times that can't overlap with the task
'Lets set the next collection of possible start times
'First possible start time is 5 AM
startEndDatePair(1) = #5:00:00 AM#
'Open up a blank collection of possible valid intervals
Set startEndDates = New Collection
'Next step seems familliar, loop through the invalid times to calculate the valid ones
For i = 1 To overlappingTimes.Count
'If the start of the next task is more than 2 hours away than the possible start time, we can plan it before this task
If startEndDatePair(1) + #2:00:00 AM# < overlappingTimes(i)(1) Then
'Set the maximum start time to 2 hours before the next task
startEndDatePair(2) = overlappingTimes(i)(1) - #2:00:00 AM#
'Add it to the collection of possible times
startEndDates.Add startEndDatePair
End If
'Set the next possible valid interval to be behind this task
startEndDatePair(1) = overlappingTimes(i)(2)
Next i
'All unchanged since last edit here
'Check if we can put our task behind the last one, if so, last possible start time is 10 PM
If startEndDatePair(1) < #10:00:00 PM# Then
'Set it's maximum end time
startEndDatePair(2) = #10:00:00 PM#
startEndDates.Add startEndDatePair
End If
'Now, we have an array with all possible intervals where our task can start at
If startEndDates.Count = 0 Then
'Apparently, this day is already full and we can't plan the task. Do something adequate for that scenario
GoTo NextDate
End If
'pick a random interval from our intervals array
randomInt = RandomRange(1, startEndDates.Count, False)
'Assign our array to that interval
startEndDatePair(1) = startEndDates(randomInt)(1)
startEndDatePair(2) = startEndDates(randomInt)(1)
'update the schdTasks table
rsSchdTasks.Edit
rsSchdTasks.AddNew
rsSchdTasks.Fields("Date") = dateIterator
'Pick a random start time within our interval
rsSchdTasks.Fields("Start_Time") = RandomTime(startEndDatePair(1), startEndDatePair(2))
'End = 2 hours later
rsSchdTasks.Fields("End_Time") = rsSchdTasks.Fields("Start_Time") + #2:00:00 AM#
rsSchdTasks.Fields("TaskIdentifier") = TaskIdentifier
rsSchdTasks.Update
NextDate:
dateIterator = dateIterator + 1
Loop
End Function
您可能会注意到,改进包括:
可能的缺点: