MySQL爆炸了一个字符串?

时间:2012-07-05 20:05:22

标签: mysql string stored-procedures

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

1 个答案:

答案 0 :(得分: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