“重复任务”的设计选项

时间:2013-01-19 08:28:19

标签: sql sql-server

我正在编写一个处理人员任务的小应用程序。非常简单,但就桌面设计而言,我所坚持的区域是一个重复性任务的情况,可以是一次性,每天,每周或每月。如果每周一次,那就是特定的一天,每周一次。每月是特定的一天。

我有一个任务表和一个recurring_type_id,并且会在代码中处理重复的任务,但这是理想的方法吗?另一种方法是在创建任务时插入所有任务 - 每个事件时间。但这似乎也不对。

任何人都可以就设计提出建议以及如何以可维护和有效的方式处理这个问题吗?

我正在使用SQL Server 2008 R2

8 个答案:

答案 0 :(得分:12)

我会创建一个任务表来插入我的任务。

taskTable
|taskID  |Freq   |runAt       |
-------------------------------
|1       |daily  |1           |
|2       |daily  |0           |
|3       |weekly |5           |
|4       |weekly |1           |
|5       |monthly|15          |
|6       |monthly|1           |
|7       |once   |2013-7-4    |

日报的runAt从未被考虑过,因此输入的值无关紧要。

每周项的runAt是任务运行的星期几。

对于 mothly 的runAt是任务运行的月份的那一天(我通常在第一天运行的月末任务,因为它可以节省处理当月结束的那天的麻烦,尽管你可以用它来搞清楚

lastDayOfMonth = datePart(d,dateadd(s,-1,dateadd(mm, datediff(m,0,getdate())+1,0)))

runAt for 一次是任务运行的实际日期。

然后我创建一个每天运行的任务,看看需要运行什么。

select  taskID
from    taskTable
where   (freq = 'once' and runAt = convert(varchar(10),getDate(),21))
  or    freq = 'daily'
  or    (freq = 'weekly' and runAt = datePart(dw,getDate()))
  or    (freq = 'monthly' and runAt = datePart(d,getDate())

此查询为我提供了我需要运行的任何任务的所有taskID。

不确定这是否是您正在寻找的东西,但希望您能找到有用的东西。

答案 1 :(得分:5)

我会尝试使用经过试验和测试的解决方案。 Unix Cron Table解决方案。许多其他工具也使用此解决方案,如Hudson / Jenkins。

来自维基百科。

*    *    *    *    *  command to be executed
┬    ┬    ┬    ┬    ┬
│    │    │    │    │
│    │    │    │    │
│    │    │    │    └───── day of week (0 - 7) (0 or 7 are Sunday, or use names)
│    │    │    └────────── month (1 - 12)
│    │    └─────────────── day of month (1 - 31)
│    └──────────────────── hour (0 - 23)
└───────────────────────── min (0 - 59)

它还允许条目的快捷方式名称。

Entry      Description                                                             Equivalent To
@yearly    Run once a year at midnight in the morning of January 1              0 0 1 1 *
@annually   
@monthly   Run once a month at midnight in the morning of the first of the month    0 0 1 * *
@weekly    Run once a week at midnight in the morning of Sunday                     0 0 * * 0
@daily     Run once a day at midnight                                               0 0 * * *
@hourly    Run once an hour at the beginning of the hour                        0 * * * *

从表格设计开始。

taskID  cronExpression preDefinedDefinition
 1       30 * * * *      null
 2       0 * * * *      @hourly
 3        ...            ....
@Timothy Britt提到这个解决方案并不能解释一次性问题。那是真实的。 Linux和/或Unix也有一个名为at的命令。似乎在商店中它的条目在一个单独的文件和单独的deamon监视此文件。 如果我们想在此表中包含一次性作业。我们可以添加额外的布尔列OneTimeOnly。或另一个只包含一次性工作的表。

答案 2 :(得分:1)

我做了一件非常相似的事情:

   _id
   interval_type
   time
   day
   next

day字段表示区间内的日期,单次和每日为空。表中的next字段用于标识下次运行任务的时间。当任务运行时设备关闭的情况下需要这样做。您只需要决定如何处理一个或多个错过的时间间隔。

答案 3 :(得分:1)

我应该像表格一样使用日期作为参考

taskid      date_reference Freq            distance
----------- -------------- --------------- -----------
1           2011-01-01     daily           NULL
2           2011-01-17     monthly         NULL
3           2013-01-17     weekly          NULL
4           2013-01-24     once            NULL
5           2011-01-01     monthly         NULL
6           2011-01-01     monthly         NULL
7           2011-01-01     before_eomonth  5
8           2013-01-01     loop_day        4
9           2013-01-01     loop_month      4

所以你可以通过多种方式检查这段时间......

传统的每周,每日,每月和一次。像长矛的解决方案..

但你会有几种不同的方式,比如

每4天

每4个月

或5月到月底

或月末

应考虑在任务循环结束时包含另一个日期......

但是,工作永远不会完成......所以,它永远不会被使用..;)

答案 4 :(得分:1)

我建议使用行业标准的iCalendar格式(http://www.rfc-editor.org/rfc/rfc5545.txt),而不是尝试为此设计关系设计。你可以看到一些例子here。由于您的重复模式看起来非常简单,因此为它们创建iCalendar表达式应该是一项非常简单的任务。

这是一个有用的语法验证器:http://severinghaus.org/projects/icv/

答案 5 :(得分:0)

目前尚不清楚您的环境究竟是什么,但开源Job Scheduler可以解决此类任务。

http://en.wikipedia.org/wiki/Open_Source_Job_Scheduler

答案 6 :(得分:0)

我已经在这里给出了答案并提出了以下数据库结构:

Column          Data Type
id              int
task_name       nvarchar(50)
frequency_type  nvarchar(10)
runat_day       int
runat_month     int
runat_year      int

数据如下所示:

id  task_name       frequency_type  runat_day   runat_month runat_year
1   Do this Once    once            16          2           2018
2   Do this Monthly monthly         31          0           0
3   Do this Yearly  yearly          28          2           0
4   Do this Daily   daily           0           0           0
5   Do this Weekly  weekly          6           0           0

将数据拉回的查询如下所示:

DECLARE @chosen_date datetime = '2018-02-28'
DECLARE @lastDayOfMonth int = datePart(d,dateadd(s,-1,dateadd(mm, datediff(m,0,getdate())+1,0)))

select  task_name
from    scheduled_tasks
where   (frequency_type = 'once' and runat_day = DATEPART(DAY, @chosen_date) and runat_month = DATEPART(MONTH, @chosen_date) and runat_year = DATEPART(YEAR, @chosen_date))
  or    frequency_type = 'daily'
  or    (frequency_type = 'weekly' and runat_day = DATEPART(WEEKDAY,@chosen_date))
  or    (frequency_type = 'monthly' and runat_day = DATEPART(DAY, @chosen_date))
  or    (frequency_type = 'monthly' and @lastDayOfMonth = DATEPART(DAY, @chosen_date) and runat_day >= @lastDayOfMonth)
  or    (frequency_type = 'yearly' and runat_day = DATEPART(DAY, @chosen_date) and runat_month = DATEPART(MONTH, @chosen_date))

到目前为止,我的所有用例都已正确显示,即使是在某一天没有存在的某一天的月末日期也会得到正确处理(例如,每个月的第31个仍然会被触发) 2月28日)

给定frequency_type所不需要的字段只有零或空,并且被查询忽略。

它不会考虑在闰年2月29日发生的年度事件,也不会以任何方式考虑时间。

答案 7 :(得分:0)

我已经阅读了上面的答案,这是我认为应该做的:

计划


  • Id

  • 类型(每日,每月,每周,固定,每年)-枚举

  • 频率(可以是1-7 [一周中的天],1-30(或28)[一个月中的天],1-365 [一年中的天]或为null(对于每日,固定)-ArrayField (以整数为单位)-[1,7]或[23]或[235]或null

  • 时间(UTC中的一天中的时间)-ArrayField(Char字符串-['9:00','13:30']

  • 日期(用于固定类型)-datetime-2009-03-21

  • is_active(布尔值)-用于启用,禁用计划

  • 名称(CharField)-如果要命名时间表

其余字段将需要您所构建的内容的上下文。

现在,为此,我正在考虑每30分钟运行一次cronjob(我花的时间输入间隔为30分钟),它运行一个脚本(在我的情况下为django管理命令),该脚本从该表中筛选出需要运行:

查询将是这样的:

current_day_of_week = 3
current_day_of_month = 24
current_day_of_year = 114
current_time = 13:30
current_date = 2019-04-24

Filter records that match the below query(not even psuedo code)(I'm using Q objects(https://docs.djangoproject.com/en/2.2/topics/db/queries/#complex-lookups-with-q-objects)

Q(daily AND current_time) OR
Q(weekly AND current_day_of_week AND current_time) OR
Q(monthly AND current_day_of_month AND current_time) OR
Q(yearly AND current_day_of_year AND current_time) OR
Q(fixed AND current_date AND current_time)