inserisci più righe tramite un array php in mysql


129

Sto passando un set di dati di grandi dimensioni in una tabella MySQL tramite PHP utilizzando i comandi di inserimento e mi chiedo se sia possibile inserire circa 1000 righe alla volta tramite una query diversa dall'aggiunta di ciascun valore alla fine di una stringa lunga un miglio e quindi eseguendolo. Sto usando il framework CodeIgniter quindi le sue funzioni sono disponibili anche per me.


Ho dato una risposta in base alla tua domanda per l'inserimento di più righe di Codeigniter.
Somnath Muluk,

@SomnathMuluk Grazie, comunque è da un po 'che non ho bisogno di rispondere a questa domanda
:)

Consiglierei di usare la funzione insert_batch di CodeIgniter. Se usi una libreria, cerca sempre di sfruttare i suoi punti di forza e gli standard di codifica.
Dewald Els,

Credo che inserire batch sia il modo migliore per farlo, vedere il link stackoverflow.com/questions/27206178/codeigniter-insert-batch
Syed Amir Bukhari

Risposte:


234

L'assemblaggio di INSERTun'istruzione con più righe è molto più veloce in MySQL di unoINSERT un'istruzione per riga.

Detto questo, sembra che potresti riscontrare problemi di gestione delle stringhe in PHP, che è in realtà un problema di algoritmo, non di linguaggio. Fondamentalmente, quando si lavora con stringhe di grandi dimensioni, si desidera ridurre al minimo le copie non necessarie. In primo luogo, questo significa che vuoi evitare la concatenazione. Il modo più rapido ed efficiente in termini di memoria per creare una stringa di grandi dimensioni, ad esempio per inserire centinaia di righe in una, è sfruttare la implode()funzione e l'assegnazione dell'array.

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

Il vantaggio di questo approccio è che non si copia e si ricopia l'istruzione SQL che è stata finora assemblata con ciascuna concatenazione; invece, PHP fa una volta nella implode()dichiarazione. Questa è una grande vittoria.

Se hai molte colonne da mettere insieme e una o più sono molto lunghe, puoi anche creare un ciclo interno per fare la stessa cosa e usarlo implode()per assegnare la clausola dei valori all'array esterno.


5
Grazie per quello! Tra parentesi manca una parentesi quadra chiusa alla fine della funzione se qualcuno ha intenzione di copiarla. mysql_real_query ('INSERT INTO VALUES (text, category)' .implode (','. $ sql));
toofarsideways il

3
Grazie! Fisso. (Lo faccio spesso ...)
staticsan,

1
e la query dovrebbe davvero essere 'INSERT INTO table (text, category) VALUES' .implode (','. $ sql) 'sigh 4am coding porta a un terribile debug :(
toofarsideways,

3
Credo che questo codice creerà una soluzione per il mio ultimo progetto. La mia domanda qui è, è sicuro dall'iniezione SQL? I miei piani sono di cambiare mysql_real_escape_stringcon mysqli_real_escape_stringe mysql_querycon mysqli_querycome sto usando MySQLi e questi sono stati deprecati a partire da PHP5. Grazie molto!
wordman

2
mysql_*è stato rimosso da PHP, quindi assicurati di utilizzare l' mysqli_*interfaccia.
Rick James,

60

L'inserimento multiplo / inserimento batch è ora supportato da codeigniter. Ho avuto lo stesso problema. Anche se è molto tardi per rispondere alla domanda, aiuterà qualcuno. Ecco perché rispondere a questa domanda.

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

2
Penso che questo sia il modo più consigliato di fare l'inserimento di più righe piuttosto che usare mysql_query. Perché quando utilizziamo un framework è buona norma utilizzare sempre le funzionalità integrate del framework.
Praneeth Nidarshan,

22

È possibile preparare la query per l'inserimento di una riga utilizzando la classe mysqli_stmt e quindi scorrere sull'array di dati. Qualcosa di simile a:

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

Dove "idsb" sono i tipi di dati che si stanno legando (int, double, string, blob).


