2009-04-23 12 views
111

Estoy pasando un gran conjunto de datos en una tabla mysql a través de php utilizando comandos de inserción y me pregunto si es posible insertar aproximadamente 1000 filas a la vez a través de una consulta que no sea cada valor en el final de una cadena de una milla de largo y luego ejecutarlo. Estoy usando el marco codeigniter por lo que sus funciones también están disponibles para mí.insertar varias filas a través de una matriz php en mysql

+0

he dado respuesta de acuerdo a su pregunta para insertar múltiples filas de CodeIgniter. –

+0

@SomnathMuluk Gracias, sin embargo, hace tiempo que no necesito responder esta pregunta:) ... – toofarsideways

+0

Recomendaría usar la función insert_batch de CodeIgniter. Si usa una biblioteca, siempre trate de aprovechar sus fortalezas y estándares de codificación. –

Respuesta

202

Ensamblar una instrucción INSERT con varias filas es mucho más rápido en MySQL que una instrucción INSERT por fila.

Dicho esto, parece que podría encontrarse con problemas de manejo de cadenas en PHP, que es realmente un problema de algoritmo, no de lenguaje. Básicamente, cuando se trabaja con cadenas grandes, se debe minimizar la copia innecesaria. En primer lugar, esto significa que quiere evitar la concatenación. La forma más rápida y más eficiente desde el punto de vista de la memoria de construir una cadena grande, como insertar cientos de filas a la vez, es aprovechar la función implode() y la asignación de matriz.

$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)); 

La ventaja de este enfoque es que no copia y volver a copiar la instrucción SQL que hasta ahora haya reunido con cada uno de concatenación; en cambio, PHP hace esto una vez en la declaración implode(). Este es un gran triunfo.

Si tiene muchas columnas para armar, y una o más son muy largas, también podría construir un bucle interno para hacer lo mismo y usar implode() para asignar la cláusula de valores a la matriz externa.

+4

¡Gracias por eso! Por cierto, falta un corchete de cierre al final de la función si alguien está planeando copiarlo. mysql_real_query ('INSERT INTO table VALUES (texto, categoría)' .implode (','. $ sql)); – toofarsideways

+3

¡Gracias! Fijo. (A menudo hago eso ...) – staticsan

+0

También me acabo de dar cuenta, siendo tonto también la implosión (','. $ Sql)); debería ser implode (',', $ sql)); – toofarsideways

15

Puede preparar la consulta para insertar una fila usando la clase mysqli_stmt, y luego iterar sobre la matriz de datos. Algo así como:

$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(); 

Donde '' idsb son los tipos de los datos que está vinculante (int, double, string, gota).

+3

Recientemente ejecuté algunos benchmarks comparando la inserción masiva y las declaraciones de inserción preparadas como se menciona aquí. Para alrededor de 500 inserciones, el método de inserciones preparadas se completa entre 2.6-4.4 segundos y el método de inserción masiva en 0.12-0.35 segundos. Hubiera pensado que mysql habría "agrupado" estas declaraciones preparadas juntas y funcionara tan bien como las inserciones masivas, pero en una configuración predeterminada, la diferencia de rendimiento es enorme aparentemente. (Por cierto, todas las consultas de referencia se ejecutaban dentro de una sola transacción para cada prueba, para evitar el autocompromiso) – Motin

5

Bueno, usted no desea ejecutar 1.000 llamadas de consulta, pero haciendo esto está muy bien:

$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); 

Dependiendo de su fuente de datos, poblar la matriz podría ser tan fácil como abrir un archivo y el vertido contenidos en una matriz a través de file().

+1

Es más claro si mueve eso si está por encima de la consulta y cámbielo a algo como si ($ k> 0). – cherouvim

+0

@cherouvim ... umm, gracias? Esta publicación es de hace más de un año ... – bdl

+1

Sí. La información permanece para siempre :) – cherouvim

7

Usted podría utilizar siempre de LOAD DATA mysql:

LOAD DATA LOCAL INFILE '/full/path/to/file/foo.csv' INTO TABLE `footable` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n' 

a hacer las inserciones en lugar de utilizar un montón de INSERT declaraciones.

+0

