Fügen Sie mehrere Zeilen über ein PHP-Array in MySQL ein

129

Ich übergebe ein großes Dataset mithilfe von Einfügebefehlen über PHP in eine MySQL-Tabelle und frage mich, ob es möglich ist, ungefähr 1000 Zeilen gleichzeitig über eine andere Abfrage einzufügen, als jeden Wert am Ende einer kilometerlangen Zeichenfolge anzuhängen und dann es ausführen. Ich verwende das CodeIgniter-Framework, damit mir auch seine Funktionen zur Verfügung stehen.

toofarsideways
quelle
Ich habe gemäß Ihrer Frage eine Antwort für die mehrzeilige Einfügung von Codeigniter gegeben.
Somnath Muluk
@ SomnathMuluk Vielen Dank, aber es ist schon eine Weile her, seit ich diese Frage beantworten musste
:)
Ich würde empfehlen, die Funktion insert_batch von CodeIgniter zu verwenden. Wenn Sie eine Bibliothek verwenden, versuchen Sie immer, deren Stärken und Codierungsstandards zu nutzen.
Dewald Els
Ich glaube, Batch einfügen ist der beste Weg, dies zu tun, siehe den Link stackoverflow.com/questions/27206178/codeigniter-insert-batch
Syed Amir Bukhari

Antworten:

234

Das Zusammenstellen einer INSERTAnweisung mit mehreren Zeilen ist in MySQL viel schneller als eine INSERTAnweisung pro Zeile.

Das heißt, es hört sich so an, als ob Sie in PHP auf Probleme beim Umgang mit Zeichenfolgen stoßen, was wirklich ein Algorithmusproblem ist, kein Sprachproblem. Grundsätzlich möchten Sie beim Arbeiten mit großen Zeichenfolgen unnötiges Kopieren minimieren. In erster Linie bedeutet dies, dass Sie eine Verkettung vermeiden möchten. Der schnellste und speichereffizienteste Weg, um eine große Zeichenfolge zu erstellen, z. B. um Hunderte von Zeilen gleichzeitig einzufügen, besteht darin, die implode()Funktions- und Arrayzuweisung zu nutzen.

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

Der Vorteil dieses Ansatzes besteht darin, dass Sie die SQL-Anweisung, die Sie bisher zusammengestellt haben, nicht bei jeder Verkettung kopieren und erneut kopieren. Stattdessen macht PHP dies einmal in der implode()Anweisung. Dies ist ein großer Gewinn.

Wenn Sie viele Spalten zusammenstellen müssen und eine oder mehrere sehr lang sind, können Sie auch eine innere Schleife erstellen, um dasselbe zu tun und implode()die Werteklausel dem äußeren Array zuzuweisen.

staticsan
quelle
5
Dank dafür! Übrigens fehlt Ihnen eine schließende Klammer am Ende der Funktion, wenn jemand vorhat, sie zu kopieren. mysql_real_query ('INSERT INTO table VALUES (Text, Kategorie)' .implode (','. $ sql));
toofarsideways
3
Vielen Dank! Fest. (Ich mache das oft ...)
staticsan
1
und die Abfrage sollte wirklich 'INSERT INTO table (Text, Kategorie) VALUES' sein .implode (','. $ sql) 'Seufzer 4 Uhr morgens Codierung führt zu schrecklichem Debugging :(
toofarsideways
3
Ich glaube, dieser Code wird eine Lösung für mein neuestes Projekt schaffen. Meine Frage hier ist, ist dies sicher vor SQL-Injection? Meine Pläne sind, mysql_real_escape_stringmit mysqli_real_escape_stringund mysql_querymit mysqli_queryMySQLi zu wechseln, und diese sind ab PHP5 veraltet. Danke vielmals!
Wordman
2
mysql_*wurde aus PHP entfernt, verwenden Sie also unbedingt die mysqli_*Schnittstelle.
Rick James
60

Mehrfacheinfügung / Stapeleinfügung wird jetzt vom Codeigniter unterstützt. Ich hatte das gleiche Problem. Obwohl es sehr spät ist, Fragen zu beantworten, wird es jemandem helfen. Deshalb beantworte ich diese Frage.

$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')
Somnath Muluk
quelle
2
Ich denke, dies ist die am meisten empfohlene Methode zum Einfügen mehrerer Zeilen, anstatt mysql_query zu verwenden. Denn wenn wir ein Framework verwenden, ist es eine gute Praxis, immer integrierte Funktionen des Frameworks zu verwenden.
Praneeth Nidarshan
22

Sie können die Abfrage zum Einfügen einer Zeile mit der Klasse mysqli_stmt vorbereiten und dann über das Datenarray iterieren. Etwas wie:

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

Wobei 'idsb' die Arten der Daten sind, die Sie binden (int, double, string, blob).

