sql >> データベース >  >> RDS >> Mysql

MySQLi:1つのプリペアドステートメントで複数の行を挿入する

    一括挿入ステートメントクエリをその場で作成することで準備することは可能ですが、いくつかのトリックが必要です。最も重要なビットは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の長さと一致する必要があります ほとんどの場合、特にそれらのいくつかが異なる場合。
    • 失敗する3つの方法を区別しません。また、成功した挿入の数を追跡したり、エラー後に続行しようとしたりすることもありません。

    この関数を定義すると、サンプルコードを次のように置き換えることができます。

    $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()を書き換えることも可能です 多次元配列を受け入れて、潜在的なエラーの1つの原因を排除しますが、それは、配列をチャンク化した後に平坦化する必要があります。




    1. phpを使用してXMLデータをmysqlに挿入します

    2. 動的な値を使用したMySql条件付き挿入

    3. 単純なMySQLデータベース設計に関するヒントが必要

    4. MySQL COUNT()およびnull