6
Di recente ho eseguito alcuni benchmark comparando l'inserto di massa e preparato istruzioni di inserimento come menzionato qui. Per circa 500 inserti, il metodo di inserimento preparato è stato completato tra 2,6-4,4 secondi e il metodo di inserimento bulk in 0,12-0,35 secondi. Avrei pensato che mysql avrebbe "ammassato" insieme queste dichiarazioni preparate ed eseguito allo stesso modo degli inserti di massa, ma in una configurazione predefinita, la differenza di prestazioni è apparentemente enorme. (A proposito, tutte le query di riferimento sono state eseguite all'interno di una singola transazione per ciascun test, al fine di impedire il commit automatico)
Motin

16

So che questa è una vecchia query, ma stavo solo leggendo e ho pensato di aggiungere ciò che ho trovato altrove:

mysqli in PHP 5 è un oggetto con alcune buone funzioni che ti permetteranno di velocizzare i tempi di inserimento della risposta sopra:

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

La disattivazione di autocommit quando si inseriscono molte righe accelera notevolmente l'inserimento, quindi disattivarlo, quindi eseguirlo come indicato sopra, o semplicemente creare una stringa (sqlCombined) che è molte istruzioni di inserimento separate da punti e virgola e le query multiple li gestiranno bene.

Spero che questo aiuti qualcuno a risparmiare tempo (ricerca e inserimento!)

R


Questo è l'errore che ho ottenuto usando la tua idea: "Errore irreversibile: chiamata a una funzione membro autocommit () su null in /homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.php sulla linea 30"
user3217883

8

Puoi sempre usare mysql LOAD DATA:

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

fare inserimenti di massa piuttosto che usare un sacco di INSERTaffermazioni.


Ci avevo pensato ma devo manipolare i dati prima di inserirli. Mi è stato dato come prodotto cartesiano di un set di valori int 1400 per 1400, molti dei quali sono zero. Devo convertirlo in una relazione da molte a molte usando una tabella intermedia per risparmiare spazio, quindi la necessità di inserti rispetto a un inserto di massa
toofarsideways,

Puoi sempre generare un file CSV dopo averlo manipolato e aver chiamato l'istruzione mysql che carica i dati
Alexander Jardim,

Penso che sia utile sapere che il percorso è locale per il tuo client SQL e non sul server SQL. Il file viene caricato sul server e quindi letto da esso. Pensavo che il file dovesse essere già sul server, il che non è il caso. Se è già sul server, rimuovere il LOCALbit.
Kyle,

5

Bene, non vuoi eseguire 1000 chiamate di query, ma farlo va bene:

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

A seconda dell'origine dati, popolare l'array potrebbe essere facile come aprire un file e scaricare il contenuto in un array tramite file().


1
È più pulito se lo sposti se sopra la query e lo cambi in qualcosa come if ($ k> 0).
Cherouvim,

@cherouvim ... Beh, hai ragione. Grazie per il tuo contributo. Mentre rileggo l'esempio che ho fornito, non riesco a vedere il tuo punto. Cura di elaborare (via pastebin, ecc?). Grazie-
bdl,

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

1

Puoi farlo in diversi modi in codeigniter ad es

Primo ciclo

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

Secondo : per inserimento 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);

Terza via: per passaggio di più valori

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

1

Anche se è troppo tardi per rispondere a questa domanda. Ecco la mia risposta sullo stesso.

Se si utilizza CodeIgniter, è possibile utilizzare i metodi integrati definiti nella classe query_builder.

$ This-> db-> insert_batch ()

Genera una stringa di inserimento in base ai dati forniti ed esegue la query. È possibile passare un array o un oggetto alla funzione. Ecco un esempio usando un array:

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

Il primo parametro conterrà il nome della tabella, il secondo è una matrice associativa di valori.

Puoi trovare maggiori dettagli su query_builder qui


0

Ho creato una classe che esegue multilinea che viene utilizzata come segue:

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

dove la classe è definita come segue:

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

Utilizzare Inserisci batch nel codeigniter per inserire più righe di dati.

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

0

Ho creato questa semplice funzione che potete usare facilmente. Dovrai passare il nome della ($tbl)tabella, il campo della tabella ($insertFieldsArr)contro l'inserimento dei dati, l'array di dati ($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));
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.