Espresso_Boy
quelle
6
Ich habe kürzlich einige Benchmarks durchgeführt, in denen Masseneinfügungen und vorbereitete Einfügeanweisungen verglichen wurden, wie hier erwähnt. Bei etwa 500 Einsätzen wurde die Methode für vorbereitete Einsätze zwischen 2,6 und 4,4 Sekunden und die Methode für Masseneinsätze in 0,12 bis 0,35 Sekunden abgeschlossen. Ich hätte gedacht, MySQL hätte diese vorbereiteten Anweisungen zusammen "geballt" und genauso gut wie die Bulk-Einfügungen ausgeführt, aber in einer Standardeinstellung ist der Leistungsunterschied anscheinend enorm. (Übrigens wurden alle Benchmark-Abfragen für jeden Test in einer einzigen Transaktion ausgeführt, um ein automatisches
Festschreiben
16

Ich weiß, dass dies eine alte Abfrage ist, aber ich habe gerade gelesen und dachte, ich würde hinzufügen, was ich woanders gefunden habe:

mysqli in PHP 5 ist ein Ojbect mit einigen guten Funktionen, mit denen Sie die Einfügezeit für die obige Antwort beschleunigen können:

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

Das Deaktivieren der automatischen Festschreibung beim Einfügen vieler Zeilen beschleunigt das Einfügen erheblich. Deaktivieren Sie sie daher und führen Sie sie wie oben beschrieben aus. Erstellen Sie einfach eine Zeichenfolge (sqlCombined), bei der es sich um viele Einfügeanweisungen handelt, die durch Semikolons und Mehrfachabfragen getrennt sind.

Hoffe das hilft jemandem Zeit zu sparen (suchen und einfügen!)

R.

Ross Carver
quelle
Dies ist der Fehler, den ich mit Ihrer Idee erhalten habe: "Schwerwiegender Fehler: Aufruf einer Mitgliedsfunktion autocommit () unter null in /homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.php in Zeile 30"
user3217883
8

Sie können immer MySQL verwenden LOAD DATA:

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

Masseneinfügungen durchführen, anstatt eine Reihe von INSERTAnweisungen zu verwenden.

vezult
quelle
Ich hatte das untersucht, aber ich muss die Daten manipulieren, bevor ich sie einfüge. Es wurde mir als kartesisches Produkt eines Satzes von int-Werten von 1400 mal 1400 gegeben, von denen viele Null sind. Ich muss das unter Verwendung einer Zwischentabelle in eine Viele-zu-Viele-Beziehung umwandeln, um Platz zu sparen, daher sind Einfügungen im Gegensatz zu
Masseneinfügungen erforderlich
Sie können jederzeit eine CSV-Datei generieren, nachdem Sie sie bearbeitet und die MySQL-Anweisung aufgerufen haben, mit der die Daten geladen werden
Alexander Jardim
Ich denke, es ist hilfreich zu wissen, dass der Pfad lokal zu Ihrem SQL-Client und nicht auf dem SQL Server ist. Die Datei wird auf den Server hochgeladen und von diesem gelesen. Ich dachte, die Datei muss sich bereits auf dem Server befinden, was nicht der Fall ist. Wenn es sich bereits auf dem Server befindet, entfernen Sie das LOCALBit.
Kyle
5

Nun, Sie möchten nicht 1000 Abfrageaufrufe ausführen, aber dies ist in Ordnung:

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

Abhängig von Ihrer Datenquelle ist das Auffüllen des Arrays möglicherweise so einfach wie das Öffnen einer Datei und das Ablegen des Inhalts in ein Array über file().

bdl
quelle
1
Es ist sauberer, wenn Sie das über die Abfrage verschieben und in if ($ k> 0) ändern.
Cherouvim
@cherouvim ... Nun, da hast du recht. Danke für deinen Beitrag. Wenn ich das von mir bereitgestellte Beispiel erneut lese, verstehe ich Ihren Standpunkt nicht. Sorgfältig ausarbeiten (über Pastebin, etc?). Danke
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));
Nikunj Dhimar
quelle
1

Sie können dies im Codeigniter auf verschiedene Arten tun, z

First By-Schleife

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

Zweitens - Durch Einfügen eines Stapels

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

Dritter Weg - Durch mehrere Werte übergeben

$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));
Kumar Rakesh
quelle
1

Obwohl es zu spät ist, diese Frage zu beantworten. Hier ist meine Antwort auf die gleiche.

Wenn Sie CodeIgniter verwenden, können Sie integrierte Methoden verwenden, die in der Klasse query_builder definiert sind.

$ this-> db-> insert_batch ()

Generiert eine Einfügezeichenfolge basierend auf den von Ihnen angegebenen Daten und führt die Abfrage aus. Sie können entweder ein Array oder ein Objekt an die Funktion übergeben. Hier ist ein Beispiel mit einem 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')

Der erste Parameter enthält den Tabellennamen, der zweite ist ein assoziatives Array von Werten.

Weitere Details zu query_builder finden Sie hier

Abhishek Singh
quelle
0

Ich habe eine Klasse erstellt, die mehrere Zeilen ausführt und wie folgt verwendet wird:

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

wobei die Klasse wie folgt definiert ist:

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

Verwenden Sie Batch in Codeigniter einfügen, um mehrere Datenzeilen einzufügen.

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

Ich habe diese einfache Funktion erstellt, die ihr leicht benutzen könnt. Sie müssen den Tabellennamen ($tbl)und das Tabellenfeld ($insertFieldsArr)an Ihr Datenfeld für das Einfügen von Daten übergeben ($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));
}
Waqas
quelle