Había investigado eso, pero necesito manipular los datos antes de insertarlos. Se me ha dado como un producto cartesiano de un conjunto de 1400 por 1400 valores int muchos de los cuales son cero. Necesito convertir eso en una relación de muchos a muchos utilizando una tabla intermedia para ahorrar espacio, por lo tanto, la necesidad de inserciones en oposición a una inserción masiva – toofarsideways

+0

Siempre puede generar un archivo csv después de manipularlo y llamar a la instrucción mysql que carga los datos –

+0

Creo que es útil saber que la ruta es local para su cliente SQL, y no en el servidor SQL. El archivo se carga en el servidor y luego lo lee. Pensé que el archivo ya debía estar en el servidor, que no es el caso. Si ya está en el servidor, elimine el bit 'LOCAL'. – Kyle

13

Sé que esto es una consulta de edad, pero yo estaba leyendo y que me gustaría añadir lo que he encontrado en otro lugar:

mysqli en PHP 5 es una ojbect con algunas buenas funciones que permitirán incrementar la velocidad el tiempo de inserción para la respuesta anterior:

$mysqli->autocommit(FALSE); 
$mysqli->multi_query($sqlCombined); 
$mysqli->autocommit(TRUE); 

Desactivación de confirmación automática al insertar muchas filas en gran medida acelera la inserción, por lo desactivarlo, a continuación, ejecutar como se mencionó anteriormente, o simplemente hacer una cadena (sqlCombined) que es muchos inserto las declaraciones separadas por punto y coma y la consulta múltiple las manejarán bien.

Espero que esto ayude a alguien a ahorrar tiempo (buscando e insertando!)

R

+0

Este es el error que obtuve al usar su idea: "Error fatal: Llamar a una función miembro autocommit() en null en /homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.php en la línea 30" – user3217883

53

múltiple inserto/lote inserto está ahora apoyado por CodeIgniter. Tuve el mismo problema Aunque es muy tarde para responder la pregunta, ayudará a alguien. Es por eso que estoy respondiendo esta pregunta.

$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') 
+1

Creo que este es el La forma más recomendada de hacer varias filas inserta más bien usando mysql_query. Porque cuando utilizamos un marco, es una buena práctica usar siempre las funciones incorporadas del marco. –

3
$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)); 
-3
use this in codeigniter for multiple data insertion 


$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') 
+3

¿Y cómo es esto diferente de la respuesta de Somnath? De hecho, es una copia al carbón – asprin

0

He creado una clase que realiza múltiples líneas que se utiliza de la siguiente manera:

$pdo->beginTransaction(); 
$pmi = new PDOMultiLineInserter($pdo, "foo", array("a","b","c","e"), 10); 
$pmi->insertRow($data); 
// .... 
$pmi->insertRow($data); 
$pmi->purgeRemainingInserts(); 
$pdo->commit(); 

donde la clase se define como sigue:

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; 
    } 
} 
0

Utilice el lote de inserción en codeigniter para insertar varias filas de d ata

$this->db->insert_batch('tabname',$data_array); // $data_array holds the value to be inserted 
0

Puedes hacerlo de varias formas en Codeigniter, p.

First By loop

foreach($myarray as $row) 
{ 
    $data = array("first"=>$row->first,"second"=>$row->sec); 
    $this->db->insert('table_name',$data); 
} 

Second -- By insert batch

$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); 

Third way -- By multiple value pass

$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)); 
0

He creado esta función simple que ustedes pueden utilizar fácilmente. Tendrá que pasar el nombre de la tabla ($tbl), campo de tabla ($insertFieldsArr) con sus datos de inserción, matriz de datos ($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)); 
} 
0

Aunque es demasiado tarde para responder esta pregunta. Aquí está mi respuesta sobre lo mismo.

Si está utilizando CodeIgniter, puede utilizar métodos incorporados definidos en la clase query_builder.

$ this-> db-> insert_batch()

genera una cadena de inserción sobre la base de los datos que proporcione, y se ejecuta la consulta. Puede pasar una matriz o un objeto a la función. Aquí hay un ejemplo usando una matriz:

$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') 

El primer parámetro contendrá el nombre de la tabla, el segundo es una matriz asociativa de valores.

Puede encontrar más detalles sobre query_builder here

Cuestiones relacionadas