我正在尝试将一些 PHP 代码转换为 MySQL 存储过程。这是 PHP 代码:
$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 函数,但我不知道可能有多少个值。
如果您的参数值在显示时始终格式正确,即四位数的年份、破折号和两位数的月份,并用竖线分隔且没有空格,则可以让数据库为您进行搜索,如下所示:
INSTR(CONCAT('|',parameter_value,'|'),DATE_FORMAT(e.sDate,'|%Y-%m|')) > 0
或者,对空格和分隔符更加自由一点,
INSTR(parameter_value,DATE_FORMAT(e.sDate,'%Y-%m')) > 0
我认为这些不会比在日期列上运行 MONTH 和 YEAR 函数并与文字进行相等比较更差的性能。这些谓词都不是可控制的(即允许使用索引)。
这种方法的优点是可以减少存储过程中的工作量,因为您不必动态创建 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 .