我正在编写一个为人们处理任务的小应用程序。非常简单,但就表格设计而言,我坚持的领域是重复任务的情况,可以是一次,每天,每周或每月。如果是每周,它是在特定的一天,每周。每月是一个特定的日子。
我有一个任务表和一个 recurring_type_id,并且打算在代码中处理重复性任务,但这是理想的方式吗?另一种方法是在创建任务时插入所有任务 - 对于每个事件时间。但这似乎也不对。
任何人都可以就设计以及如何以可维护和有效的方式处理这个问题提出建议吗?
我正在使用 SQL Server 2008 R2
我正在编写一个为人们处理任务的小应用程序。非常简单,但就表格设计而言,我坚持的领域是重复任务的情况,可以是一次,每天,每周或每月。如果是每周,它是在特定的一天,每周。每月是一个特定的日子。
我有一个任务表和一个 recurring_type_id,并且打算在代码中处理重复性任务,但这是理想的方式吗?另一种方法是在创建任务时插入所有任务 - 对于每个事件时间。但这似乎也不对。
任何人都可以就设计以及如何以可维护和有效的方式处理这个问题提出建议吗?
我正在使用 SQL Server 2008 R2
我会创建一个任务表来插入我的任务。
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是任务要运行的星期几。
runAt for mothly是任务要运行的月份中的那一天(我通常在第一次运行月末任务,因为这样可以省去处理月份结束日期的麻烦,尽管您可以使用它来解决这个问题
lastDayOfMonth = datePart(d,dateadd(s,-1,dateadd(mm, datediff(m,0,getdate())+1,0)))
runAt for once是任务运行的实际日期。
然后我会创建一个每天运行的任务,看看需要运行什么。
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。
不确定这是否是您正在寻找的东西,但希望您能从中找到有用的东西。
我会尝试使用一些久经考验的解决方案。Unix Cron Table解决方案。许多其他工具(如 Hudson/Jenkins)也使用此解决方案。
来自维基百科。
* * * * * 要执行的命令 ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └───── 星期几(0 - 7)(0 或 7 为星期日,或使用名称) │ │ │ └────────── 月 (1 - 12) │ │ └─────────────── 一个月中的某天 (1 - 31) │ └──────────────────── 小时 (0 - 23) └────────────────────────── 分钟 (0 - 59)
此外,它还允许条目的快捷方式名称。
条目描述相当于 @yearly 每年 1 月 1 日凌晨午夜运行一次 0 0 1 1 * @每年 @monthly 每月第一天凌晨的午夜运行一次 0 0 1 * * @weekly 每周在周日凌晨运行一次 0 0 * * 0 @daily 每天午夜运行一次 0 0 * * * @hourly 每小时开始运行一次 0 * * * *
从这里,我们得到以下表格设计:
taskID cronExpression preDefinedDefinition 1 30 * * * * 空 2 0 * * * * @hourly 3…………
@Timothy Britt 提到此解决方案不考虑一次性休假。那是真实的。Linux 和/或 Unix 也有一个名为at的命令。似乎它将其条目存储在单独的文件中,并且单独的守护程序监视该文件。如果我们想在此表中包括一次性工作。我们可以添加额外的布尔列 OneTimeOnly。或另一张表,其中仅包含一次工作。
我做了一个非常相似的事情:
_id
interval_type
time
day
next
该day字段表示时间间隔内的日期,单次和每日为空。表中的next字段用于标识下一次运行任务的时间。当任务应该运行时设备关闭的情况下,这是需要的。您只需要决定如何处理一个或多个错过的间隔。
我应该像这样使用一个日期作为参考
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 天到月底
或月底
应该考虑在任务循环结束时包含另一个日期......
但是,工作永远不会完成......所以,它永远不会被使用......;)
我建议使用行业标准的 iCalendar 格式 ( http://www.rfc-editor.org/rfc/rfc5545.txt ),而不是尝试为此提出关系设计。你可以在这里看到一些例子。由于您的重复模式看起来很简单,因此为它们创建 iCalendar 表达式应该是一项相当简单的任务。
这是一个有用的语法验证器:http ://severinghaus.org/projects/icv/
目前尚不清楚您的环境究竟是什么,但开源 Job Scheduler 可以解决此类任务。
我已经接受了这里给出的答案,并提出了以下数据库结构:
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 日触发)
给定频率类型不需要的字段仅包含零或空值,并被查询忽略。
它不考虑闰年 2 月 29 日发生的年度事件,也不以任何方式考虑一天中的时间。
我已经阅读了上面的答案,这是我认为应该做的:
日程
ID
类型(每日、每月、每周、固定、每年) - 枚举
频率(可以是 1-7 [星期几]、1-30(或 28)[月份中的几天]、1-365 [一年中的几天] 或 null(对于每日,固定) - ArrayField(整数) - [ 1, 7] 或 [23] 或 [235] 或空
时间(UTC 时间) - ArrayField(字符字符串 - ['9:00', '13:30']
日期(对于固定类型) - 日期时间 - 2009-03-21
is_active (boolean) - 用于启用、禁用计划
name (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)