2012-07-05 117 views
0

我想將一些PHP代碼轉換爲一個MySQL存儲過程。以下是PHP代碼:MySQL爆炸字符串?

$aORs = array(); // create an empty array to hold the individual criteria to be OR'd... 
$months = explode('|',$_REQUEST['sSearch_'.$i]); 
$sWhere .= '('; 
foreach($months as $month) { 
    $year = intval(substr($month, 0, 4)); 
    $mon = intval(substr($month, 5, 2)); 
    array_push($aORs, '(MONTH(e.StartDate) = '.$mon.' and YEAR(e.StartDate) = '.$year.')'); 
} 
$sWhere .= implode(" OR ",$aORs); // transform the array of criteria into a properly delimited WHERE clause... 
$sWhere .= ')'; 

我將參數傳遞到存儲過程是這樣的:

2012-07|2012-10|2013-02 

我無法搞清楚如何上面的輸入解析成看起來WHERE子句像這樣:

(MONTH(e.sDate) = 07 AND YEAR(e.sDate) = 2012) OR 
(MONTH(e.sDate) = 10 AND YEAR(e.sDate) = 2012) OR 
(MONTH(e.sDate) = 02 AND YEAR(e.sDate) = 2013) OR 

如何在存儲過程中執行此操作?我見過「臭名昭​​着的」split_str函數,但我不知道可能有多少個值。

回答

1

如果您的參數值在您顯示時始終保持良好狀態,即四位數字的年份,短劃線和兩位數月份,並且沒有空白的垂直條分隔,則可以讓數據庫爲您進行搜索像這樣:

INSTR(CONCAT('|',parameter_value,'|'),DATE_FORMAT(e.sDate,'|%Y-%m|')) > 0 

或者是白色空間和分隔符多一點寬鬆,

INSTR(parameter_value,DATE_FORMAT(e.sDate,'%Y-%m')) > 0 

我不認爲這些會不會有更糟糕的表現明智,不是運行MONTH和YEAR函數在日期列上進行平等比較terals。這些謂詞都不會是可選的(也就是說,允許使用索引)。

由於您不必動態創建SQL語句並傳入可變數量的參數,因此此方法在存儲過程中具有較少工作的好處。 (要測試的代碼較少)

如果您需要允許參數爲可選參數,即未提供參數(默認爲空字符串),則可以避免在日期中應用謂詞在完全一樣的語句列,在不改變SQL文本:

(paramater_value = '' OR INSTR(parameter_value,DATE_FORMAT(e.sDate,'%Y-%m')) > 0) 


要讓謂語是可優化搜索,我想我的SQL引用本地日期列。我將它寫成範圍掃描,所以我的SQL文本將是形式:

( (e.sDate >=   CONCAT('2012-07','-01') AND 
    e.sDate < DATE_ADD(CONCAT('2012-07','-01'),INTERVAL 1 MONTH)) 
OR (e.sDate >=   CONCAT('2012-10','-01') AND 
    e.sDate < DATE_ADD(CONCAT('2010-10','-01'),INTERVAL 1 MONTH)) 
OR (e.sDate >=   CONCAT('2013-02','-01') AND 
    e.sDate < DATE_ADD(CONCAT('2013-02','-01'),INTERVAL 1 MONTH)) 

而不必使用索引的能力是唯一的好理由我會去解析出參數的所有麻煩字符串,使用可變數量的謂詞構建查詢文本,以及使用動態SQL,以及全部測試的麻煩。我寧願創建一個臨時表並使用從參數字符串解析的「yyyy-mm」值加載它,並在連接謂詞中引用它。

CREATE TEMPORARY TABLE my_temp_parameter 
(yyyy SMALLINT   NOT NULL COMMENT 'year yyyy'  
, mm TINYINT UNSIGNED NOT NULL COMMENT 'month, 1-12' 
, PRIMARY KEY (yyyy,mm) 
) 

(我肯定要對這些兩列的唯一約束,因爲我不希望我的加入產生「額外」行。)在我的查詢,我將引用臨時表是這樣的:

FROM ... e 
JOIN my_temp_parameter p 
    ON e.sDate >= CONCAT(p.yyyy,'-',p.mm,'-01') AND 
    e.sDate < DATE_ADD(CONCAT(p.yyyy,'-',p.mm,'-01'),INTERVAL 1 MONTH) 

這種方法的好處是我不必調試動態SQL,並維護生成SQL文本的代碼。我寧願有更容易測試的靜態SQL文本。

我很抱歉,如果這不能回答你的問題。但在我開始在存儲過程中創建動態SQL之前(並且有些情況下需要它),我會嘗試其他方法,並且如果我找不到其他合適的解決方案,則會回到動態SQL 。

+0

參數將形成良好,可能會超過1.我不打算修改表的結構。 – MB34 2012-07-09 18:00:34

+0

使用INSTR的第一個工作完美。我不必再解析出這些值。它也適用於國家,因爲我們以相同的方式通過它們。 – MB34 2012-07-09 18:05:17

+0

@ MB34:很高興聽到它爲你工作。你說你不打算修改表格的結構。我沒有看到你需要這樣做的任何理由。我不相信我在任何地方都會建議你需要改變桌子的結構,或者它會帶來任何好處。 (根據我對INSTR方法的建議,我只是建議查詢使用可用索引,晚上需要更詳細的查詢。) – spencer7593 2012-07-09 18:19:54