2014-04-29 63 views
1

我有一個事件系統,對於我的重複事件,我使用的是類似系統的cron。mysql在where子句中分割字符串

重複事件:

+----+----------+--------------+ 
| id | event_id | repeat_value | 
+----+----------+--------------+ 
| 1 |  11 | *_*_*  | 
| 2 |  12 | *_*_2  | 
| 3 |  13 | *_*_4/2  | 
| 4 |  14 | 23_*_*  | 
| 5 |  15 | 30_05_*  | 
+----+----------+--------------+ 

注:cron的值是day_month_day周

事件:

+----+------------------------+---------------------+---------------------+ 
| id | name     | start_date_time  | end_date_time  | 
+----+------------------------+---------------------+---------------------+ 
| 11 | Repeat daily   | 2014-04-30 12:00:00 | 2014-04-30 12:15:00 | 
| 12 | Repeat weekly   | 2014-05-06 12:00:00 | 2014-05-06 13:00:00 | 
| 13 | Repeat every two weeks | 2014-05-08 12:45:00 | 2014-05-08 13:45:00 | 
| 14 | Repeat monthly   | 2014-05-23 15:15:00 | 2014-05-23 16:00:00 | 
| 15 | Repeat yearly   | 2014-05-30 07:30:00 | 2014-05-30 10:15:00 | 
+----+------------------------+---------------------+---------------------+ 

反正我有一個查詢,選擇事件:

SELECT * 
FROM RepeatEvent 
JOIN `Event` 
ON `Event`.`id` = `RepeatEvent`.`event_id` 

Th at產生:

+----+----------+--------------+----+------------------------+---------------------+---------------------+ 
| id | event_id | repeat_value | id | name     | start_date_time  | end_date_time  | 
+----+----------+--------------+----+------------------------+---------------------+---------------------+ 
| 1 |  11 | *_*_*  | 11 | Repeat daily   | 2014-04-30 12:00:00 | 2014-04-30 12:15:00 | 
| 2 |  12 | *_*_2  | 12 | Repeat weekly   | 2014-05-06 12:00:00 | 2014-05-06 13:00:00 | 
| 3 |  13 | *_*_4/2  | 13 | Repeat every two weeks | 2014-05-08 12:45:00 | 2014-05-08 13:45:00 | 
| 4 |  14 | 23_*_*  | 14 | Repeat monthly   | 2014-05-23 15:15:00 | 2014-05-23 16:00:00 | 
| 5 |  15 | 30_05_*  | 15 | Repeat yearly   | 2014-05-30 07:30:00 | 2014-05-30 10:15:00 | 
+----+----------+--------------+----+------------------------+---------------------+---------------------+ 

但是,我想選擇一個月內的事件。我只會有一些條件:每日,每週,每兩週,每月和每年。 我想在我的where子句中分割重複值的字符串,並且如果它符合以下任何條件以將其顯示爲結果(repeatEvent是正在被詢問的行,搜索是要查找的日期):

array(3) = string_divide(repeat_value, '_') 
daily = array(0) 
monthy = array(1) 
dayOfWeek = array(2) 

if(daily == '*' && month == '*' && dayOfWeek == '*') //returns all the daily events as they will happen 
    return repeatEvent 

