[英]mysql split a string in a where clause
我有一個事件系統,對於重復事件,我使用的是cron式系統。
重復事件:
+----+----------+--------------+
| 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`
產生:
+----+----------+--------------+----+------------------------+---------------------+---------------------+
| 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中所有重復事件的結果,但是,我認為這非常慢。
如果要查看4月,這是我想查看的內容:
+----------+--------------+----+------------------------+---------------------+---------------------+
| 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 |
+----------+--------------+----+------------------------+---------------------+---------------------+
這是我想看看六月的情況
+----------+--------------+----+------------------------+---------------------+---------------------+
| 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 |
+----------+--------------+----+------------------------+---------------------+---------------------+
您可以對此進行創可貼,但是沒有人會幫您任何忙,告訴您這就是答案。
如果您的MySQL數據庫可以更改,我強烈建議您將帶下划線的day_month_day of year
當前列拆分為3個獨立的列, day
, month
和day_of_year
。 我還建議您將格式更改為INT
而不是VARCHAR
。 這將使它更快,更容易搜索和解析,因為它的設計方式不需要通過復雜的程序將其翻譯成計算機語言……這已經是大多數方式了。
原因如下:
無論您在此階段選擇做什么,您的表都沒有經過優化,並且會變慢。 SQL並非在一列中具有多個值。 SQL數據庫的重點是將值分成不同的列和行。
對該表進行規范化的好處是它可以更快地搜索它,並且您將能夠在MySQL中建立查詢。 看一看Normalization 。 這是一個復雜的概念,但是一旦掌握了它,就可以避免創建混亂而復雜的程序。
計算機根據Unix Epoch Time跟蹤時間。 它數秒,並且始終在您的計算機上運行。 實際上,自從顧名思義以來,第一台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
leap年修正,其中我選擇忽略2月29日。一種更好的解決方案消除了對修正的需要。 用復合索引將day_of_year
拆分為month
和day
。 他也有關於周數和周數的建議,但我只建議您閱讀以獲取更多詳細信息。
嘗試使用以下條件寫出where條件:
substring_index(repeat_value,'_', 1)
而不是每天
substring_index(substring_index(repeat_value,'_', -2), '_', 1)
而不是每月和
substring_index(substring_index(repeat_value,'_', -1), '_', 1)
而不是dayOfWeek
我認為如果您只想要每月而不是每天的活動,那么您就想得太多了。 假設您總是正確地填充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
可能是錯誤的,則可以添加一個簡單的正則表達式檢查,以確保該值始終像*_*_*
或*_*_*/*
您可以在MySQL中使用基本的正則表達式:
http://dev.mysql.com/doc/refman/5.0/en/pattern-matching.html
對於5月(第一天)的每月活動,您可以使用以下模式(未經測試):
[0-9\*]+\_[5\*]\_1
您可以通過PHP生成此模式
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.