MySQL 分解字符串?

标签 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 函数,但我不知道可能有多少个值。

最佳答案

如果您的参数值在您显示时始终格式正确,即四位数的年份、破折号和两位数的月份,并用竖线分隔,没有空格,您可以让数据库为您进行搜索这个:

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 引用 native 日期列。我会将其写为范围扫描,因此我的 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 .

关于MySQL 分解字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11351775/

相关文章:

c# - ASP C# |使用 SQL 和存储过程更新数据库

php - 从 MySQL 迁移到 MongoDB - 自增字段

c# - 如何在 C# 中连接到 mysql 数据库并模拟 SELECT、UPDATE 和 INSERT 函数

Python,使用字符串参数指向定义的列表/数组对象

javascript url 安全文件名安全字符串

sql - Oracle - 将存储过程复制到远程数据库

mysql - 将参数传递给mysql中的存储过程

c# - 使用 C# 在 MYSQL 中声明局部变量时发生 fatal error

sql - 在sqlite中模拟变量列名

c++ - 错误 C2679 : binary '=' : no operator defined which takes a right-hand operand of type