MySQL 分解字符串?

问题描述 投票:0回答:1

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

mysql string stored-procedures delimited
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 函数并与文字进行相等比较更差的性能。这些谓词都不是可控制的(即允许使用索引)。

这种方法的优点是可以减少存储过程中的工作量,因为您不必动态创建 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 .

© www.soinside.com 2019 - 2024. All rights reserved.