0

我正在尝试将一些 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 函数,但我不知道可能有多少值。

4

1 回答 1

1

如果您的参数值在显示时总是格式正确,一个四位数的年份、一个破折号和一个两位数的月份,由没有空格的竖线分隔,您可以让数据库像这样为您搜索:

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

或者,对空白和分隔符更自由一点,

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

我认为这些在性能方面不会比在日期列上运行 MONTH 和 YEAR 函数并与文字进行相等比较更差。这些谓词都不会是 sargable(即,允许使用索引)。

这种方法的好处是在存储过程中要做的工作更少,因为您不必动态创建 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 .

于 2012-07-05T22:11:44.660 回答