Je passe un grand ensemble de données dans une table MySQL via PHP à l'aide de commandes d'insertion et je me demande s'il est possible d'insérer environ 1000 lignes à la fois via une requête autre que l'ajout de chaque valeur au bout d'un kilomètre longue chaîne et ensuite l'exécuter. J'utilise le framework CodeIgniter afin que ses fonctions me soient également disponibles.
Assembler une instruction INSERT
avec plusieurs lignes est beaucoup plus rapide dans MySQL qu'une instruction INSERT
par ligne.
Cela dit, il semblerait que vous ayez peut-être des problèmes de traitement des chaînes en PHP, qui est en réalité un problème d’algorithme et non de langage. Fondamentalement, lorsque vous travaillez avec de grandes chaînes, vous voulez minimiser les copies inutiles. Cela signifie principalement que vous souhaitez éviter la concaténation. Le moyen le plus rapide et le moins gourmand en mémoire de construire une chaîne de grande taille, par exemple pour insérer des centaines de lignes en une, consiste à tirer parti de la fonction implode()
et de l'affectation de tableau.
$sql = array();
foreach( $data as $row ) {
$sql[] = '("'.mysql_real_escape_string($row['text']).'", '.$row['category_id'].')';
}
mysql_query('INSERT INTO table (text, category) VALUES '.implode(',', $sql));
L'avantage de cette approche est que vous ne copiez pas et ne recopiez pas l'instruction SQL que vous avez assemblée jusqu'à présent avec chaque concaténation. Au lieu de cela, PHP effectue ceci une fois dans l'instruction implode()
. C'est une grosse victoire.
Si vous avez beaucoup de colonnes à assembler et qu'une ou plusieurs sont très longues, vous pouvez également créer une boucle interne pour faire la même chose et utiliser implode()
pour affecter la clause values au tableau externe.
Plusieurs insert/batch insert est maintenant supporté par codeigniter. J'ai eu le même problème. Bien qu'il soit très tard pour répondre à la question, cela aidera quelqu'un. C'est pourquoi répondre à cette question.
$data = array(
array(
'title' => 'My title' ,
'name' => 'My Name' ,
'date' => 'My date'
),
array(
'title' => 'Another title' ,
'name' => 'Another Name' ,
'date' => 'Another date'
)
);
$this->db->insert_batch('mytable', $data);
// Produces: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date'), ('Another title', 'Another name', 'Another date')
Vous pouvez préparer la requête pour l'insertion d'une ligne à l'aide de la classe mysqli_stmt, puis effectuer une itération sur le tableau de données. Quelque chose comme:
$stmt = $db->stmt_init();
$stmt->prepare("INSERT INTO mytbl (fld1, fld2, fld3, fld4) VALUES(?, ?, ?, ?)");
foreach($myarray as $row)
{
$stmt->bind_param('idsb', $row['fld1'], $row['fld2'], $row['fld3'], $row['fld4']);
$stmt->execute();
}
$stmt->close();
Où 'idsb' représente les types de données que vous liez (int, double, string, blob).
Je sais que c'est une vieille requête, mais je lisais et pensais ajouter ce que j'ai trouvé ailleurs:
mysqli in PHP 5 est un objet contenant de bonnes fonctions qui vous permettra d’accélérer le temps d’insertion de la réponse ci-dessus:
$mysqli->autocommit(FALSE);
$mysqli->multi_query($sqlCombined);
$mysqli->autocommit(TRUE);
Désactiver la validation automatique lors de l’insertion de nombreuses lignes accélère considérablement l’insertion; désactivez-le, puis exécutez-le comme indiqué ci-dessus ou créez une chaîne (sqlCombined) composée de nombreuses instructions d’insertion séparées par des points-virgules;.
J'espère que cela aide quelqu'un à gagner du temps (recherche et insertion!)
R
Vous pouvez toujours utiliser le LOAD DATA
de mysql:
LOAD DATA LOCAL INFILE '/full/path/to/file/foo.csv' INTO TABLE `footable` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n'
faire des insertions en bloc plutôt que d'utiliser un tas d'instructions INSERT
.
Eh bien, vous ne voulez pas exécuter 1000 appels de requête, mais c'est très bien comme ça:
$stmt= array( 'array of statements' );
$query= 'INSERT INTO yourtable (col1,col2,col3) VALUES ';
foreach( $stmt AS $k => $v ) {
$query.= '(' .$v. ')'; // NOTE: you'll have to change to suit
if ( $k !== sizeof($stmt)-1 ) $query.= ', ';
}
$r= mysql_query($query);
Selon votre source de données, remplir le tableau peut être aussi simple qu'ouvrir un fichier et vider son contenu dans un tableau via file()
.
$query= array();
foreach( $your_data as $row ) {
$query[] = '("'.mysql_real_escape_string($row['text']).'", '.$row['category_id'].')';
}
mysql_query('INSERT INTO table (text, category) VALUES '.implode(',', $query));
J'ai créé cette fonction simple que vous pouvez utiliser facilement. Vous devrez passer le nom de table ($tbl)
, le champ de table ($insertFieldsArr)
à vos données d'insertion, tableau de données ($arr)
.
insert_batch('table',array('field1','field2'),$dataArray);
function insert_batch($tbl,$insertFieldsArr,$arr){ $sql = array();
foreach( $arr as $row ) {
$strVals='';
$cnt=0;
foreach($insertFieldsArr as $key=>$val){
if(is_array($row)){
$strVals.="'".mysql_real_escape_string($row[$cnt]).'\',';
}
else{
$strVals.="'".mysql_real_escape_string($row).'\',';
}
$cnt++;
}
$strVals=rtrim($strVals,',');
$sql[] = '('.$strVals.')';
}
$fields=implode(',',$insertFieldsArr);
mysql_query('INSERT INTO `'.$tbl.'` ('.$fields.') VALUES '.implode(',', $sql));
}
Utilisez insert batch dans codeigniter pour insérer plusieurs lignes de données.
$this->db->insert_batch('tabname',$data_array); // $data_array holds the value to be inserted
Vous pouvez le faire de plusieurs manières dans codeigniter, par exemple.
Premier Par boucle
foreach($myarray as $row)
{
$data = array("first"=>$row->first,"second"=>$row->sec);
$this->db->insert('table_name',$data);
}
Deuxième - Par lot d'insertion
$data = array(
array(
'first' => $myarray[0]['first'] ,
'second' => $myarray[0]['sec'],
),
array(
'first' => $myarray[1]['first'] ,
'second' => $myarray[1]['sec'],
),
);
$this->db->insert_batch('table_name', $data);
Troisième voie - Par passes de valeurs multiples
$sql = array();
foreach( $myarray as $row ) {
$sql[] = '("'.mysql_real_escape_string($row['first']).'", '.$row['sec'].')';
}
mysql_query('INSERT INTO table (first, second) VALUES '.implode(',', $sql));
Bien qu'il soit trop tard pour répondre à cette question. Voici ma réponse sur le même.
Si vous utilisez CodeIgniter, vous pouvez utiliser les méthodes intégrées définies dans la classe query_builder.
$ this-> db-> insert_batch ()
Génère une chaîne d'insertion basée sur les données que vous fournissez et exécute la requête. Vous pouvez soit transmettre un tableau ou un objet à la fonction. Voici un exemple utilisant un tableau:
$data = array(
array(
'title' => 'My title',
'name' => 'My Name',
'date' => 'My date'
),
array(
'title' => 'Another title',
'name' => 'Another Name',
'date' => 'Another date'
)
)
$this->db->insert_batch('mytable', $data);
// Produces: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date'), ('Another title', 'Another name', 'Another date')
Le premier paramètre contiendra le nom de la table, le second est un tableau associatif de valeurs.
Vous pouvez trouver plus de détails sur query_builder ici
J'ai créé une classe multi-lignes utilisée comme suit:
$pdo->beginTransaction();
$pmi = new PDOMultiLineInserter($pdo, "foo", array("a","b","c","e"), 10);
$pmi->insertRow($data);
// ....
$pmi->insertRow($data);
$pmi->purgeRemainingInserts();
$pdo->commit();
où la classe est définie comme suit:
class PDOMultiLineInserter {
private $_purgeAtCount;
private $_bigInsertQuery, $_singleInsertQuery;
private $_currentlyInsertingRows = array();
private $_currentlyInsertingCount = 0;
private $_numberOfFields;
private $_error;
private $_insertCount = 0;
/**
* Create a PDOMultiLine Insert object.
*
* @param PDO $pdo The PDO connection
* @param type $tableName The table name
* @param type $fieldsAsArray An array of the fields being inserted
* @param type $bigInsertCount How many rows to collect before performing an insert.
*/
function __construct(PDO $pdo, $tableName, $fieldsAsArray, $bigInsertCount = 100) {
$this->_numberOfFields = count($fieldsAsArray);
$insertIntoPortion = "REPLACE INTO `$tableName` (`".implode("`,`", $fieldsAsArray)."`) VALUES";
$questionMarks = " (?".str_repeat(",?", $this->_numberOfFields - 1).")";
$this->_purgeAtCount = $bigInsertCount;
$this->_bigInsertQuery = $pdo->prepare($insertIntoPortion.$questionMarks.str_repeat(", ".$questionMarks, $bigInsertCount - 1));
$this->_singleInsertQuery = $pdo->prepare($insertIntoPortion.$questionMarks);
}
function insertRow($rowData) {
// @todo Compare speed
// $this->_currentlyInsertingRows = array_merge($this->_currentlyInsertingRows, $rowData);
foreach($rowData as $v) array_Push($this->_currentlyInsertingRows, $v);
//
if (++$this->_currentlyInsertingCount == $this->_purgeAtCount) {
if ($this->_bigInsertQuery->execute($this->_currentlyInsertingRows) === FALSE) {
$this->_error = "Failed to perform a multi-insert (after {$this->_insertCount} inserts), the following errors occurred:".implode('<br/>', $this->_bigInsertQuery->errorInfo());
return false;
}
$this->_insertCount++;
$this->_currentlyInsertingCount = 0;
$this->_currentlyInsertingRows = array();
}
return true;
}
function purgeRemainingInserts() {
while ($this->_currentlyInsertingCount > 0) {
$singleInsertData = array();
// @todo Compare speed - http://www.evardsson.com/blog/2010/02/05/comparing-php-array_shift-to-array_pop/
// for ($i = 0; $i < $this->_numberOfFields; $i++) $singleInsertData[] = array_pop($this->_currentlyInsertingRows); array_reverse($singleInsertData);
for ($i = 0; $i < $this->_numberOfFields; $i++) array_unshift($singleInsertData, array_pop($this->_currentlyInsertingRows));
if ($this->_singleInsertQuery->execute($singleInsertData) === FALSE) {
$this->_error = "Failed to perform a small-insert (whilst purging the remaining rows; the following errors occurred:".implode('<br/>', $this->_singleInsertQuery->errorInfo());
return false;
}
$this->_currentlyInsertingCount--;
}
}
public function getError() {
return $this->_error;
}
}