MySQL准备语句与大容量插入


MySQL prepared statement with bulk insert

我有一个模块,允许管理员添加一些用户(可能多个)到一个组。

PHP

$user_id  = $_POST["user_id"]; //this can be an array
$group_id = $_POST["group_id"];
$sql = "INSERT IGNORE INTO users_groups (user_id, group_id) 
        VALUES ($user_id[0],$group_id)";
for ( $i = 1; $i < count($user_id); $i++) 
{
    $sql .= ", ($user_id[$i],$group_id)"
}

正如您所看到的,sql-query取决于管理员选择了多少用户。

我如何使用这段代码与准备好的语句,因为后变量来自用户(SQL注入…)

我有两个选择框:
  • 一个用于用户选择(可能有多个选择->数组):$_POST["user_id"]
  • 1用于组选择(只能选择一个):$_POST["group_id"]

现在我想要一个准备好的SQL语句插入user_idgroup_id到多对多表(users_groups)。问题是必须插入的值的数量可以改变(取决于管理员在selectbox中选择了多少用户)。

我想根据管理员选择了多少用户来更改准备好的查询。

例如:

    sql: INSERT IGNORE INTO users_groups (user_id, group_id) VALUES ($user_id[0],$group_id), ($user_id[1],$group_id)
  • admin选择了四个用户-> sql: INSERT IGNORE INTO users_groups (user_id, group_id) VALUES ($user_id[0],$group_id), ($user_id[1],$group_id), ($user_id[2],$group_id), ($user_id[3],$group_id)

我的问题:我怎么能自动做到这一点,并与准备好的sql语句,因为我不想有像10倍的if(count($user_id) == number) {... ?

更新2 如果我手动执行此操作,代码将看起来像这样:

$sql = $db->prepare("INSERT IGNORE INTO users_groups (user_id, group_id) VALUES (?, ?)"); 
$sql->bind_param('ii', $user_id[0], $group_id);

更新3 检查post变量中是否只有整数:

$user_id = filter_input_array(INPUT_POST, 'user_id', FILTER_SANITIZE_NUMBER_INT);
$user_id = abs($user_id);
$group_id = filter_input(INPUT_POST, 'group_id', FILTER_SANITIZE_NUMBER_INT);
$group_id = abs($group_id);

要创建SQL语句,您可以使用它为所有user_id值生成尽可能多的占位符。

$user_ids  = $_POST["user_id"]; //this can be an array
$group_id = $_POST["group_id"];
$placeholders = implode(', ', array_fill(0, count($user_ids), "(?, ?)"));    
$sql = "INSERT IGNORE INTO users_groups (user_id, group_id) VALUES $placeholders";

然后绑定值,如果你使用pdo,像这样应该工作:

$stmt = $pdo->prepare($sql);
$i = 1;
foreach ($user_ids as $user_id) {
    $stmt->bindValue($i++, $user_id);
    $stmt->bindValue($i++, $group_id);
}

或者如果你正在使用mysqli,你可以尝试使用反射(取自php文档中的用户说明),该说明确实说明这需要php 5.3+,希望你有。

$stmt = $mysqli->prepare($sql);
$values[] = str_repeat('ii', count($user_ids));
foreach ($user_ids as $user_id) {
    $values[] = $user_id;
    $values[] = $group_id;
}
$ref = new ReflectionClass('mysqli_stmt');
$method = $ref->getMethod("bind_param");
$method->invokeArgs($stmt, $values);
$stmt->execute(); 

或者,因为我很固执,从php文档用户注意的事情似乎不工作,阅读更多后,我不知道它是如何工作的,考虑到php文档的ReflectionMethod::invokeArgs特别说

注意:如果函数有需要引用的实参,则它们必须是传递的实参列表中的引用。

则使用call_user_func_array可能会工作。

$stmt = $mysqli->prepare($sql);
$type = str_repeat('ii', count($user_ids));
foreach ($user_ids as $user_id) {
    $values[] = $user_id;
    $values[] = $group_id;
}
foreach ($values as $key => $value) {
    $value_references[$key] = &$values[$key];
}
call_user_func_array('mysqli_stmt_bind_param', 
    array_merge(array($stmt, $type), $value_references));

但是这太痛苦了。我真的很喜欢pdo。