if(if(daily == '*' && month == '*' && dayOfWeek == search.dayOfWeek) //returns all the events on specific day 
    return repeatEvent 

if(daily == search.date && month == '*' && dayOfWeek == '*') //returns all the daily events as they will happen 
    return repeatEvent 

if (contains(dayOfWeek, '/')) 
    array(2) = string_divide(dayOfWeek,'/') 
    specificDayOfWeek = array(0); 
    if(specificDayOfWeek == repeatEvent.start_date.dayNumber) 
      if(timestampOf(search.timestamp)-timestampOf(repeatEvent.start_date)/604800 == (0 OR EVEN) 
       return repeatEvent 

if(daily == search.date && month == search.month && dayOfWeek == '*') //returns a single yearly event (shouldn't often crop up) 
    return repeatEvent 

//everything else is either an unknown format of repeat_value or not an event on this day 

總之我想運行中重複值在where子句中拆分查詢,我可以詢問拆分項目。我看過遊標,但互聯網似乎對他們提出建議。

我可以處理在PHP中選擇所有重複事件的結果,但是,我想這很慢。

這裏是我想看看是否在看四月份是什麼:

+----------+--------------+----+------------------------+---------------------+---------------------+ 
| event_id | repeat_value | id | name     | start_date_time  | end_date_time  | 
+----------+--------------+----+------------------------+---------------------+---------------------+ 
|  11 | *_*_*  | 11 | Repeat daily   | 2014-04-30 12:00:00 | 2014-04-30 12:15:00 | 
+----------+--------------+----+------------------------+---------------------+---------------------+ 

這是我希望看到的,如果看月

+----------+--------------+----+------------------------+---------------------+---------------------+ 
| event_id | repeat_value | id | name     | start_date_time  | end_date_time  | 
+----------+--------------+----+------------------------+---------------------+---------------------+ 
|  11 | *_*_*  | 11 | Repeat daily   | 2014-04-30 12:00:00 | 2014-04-30 12:15:00 | 
|  12 | *_*_2  | 12 | Repeat weekly   | 2014-05-06 12:00:00 | 2014-05-06 13:00:00 | 
|  13 | *_*_4/2  | 13 | Repeat every two weeks | 2014-05-08 12:45:00 | 2014-05-08 13:45:00 | 
|  14 | 23_*_*  | 14 | Repeat monthly   | 2014-05-23 15:15:00 | 2014-05-23 16:00:00 | 
|  15 | 30_05_*  | 15 | Repeat yearly   | 2014-05-30 07:30:00 | 2014-05-30 10:15:00 | 
+----------+--------------+----+------------------------+---------------------+---------------------+ 

這裏什麼是我想看看是否看6月份

+----------+--------------+----+------------------------+---------------------+---------------------+ 
| event_id | repeat_value | id | name     | start_date_time  | end_date_time  | 
+----------+--------------+----+------------------------+---------------------+---------------------+ 
|  11 | *_*_*  | 11 | Repeat daily   | 2014-04-30 12:00:00 | 2014-04-30 12:15:00 | 
|  12 | *_*_2  | 12 | Repeat weekly   | 2014-05-06 12:00:00 | 2014-05-06 13:00:00 | 
|  13 | *_*_4/2  | 13 | Repeat every two weeks | 2014-05-08 12:45:00 | 2014-05-08 13:45:00 | 
|  14 | 23_*_*  | 14 | Repeat monthly   | 2014-05-23 15:15:00 | 2014-05-23 16:00:00 | 
+----------+--------------+----+------------------------+---------------------+---------------------+ 
+0

你能提供你想要的樣本結果嗎? –

+0

在問題中加入了我想看到的內容 – bubblebath

+0

@bubblebath爲什麼你會在6月份看到任何輸出,因爲start_date_time和end_date_time間隔不包括任何事件的June? –

回答

4

這種模式你可以把繃帶上這一點,但沒有人會做你任何好處告訴你,這就是答案。

如果你的MySQL數據庫是可以改變的,我會強烈建議您將當前列用下劃線day_month_day of year三個單獨的列,daymonthday_of_year分裂。我還建議您將您的格式更改爲INT而不是VARCHAR。這會使搜索和解析變得更快,更容易,因爲它的設計方式不需要通過複雜的程序翻譯成計算機語言......這已經是大多數情況。

這裏的原因:

原因1:你的表是不是優化

你的表是不是優化,無論你選擇在這個階段做什麼會放緩。 SQL不是爲了在一列中具有多個值而構建的。 SQL數據庫的整個要點是將值拆分成不同的列和行。

規範化這張表的好處是它可以更快地搜索它,並且你將能夠在MySQL中建立查詢。看看Normalization。這是一個複雜的概念,但一旦你得到它,你將避免創建雜亂和複雜的程序。

原因2:您的表可以稍微調整以利用計算機日期/時間函數的功能。

計算機遵循基於Unix紀元時間的時間。它計算秒數,並始終在您的計算機上運行。事實上,計算機一直在計算這一點,因爲顧名思義,第一臺Unix計算機曾經開啓。此外,每個基於計算機和計算機的程序/系統都具有內置的快速日期和時間功能。 MySQL也沒有什麼不同。

我也建議還將所有這些存儲爲整數。 repeat_doy(一年中的某一天)很容易成爲smallint或至少一個標準int,而不是放置一個月和一天,您可以放置​​一年的實際1-365天。您可以使用DAY_OF_YEAR(NOW())將此輸入到MySQL中。要將其作爲日期退出,您可以使用MAKEDATE(YEAR(NOW),repeat_doy)。您可以使用0或NULL來代替所有的星號。

對於類似cron的系統,無論如何您可能都不需要進行那種計算。相反,在其他地方測量一年中的每一天(每臺計算機和語言都可以做到這一點,在Unix中它只是date "%j")可能會更容易。

解決方案

分割你的一個repeat_value成三個獨立的值,並把它們全部納入基於UNIX的時間值的整數。一天是1-7(週日到週六是0-6),月份是1-12,年中的一天是1-365(請記住,我們不包括366,因爲我們將我們的年份定位在任意的非躍進年)。

如果您想以您的原始格式在您的SELECT查詢中提取信息,使用concat合併三列的操作要比試圖在一列上進行搜索和分割容易得多。您還可以輕鬆利用內置的MySQL函數,快速將您所需要的內容轉化爲真實的,最新的,幾天,而無需您付出一定的努力。

要實現它在你的SQL數據庫:

+----+----------+--------------+--------------+------------+ 
| id | event_id | repeat_day | repeat_month | repeat_doy | 
+----+----------+--------------+--------------+------------+ 
| 1 |  11 |  *  |  *  |  *  | 
| 2 |  12 |  *  |  *  |  2  | 
| 3 |  13 |  *  |  *  |  4/2 | 
| 4 |  14 |  23  |  *  |  *  | 
| 5 |  15 |  30  |  5  |  *  | 
+----+----------+--------------+--------------+------------+ 

現在,你應該能夠建立一個查詢來獲取所有這些數據一起,無論多麼複雜的查詢。通過對錶格進行規範化,您將能夠充分利用關係數據庫的力量,而不會感到頭疼和被黑客攻擊。

編輯 Hugo Delsing在下面的評論中提到了一個很好的觀點。在我的第一個例子中,我爲day_of_year提供了閏年的修復,其中我選擇忽略2月29日。一個更好的解決方案無需修復。分割day_of_yearmonthday與複合索引。他還有一個關於周和週數的建議,但我會建議你閱讀它以獲取更多細節。

+0

我正要給你一個+1,直到你開始亂飛閏年。僅僅因爲它不大可能有人會在29日下午安排任何事情,你忘記了閏年?而且因爲遊行後的一切都可以休息一天,所以我們不使用實際的年份,而只是一年不是閏年?這只是問問題。然而,將價值分解爲單獨列的觀點是長遠來看最好的解決方案 –

+0

忘記閏年?如果我正在解決問題並提供解決方案,我不會忘記閏年。我會更新我的工作,以反映使用一年不是閏年的替代方法來進行計算。但是,如果您提供的解決方案確保閏年不是一年,那肯定不會「遺忘」。我用了太多的程序,他們只是忽略了,給每個人造成了問題。 – smcjones

+0

是的,你提供了另一種選擇,但你仍然無法計劃在2月29日的任何活動。把它叫做你想要的,但是一年中每天都不能使用的日曆系統顯然是錯誤的。無論多麼不可能,也不管多快的查詢。 –

2

試着寫在哪裏使用這個條件:

substring_index(repeat_value,'_', 1) 

,而不是每天

substring_index(substring_index(repeat_value,'_', -2), '_', 1) 

,而不是每月 和

substring_index(substring_index(repeat_value,'_', -1), '_', 1) 

,而不是一週中的某天

+0

雖然這在技術上有效,但我不建議這樣做。隨着系統的成熟,您將需要執行更復雜的查詢,並且您會發現越來越多的 - 不必要的 - 複雜的編程來完成這項工作。如果因爲某種原因你不能回去修改你的SQL並且你正在尋找繃帶,那麼這個工作就可以實現。只是不要指望它永遠工作。 – smcjones

+0

當然,我同意它會很複雜。但問題始於語言:「我有一個事件系統,對於我的重複事件,我使用的是像系統一樣的cron。」所以我認爲作者不想(或不能)改變「重複事件」表的結構。 – lulco

1

我認爲你得太多的問題,如果你只希望每個事件月而不是每天。假設你總是正確地填寫repeat_value,查詢是非常基本的。

基本上所有的事件發生在每月repeat_value要麼是LIKE '%_*_%'LIKE '%_{month}_%'

既然你提到PHP我假設你正在PHP中建立查詢,因此我使用了相同的。

<?php 
function buildQuery($searchDate) { 
    //you could/should do some more checking if the date is valid if the user provides the string 
    $searchDate = empty($searchDate) ? date("Y-m-d") : $searchDate; 

    $splitDate = explode('-', $searchDate); 
    $month = $splitDate[1]; 

    //Select everything that started after the searchdate 
    //the \_ is because else the _ would match any char. 
    $query = 'SELECT * 
      FROM RepeatEvent 
      JOIN `Event` 
      ON `Event`.`id` = `RepeatEvent`.`event_id` 
      WHERE `Event`.`start_date_time` < \''.$searchDate.'\' 
      AND 
      (
       `RepeatEvent`.`repeat_value` LIKE \'%\_'.$month.'\_%\' 
       OR `RepeatEvent`.`repeat_value` LIKE \'%\_*\_%\' 
      ) 
      '; 

    return $query;     
} 

//show querys for all months on current day/year 
for ($month = 1; $month<=12; $month++) { 
    echo buildQuery(date('Y-'.$month.'-d')) . '<hr>'; 
} 

?> 

現在,如果repeat_value可能是錯誤的,你可以添加一個簡單的正則表達式檢查,以確保該值總是喜歡*_*_**_*_*/*

相關問題