库MySQLi:一个准备好的声明中插入多行(MySQLi : Inserting multiple

2019-07-19 20:40发布

我做了一个创建原始查询字符串,然后插入数百行的一个语句的脚本。 它的工作原理,但不提供该预处理语句做保护。 然后我修改我的脚本中加入准备好的发言。 它的工作原理但是,它是显著慢。 预处理语句的脚本需要更长的时间来插入行比原始查询脚本,由于通过每个准备插入语句一行在同一时间运行,而不是在一个时间插入行百脚本。

下面是预处理语句代码的一个片段:

for( $j = 0; $j < $abilitiesMax - 2; $j++ ){
  $stmtAbility->bind_param('iiiii', $abilityArray[$i]["match_id"] , $abilityArray[$i]["player_slot"],
  $abilityArray[$i][$j]["ability"], $abilityArray[$i][$j]["time"], $abilityArray[$i][$j]["level"] );

  if(  !($stmtAbility->execute()) ){      
   echo "<p>$db->error</p>";
   echo "<p>ERROR: when trying to insert abilities query</p>";
  }
}

它能够完成任务,但只有在数百数百插件后。 有没有绑定列表或数组的bind_param()的参数,只是运行$ stmtAbility->执行一次或可加速性能某些其他方法的一种方式。

很抱歉,如果这已被问和回答过。 我环顾四周,一段时间后发现,回答了我的要求明确一些类似的问题,但一无所获。

Answer 1:

这是可能通过构建它的飞行准备一个BULK INSERT语句的查询,但它需要一些技巧。 最重要的比特被使用str_pad()来构造可变长度的查询字符串,以及使用call_user_func_array()调用bind_param()与参数可变数目。

function insertBulkPrepared($db, $table, $fields, $types, $values) {
    $chunklength = 500;
    $fieldcount = count($fields);
    $fieldnames = '`'.join('`, `', $fields).'`';
    $prefix = "INSERT INTO `$table` ($fieldnames) VALUES ";
    $params = '(' . str_pad('', 3*$fieldcount - 2, '?, ') . '), ';
    $inserted = 0;

    foreach (array_chunk($values, $fieldcount*$chunklength) as $group) {
        $length = count($group);
        if ($inserted != $length) {
            if ($inserted) $stmt->close();
            $records = $length / $fieldcount;
            $query = $prefix . str_pad('', 3*$length + 2*($records - 1), $params);
            #echo "\n<br>Preparing '" . $query . "'";
            $stmt = $db->prepare($query);
            if (!$stmt) return false;
            $binding = str_pad('', $length, $types);
            $inserted = $length;
        }

        array_unshift($group, $binding);
        #echo "\n<br>Binding " . var_export($group, true);
        $bound = call_user_func_array(array($stmt, 'bind_param'), $group);
        if (!$bound) return false;
        if (!$stmt->execute()) return false;
    }

    if ($inserted) $stmt->close();
    return true;
}

此功能需要你的$db作为mysqli情况下,表名,字段名数组和引用的平面阵列值。 它插入多达500个记录每个查询,尽可能重新使用准备好的语句。 它返回true ,如果所有插入的成功,或false如果其中任何失败。 注意事项:

  • 该表和字段名都没有逃脱; 我把它留给你,以确保它们不包含反引号。 幸运的是,他们不应该来自于用户的输入。
  • 如果长度$values不是偶数倍的长度$fields ,最终块可能会失败的准备阶段。
  • 同样,长度$types参数应匹配的长度$fields在大多数情况下,特别是当他们中的一些不同。
  • 它并不三种方式失败区分。 它也没有跟踪的许多刀片是如何成功,也不会尝试错误后继续。

有了这个功能定义,你的示例代码可以像更换:

$inserts = array();
for ($j = 0; $j < $abilitiesMax - 2; $j++) {
    $inserts[] = &$abilityArray[$i]['match_id'];
    $inserts[] = &$abilityArray[$i]['player_slot'];
    $inserts[] = &$abilityArray[$i][$j]['ability'];
    $inserts[] = &$abilityArray[$i][$j]['time'];
    $inserts[] = &$abilityArray[$i][$j]['level'];
}

$fields = array('match_id', 'player_slot', 'ability', 'time', 'level');
$result = insertBulkPrepared($db, 'abilities', $fields, 'iiiii', $inserts);
if (!$result) {
    echo "<p>$db->error</p>";
    echo "<p>ERROR: when trying to insert abilities query</p>";
}

那些&符号是很重要的,因为mysqli_stmt::bind_param预计引用,这是不提供call_user_func_array在最近版本的PHP。

你没给我们最初准备的声明,所以你可能需要调整表和字段名。 它也像你的代码坐在一个循环里面超过$i ; 在这种情况下,只有for环需要是外环内。 如果走环外的其他线路,您将使用更多的内存构建$inserts阵列,以换取更有效的批量插入。

它也可以改写insertBulkPrepared()以接受一个多维阵列中,消除了潜在的误差的一个来源,但需要它分块后平坦化阵列。



文章来源: MySQLi : Inserting multiple rows with one prepared statement