【问题标题】:How to make prepared statement with multi_query in PHP如何在 PHP 中使用 multi_query 制作准备好的语句
【发布时间】:2020-01-28 08:01:22
【问题描述】:

我目前正在尝试使用 php 和 mysqli 将有关用户的一些元数据发布到 mysql db。我检索了一个数组,其中的键/值对对应于它们应该存储在数据库中的键/值对。

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = mysqli_connect("127.0.0.1", "root", "", "test");

    foreach($metaData as $key => $value) {
        $metaDataSql .= "insert into usermeta (user_id, meta_key, meta_value) values ($user_id, '$key', '$value');";
    }

    if(!$conn->multi_query($metaDataSql)) {
        echo("Failed to execute the queries." . $conn->error);
    }

    mysqli_close($conn);
}

我的下一步是防止 SQL 注入。我已经尝试过,但失败了,用准备好的语句在上面制作这个方法。这是我的尝试:

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = mysqli_connect("127.0.0.1", "root", "", "test");

    foreach($metaData as $key => $value) {
        $metaDataSql .= $conn->prepare("insert into usermeta (user_id, meta_key, meta_value) values (?, '?', '?');");
        $metaDataSql->bind_param("iss", $user_id, $key, $value);
    }

    if(!$conn->multi_query($metaDataSql)) {
        echo("Failed to execute the queries." . $conn->error);
    }

    mysqli_close($conn);
}

甚至可以这样做吗?如果不是,那么在防止 SQL 注入的同时处理对数据库的多次插入的最佳方法是什么。

【问题讨论】:

  • 为什么要多查询?只需执行与记录一样多的查询。甚至INSERT INTO VALUES (), (), ()。此外,占位符不得用引号引起来,请重读手册。
  • 我想让它成为一个多查询,这样我就可以避免对数据库进行 10 多个单独的插入查询。我在想这会提高性能/减少开销。还可以在将来更轻松地添加更多元数据,而无需编辑此方法 - 并且只需编辑数组。
  • 您仍然有 10 多个查询,但现在只有一个字符串,它们仍然会被一一执行。

标签: php mysql mysqli


【解决方案1】:

您无法准备 mysqli::multi_query - 但使用准备好的语句,您可以使用不同的值迭代相同的查询。这比迭代和运行标准 query() 更加优化。

mysqli_stmt::bind_param() 是通过引用,所以你只需要绑定一次,每次迭代都执行它。

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = new mysqli("127.0.0.1", "root", "", "test");
    $stmt = $conn->prepare("INSERT INTO usermeta (user_id, meta_key, meta_value) VALUES (?, ?, ?");
    $stmt->bind_param("iss", $user_id, $key, $value);
    foreach($metaData as $key => $value) {
        $stmt->execute();
    }
    $stmt->close();
    $conn->close();
}

如果您只想执行一次,也可以构建一个查询。这不一定比上面的快很多,但可以稍微快一点。使用解包操作符(或“splat 操作符”)... 来解压绑定它的整个数组。

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = new mysqli("127.0.0.1", "root", "", "test");
    $query = "INSERT INTO usermeta (user_id, meta_key, meta_value) VALUES ";
    $data = [];

    // Build the query, by appending values and inserting into the $data array
    foreach($metaData as $key => $value) {
        $query .= "(?, ?, ?), ";
        $data[] = $user_id;
        $data[] = $key;
        $data[] = $value;
    }

    // Trim away trailing comma
    $query = rtrim($query, ",");

    // If there are any data to bind, we can execute the query
    if (count($data)) {
        $stmt = $conn->prepare($query);
        $stmt->bind_param(str_repeat("iss", count($metaData)), ...$data);
        $stmt->execute();
        $stmt->close();
    }
    $conn->close();
}

旁注
在函数中初始化连接通常是一个非常糟糕的主意!您应该将全局连接作为参数传递给函数。现在,每次您使用该功能时都会建立一个新连接。

【讨论】:

  • 我更喜欢你的第一种方法!虽然第二个可能会稍微快一点,但第一个更容易理解。
  • 谢谢。你的第一种方法正是我想要的。并且连接不正确
  • @Nick 我同意!我也更喜欢第一个解决方案,但这只是为了表明它可以通过单个查询来实现(如前所述,它可能不一定比第一个更好......)。
  • 我还要补充一点,如果有很多插入,值得将它们包装在事务中。可以大大提高性能。
  • 如果我可以回到边注@Qirel,我已经对建立数据库连接的最佳实践进行了一些研究。 Afaik 你的建议是依赖注入,这似乎是最好的选择,但你认为我应该在哪里创建全局连接?我很难研究它,因为我发现的每个资源都是用 oop 编写的,而不是 prodedural php。
猜你喜欢
  • 2011-06-20
  • 2011-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-28
  • 1970-01-01
  • 2015-06-27
  • 1970-01-01
相关资源
最近更新 更多