簡體   English   中英

mysql在where子句中分割字符串

[英]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個獨立的列, daymonthday_of_year 我還建議您將格式更改為INT而不是VARCHAR 這將使它更快,更容易搜索和解析,因為它的設計方式不需要通過復雜的程序將其翻譯成計算機語言……這已經是大多數方式了。

原因如下:

原因1:您的表未優化

無論您在此階段選擇做什么,您的表都沒有經過優化,並且會變慢。 SQL並非在一列中具有多個值。 SQL數據庫的重點是將值分成不同的列和行。

對該表進行規范化的好處是它可以更快地搜索它,並且您將能夠在MySQL中建立查詢。 看一看Normalization 這是一個復雜的概念,但是一旦掌握了它,就可以避免創建混亂而復雜的程序。

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

計算機根據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拆分為monthday 他也有關於周數和周數的建議,但我只建議您閱讀以獲取更多詳細信息。

嘗試使用以下條件寫出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_valueLIKE '%_*_%'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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM