2013-01-19 58 views
5

我正在寫一個處理任務給人的小應用程序。非常簡單,但就表格設計而言,我堅持的區域就是重複執行的任務,可以是關閉,每天,每週或每月一次。如果是每週,則是每週特定的一天。每月是特定的一天。「重複性任務」的設計選項

我有一個任務表和一個recurring_type_id,並且要處理代碼中的循環任務,但這是理想的方式嗎?另一種方式是在創建任務時插入所有任務 - 針對每個事件時間。但是這看起來也不正確。

任何人都可以建議設計以及如何以可維護和有效的方式處理此問題?

我使用SQL Server 2008 R2的

+0

每月 - 如果你設置一個天數,你不能處理31個月,甚至2月份的30和29。你需要更復雜的邏輯嗎? – Serg

+0

對於這些情況,我的邏輯是,如果任務設置爲第31,並且該月有30,29或28天,則使用該月的最後一天。 – Craig

+0

@克雷格對於賞金來說足夠好嗎? – Lance

回答

10

我將創建一個任務表中插入我的任務之中。

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爲mothly是該月的一天,任務是運行我平時以來的首次運行(月末的任務之一是節省了處理與該月的一天結束的,雖然你可以使用這個麻煩去弄清楚

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

的Runat爲一旦是實際的一天運行任務。

然後我會創建每天運行,看看有什麼需要運行的任務。

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。

不知道這是你在找什麼,但希望你會找到有用的東西。

+0

我敢打賭,你應該使用datepart(weekday,getDate())當freq是'每週'..或者你會錯過某些事情的某個星期..;)但總體上它是一個很好的解決方案 – Frederic

+0

謝謝@Frederic我更新了它 – Lance

+0

此解決方案可以工作,但前提是您可以每天(或每當您決定運行觸發任務時)同時運行任務/提醒/通知。如果您想靈活地允許用戶要指定一天中的時間,您需要解決更像Attila建議的問題。由於cron表解決方案無法解釋一次性問題,所以最佳解決方案可能是該解決方案與cron表解決方案的混合。 –

1

我做了非常類似的事:

_id 
    interval_type 
    time 
    day 
    next 

day字段表示間隔,這是空的一次性和日常中的一天。表中的next字段用於標識任務下一次運行的時間。這是在任務運行時設備關閉的情況下所需要的。你只需要決定如何處理一個或多個錯過的間隔。

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天至一個月

或結束一個月的月底

應考慮其他日期進行任務循環結束......

但是,工作寫不完......所以,它永遠不會被用來..;)

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 * * * * 

從這裏下表desing。

 
taskID cronExpression preDefinedDefinition 
1  30 * * * *  null 
2  0 * * * *  @hourly 
3  ...   .... 

@Timothy Britt提到這個解決方案沒有考慮一次性關閉。那是真實的。 Linux和/或Unix也有一個名爲at的命令。似乎在商店它的條目在一個單獨的文件和單獨的deamon監視這個文件。 如果我們想在此表中包含一次性工作。我們可能會添加額外的布爾列OneTimeOnly。或者另一張只包括一次性工作的表格。

+0

該解決方案未能解決一次性問題。 –

+0

我建議使用額外的列(布爾)一次性。它工作後,應該被刪除。 –

+0

該解決方案背後的理論適用於多個系統,具有高度的靈活性,因此對我來說就像是正確的答案。 –

1

我會建議使用,而不是試圖想出一個關係設計爲這個行業標準的iCalendar格式(http://www.rfc-editor.org/rfc/rfc5545.txt)。你可以看到一些例子here。由於你的循環模式看起來很簡單,爲它們創建iCalendar表達式應該是一個相當簡單的任務。

這裏是一個有用的語法驗證:http://severinghaus.org/projects/icv/

+0

OP,比較http://stackoverflow.com/a/30605875:只是從複雜的http://stackoverflow.com/questions/5183630/calendar-recurring-repeating-events-best-storage-method或類似的,似乎規則數據庫比實際將這些規則映射到數據庫佈局更容易維護並且效率更高。 – isync

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日發生的年度事件,也沒有考慮以任何方式考慮時間。