我使用的一些存储过程需要根据是否提供过程输入参数来插入WHERE标准。为了避免潜在的注入点,我想利用参数绑定作为插值标准的一部分的值。
由于添加到预准备语句的条件以及因此要绑定的参数数量可能因用户输入而异,因此我设计了以下方法来确定将哪些变量传递给EXECUTE语句。这有效,但似乎不够优雅。
CREATE PROCEDURE foo (IN mandatory INT, IN optional INT, IN optional2 VARCHAR(20))
BEGIN
SELECT
0, '', '', mandatory, optional, optional2
INTO
@params, @sql, @where, @m, @o1, @o2;
IF (@o1 > '' AND @o1 IS NOT NULL) THEN
SET @where = CONCAT(@where, ' AND field = ?');
SET @params = @params + 1;
END IF;
IF (@o2 > '' AND @o2 IS NOT NULL) THEN
SET @where = CONCAT(@where, ' AND field2 = ?');
SET @params = @params + 3;
END IF;
SET @sql = CONCAT('
SELECT id, bar FROM table
WHERE
baz = ?
', @where
);
PREPARE STMT FROM @sql;
CASE @params
WHEN 0 THEN EXECUTE STMT USING @m;
WHEN 1 THEN EXECUTE STMT USING @m, @o1;
WHEN 3 THEN EXECUTE STMT USING @m, @o2;
WHEN 4 THEN EXECUTE STMT USING @m, @o1, @o2;
END CASE;
DEALLOCATE PREPARE STMT;
END$$
我知道其他选择:
但是,我想知道是否有其他人已经遇到过这种纯粹用SQL处理EXECUTE语句动态构造的愿望。
答案 0 :(得分:0)
However, I was wondering if anyone else has ran into this desire to
handle dynamic construction of an EXECUTE statement purely with SQL.
是的,我也是。
这是一个PHP解决方案,用于根据未知长度的数组生成预准备语句的问号列表:
/* My target query is this:
SELECT fun FROM fun_stuff WHERE fun_key IN ( ...unknown number of values... )
*/
/* For this example let's set our array to this: */
$val_arr = array(1,2,3,4,5,6,7,8,9);
$val_arr_cnt = count($val_arr); /* and count it */
/* Now make prepared statement q-mark string from values array */
$sql_prep = str_pad('?', ($val_arr_cnt * 2) - 1, ',?', STR_PAD_RIGHT);
/* add it to query */
$sql = "SELECT fun FROM fun_stuff WHERE fun_key IN ($sql_prep)";
/* And the result:
SELECT fun FROM fun_stuff WHERE fun_key IN (?,?,?,?,?,?,?,?,?)
*/
我不知道这有多高效。但我也时不时地想要实现MySQL预处理语句的安全性和效率,但是具有可变长度的输入数组
答案 1 :(得分:0)
不确定是否可以动态构建参数列表(动态更改参数数量等)。但是既然你可以动态地构建你的where子句,一个非常简单的解决方法是做这样的事情。假设您的验证允许,则else子句基本上与忽略您可能或可能不会过滤的参数具有相同的效果。
if p_cust_id is not null && p_cust_id > 0 then
set v_where_clause = concat(v_where_clause, ' c.cust_id = ? ');
set @v_cust_id := p_cust_id;
else
set v_where_clause = concat(v_where_clause, ' c.cust_id > ? ');
set @v_cust_id := 0;
end if;
然后将上面的所有用户变量插入到执行语句
中execute str1 using @v_cust_id, @v_etc....;*