web-dev-qa-db-fra.com

php PDO insérer lot plusieurs lignes avec des espaces réservés

Je cherche à faire plusieurs insertions en utilisant PHP PDO.

La réponse la plus proche que j'ai trouvée est celle-ci

comment-insérer-un-tableau-dans-une-unique-mysql-prepare-statement

Cependant, l'exemple qui a été donné utilise ?? au lieu de véritables espaces réservés.

J'ai consulté les exemples du PHP doc site pour les substituts

php.net pdo.prepared-statement

$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);

Maintenant, disons que je voulais réaliser ce qui précède, mais avec un tableau

$valuesToInsert = array(
  0 => array('name' => 'Robert', 'value' => 'some value'),
  1 => array('name' -> 'Louise', 'value' => 'another value')
);

Comment le ferais-je avec PDO et plusieurs insertions par transaction?

J'imagine que ça commencerait par une boucle?

$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");

foreach($valuesToInsert as $insertRow){

    // now loop through each inner array to match binded values
    foreach($insertRow as $column => value){
        $stmt->bindParam(":{$column}", value);
    }
}
$stmt->execute();

Cependant, ce qui précède ne fonctionne pas, mais nous espérons pouvoir démontrer ce que je cherche à atteindre.

18
Robbo_UK

Tout d’abord, les symboles ? sont les espaces réservés réels (la plupart des pilotes permettent d’utiliser les deux syntaxes, les espaces réservés et les espaces réservés nommés). Deuxièmement, les instructions préparées ne sont qu'un outil pour injecter une entrée brute dans les instructions SQL - la syntaxe de l'instruction SQL elle-même n'est pas affectée. Vous avez déjà tous les éléments dont vous avez besoin:

  • Comment insérer plusieurs lignes avec une seule requête
  • Comment générer du SQL dynamiquement
  • Comment utiliser des instructions préparées avec des espaces réservés nommés.

C'est assez trivial de les combiner tous:

$sql = 'INSERT INTO table (memberID, programID) VALUES ';
$insertQuery = array();
$insertData = array();
$n = 0;
foreach ($data as $row) {
    $insertQuery[] = '(:memberID' . $n . ', :programID' . $n . ')';
    $insertData['memberID' . $n] = $memberid;
    $insertData['programID' . $n] = $row;
    $n++;
}

if (!empty($insertQuery)) {
    $sql .= implode(', ', $insertQuery);
    $stmt = $db->prepare($sql);
    $stmt->execute($insertData);
}
25
Álvaro González

Je suppose que vous utilisez InnoDB, donc cette réponse n'est valide que pour ce moteur (ou tout autre moteur capable de transaction, ce qui signifie que MyISAM n'est pas inclus).

Par défaut, InnoDB s’exécute en mode de validation automatique. Cela signifie que chaque requête est traitée comme sa propre transaction contenue.

Pour traduire cela en quelque chose que nous, mortels, pouvons comprendre, cela signifie que chaque requête INSERT que vous émettez forcera le disque dur à le valider en confirmant qu'il a écrit les informations de la requête. Compte tenu de la lenteur avec laquelle les disques durs mécaniques sont très lents, car le nombre d'opérations d'entrée-sortie par seconde est faible (si je ne me trompe pas, la moyenne est de 300 io), cela signifie que vos 50 000 requêtes seront - eh bien, super lent.

Donc que fais-tu? Vous validez toutes vos requêtes de 50 000 en une seule transaction. Ce n'est peut-être pas la meilleure solution pour différents objectifs, mais ce sera rapide.

Vous le faites comme ça: 

$dbh->beginTransaction();

$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");

foreach($valuesToInsert as $insertRow)
{    
    // now loop through each inner array to match bound values
    foreach($insertRow as $column => value)
    {
        $stmt->bindParam(":$column", value);
        $stmt->execute();
    }
}


$dbh->commit();
7
N.B.

Quelques modifications en solution fournies par N.B 
$ stmt-> execute () devrait être en dehors de la boucle interne car vous pourriez avoir une ou plusieurs colonnes à lier avant d'appeler $ stmt-> execute (), sinon vous obtiendrez une exception "Numéro de paramètre non valide: Le nombre de variables liées ne correspond pas au nombre de jetons ".
La deuxième variable "valeur" correspondait à l'absence de signes dollar.

function batchinsert($sql,$params){
    try { 
                db->beginTransaction(); 

                $stmt = db->prepare($sql);

                foreach($params as $row)
                {    
                    // now loop through each inner array to match bound values
                    foreach($row as $column => $value)
                    {                           
                        $stmt->bindParam(":$column", $value);                           
                    }
                    $stmt->execute();
                }                                       
                db->commit();                   

        } catch(PDOExecption $e) {
            $db->rollback();                
        }
}

Tester:

$sql = "INSERT INTO `test`(`name`, `value`) VALUES (:name, :value)" ;

$data = array();    

array_Push($data, array('name'=>'Name1','value'=>'Value1')); 

array_Push($data, array('name'=>'Name2','value'=>'Value2')); 

array_Push($data, array('name'=>'Name3','value'=>'Value3')); 

array_Push($data, array('name'=>'Name4','value'=>'Value4')); 

array_Push($data, array('name'=>'Name5','value'=>'Value5')); 

batchinsert($sql,$data);
1
junto