【问题标题】:Design option for 'recurring tasks'“重复任务”的设计选项
【发布时间】:2013-01-02 23:11:36
【问题描述】:

我正在编写一个为人们处理任务的小应用程序。非常简单,但就表格设计而言,我坚持的领域是重复任务的情况,可以是一次,每天,每周或每月。如果是每周,它是在特定的一天,每周。每月是特定的一天。

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

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

我使用的是 SQL Server 2008 R2

【问题讨论】:

  • 每月 - 如果您设置天数,您将无法处理某些月份的 31 甚至 2 月的 30 和 29。这里需要更复杂的逻辑吗?
  • 对于这些情况,我的逻辑是,如果任务设置为 31 日,并且该月有 30、29 或 28 天,则使用该月的最后一天。
  • @Craig 的答案足以获得赏金吗?

标签: sql sql-server


【解决方案1】:

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

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    |

dailies

runAt 从未被考虑过,因此输入什么值并不重要。

weekly 项的

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。

不确定这是否是您要找的东西,但希望您能从中找到有用的东西。

【讨论】:

  • 我敢打赌,当频率为“每周”时,您应该使用 datepart(weekday,getDate()) .. 否则您会在几周内错过一些东西.. ;) 但总的来说这是一个不错的解决方案
  • 此解决方案有效,但前提是您对在一天中的同一时间、每天(或在您决定运行触发任务时)运行的任务/提醒/通知感到满意。如果您希望灵活地允许用户指定一天中的时间,您需要像 Attila 建议的那样解决问题。最佳解决方案可能是这个解决方案和 cron 表解决方案的混合体,因为 cron 表解决方案无法解决一次性问题。
  • 我知道这已经有一段时间了,但是如果你想处理频率的步骤,比如每 2 周或每 4 个月呢?
  • 一些非常相似的东西,我在这里发帖:stackoverflow.com/a/55827507/2349997
【解决方案2】:

我会尝试使用一些久经考验的解决方案。 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。或者另一个只包含一次工作的表。

【讨论】:

  • 此解决方案未能解决一次性问题。
  • 我建议一次性使用额外的列(布尔值)。生效后,应该删除。
  • 这个解决方案背后的理论适用于多个系统,非常灵活,因此对我来说是正确的答案。
【解决方案3】:

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

   _id
   interval_type
   time
   day
   next

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

【讨论】:

    【解决方案4】:

    我应该像这样使用一个日期作为参考

    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 天

    或月底

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

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

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

      【解决方案6】:

      不清楚你的环境到底是什么,但开源 Job Scheduler 可以解决这类任务。

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

      【讨论】:

        【解决方案7】:

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

        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 日发生的年度事件,也不以任何方式考虑一天中的时间。

        【讨论】:

          【解决方案8】:

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

          时间表


          • 身份证

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

          • 频率(可以是 1-7[星期几]、1-30(或 28)[月份中的天数]、1-365[一年中的天数] 或 null(对于每日,固定) - ArrayField (整数) - [1, 7] OR [23] OR [235]OR null

          • 时间(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)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-08-11
            • 2012-09-08
            • 1970-01-01
            • 1970-01-01
            • 2021-06-25
            • 2015-02-08
            • 2018-04-30
            相关资源
            最近更新 更多