mysql'ye bir php dizisi aracılığıyla birden çok satır ekleyin


129

Ekleme komutlarını kullanarak PHP aracılığıyla büyük bir veri kümesini bir MySQL tablosuna geçiriyorum ve her bir değeri bir mil uzunluğundaki dizenin sonuna eklemek dışında bir sorgu aracılığıyla bir seferde yaklaşık 1000 satır eklemenin mümkün olup olmadığını merak ediyorum ve sonra yürütmek. CodeIgniter çerçevesini kullanıyorum, böylece işlevleri de benim için kullanılabilir.


Codeigniter'in çoklu satır eki için sorunuza göre cevap verdim.
Somnath Muluk

@SomnathMuluk Teşekkürler, ancak bu soruyu cevaplamamın üzerinden
epey zaman geçti :)

CodeIgniter'in insert_batch işlevini kullanmanızı tavsiye ederim. Bir kitaplık kullanıyorsanız, her zaman onun güçlü yönlerinden ve kodlama standartlarından yararlanmaya çalışın.
Dewald Els

Yanıtlar:


234

MySQL'de INSERTbirden çok satır içeren bir ifadeyi bir araya getirmek, satır INSERTbaşına bir ifadeden çok daha hızlıdır .

Bununla birlikte, PHP'de dizgi işleme problemleriyle karşılaşıyormuşsunuz gibi görünüyor, bu gerçekten bir algoritma problemi, bir dil değil. Temel olarak, büyük dizelerle çalışırken, gereksiz kopyalamayı en aza indirmek istersiniz. Öncelikle bu, birleştirme işleminden kaçınmak istediğiniz anlamına gelir. Bire yüzlerce satır eklemek gibi büyük bir dizi oluşturmanın en hızlı ve bellek açısından en verimli yolu, implode()işlev ve dizi atamasından yararlanmaktır.

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

Bu yaklaşımın avantajı, şimdiye kadar her birleştirme ile bir araya getirdiğiniz SQL deyimini kopyalayıp yeniden kopyalamamanızdır; Bunun yerine, PHP bunu yapar bir kez de implode()açıklamada. Bu büyük bir kazanç.

Bir araya getirilecek çok sayıda sütununuz varsa ve bir veya daha fazlası çok uzunsa, aynı şeyi yapmak için bir iç döngü de oluşturabilir ve implode()değerler cümlesini dış diziye atamak için kullanabilirsiniz .


5
Bunun için teşekkürler! Biri kopyalamayı planlıyorsa, işlevin sonunda bir kapanış ayracı eksik. mysql_real_query ('INSERT INTO table VALUES (metin, kategori)' .implode (','. $ sql));
toofarsideways

3
Teşekkürler! Sabit. (Bunu sık sık yaparım ...)
staticsan

1
ve sorgu gerçekten 'INSERT INTO table (text, category) VALUES' olmalıdır .implode (','. $ sql) 'sigh 4am kodlaması korkunç hata ayıklamaya yol açar :(
toofarsideways

3
Bu kodun son projem için bir çözüm yaratacağına inanıyorum. Buradaki sorum şu, bu SQL enjeksiyonundan güvenli mi? Planlarım dışarı geçiş vardır mysql_real_escape_stringile mysqli_real_escape_stringve mysql_queryile mysqli_queryben MySQLi kullanıyorum ve bu PHP5'ta itibarıyla kullanımdan kaldırılmıştır gibi. Çok teşekkürler!
sözcü

2
mysql_*PHP'den kaldırıldı, bu yüzden mysqli_*arayüzü kullandığınızdan emin olun .
Rick James

60

Çoklu ekleme / parti ekleme artık kod işaretleyici tarafından desteklenmektedir. Benim de aynı problemim vardı. Soruyu cevaplamak için çok geç olsa da birilerine yardımcı olacaktır. Bu soruyu cevaplamanın nedeni bu.

$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
Bunun, mysql_query kullanmak yerine çok satırlı ekleme yapmanın en çok önerilen yolu olduğunu düşünüyorum. Çünkü bir çerçeve kullandığımızda, çerçevenin her zaman yerleşik özelliklerini kullanmak iyi bir uygulamadır.
Praneeth Nidarshan

22

Mysqli_stmt sınıfını kullanarak bir satır eklemek için sorguyu hazırlayabilir ve ardından veri dizisi üzerinde yineleme yapabilirsiniz. Gibi bir şey:

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

Burada 'idb', bağladığınız veri türleridir (int, double, string, blob).


6
Kısa bir süre önce, burada belirtildiği gibi toplu giriş ve ek açıklamalarını karşılaştıran bazı karşılaştırmalar yaptım. Yaklaşık 500 kesici uç için, hazırlanan uç yöntemi 2,6-4,4 saniye arasında ve toplu ekleme yöntemi 0,12-0,35 saniyede tamamlandı. Mysql'nin bu hazırlanmış ifadeleri birlikte "toplu hale getireceğini" ve toplu eklemeler kadar iyi performans göstereceğini düşünürdüm, ancak varsayılan bir kurulumda, performans farkı görünüşe göre çok büyük. (Btw tüm karşılaştırmalı sorgular, otomatik taahhüdü önlemek için her test için tek bir işlem içinde çalışıyordu)
Motin

16

Bunun eski bir sorgu olduğunu biliyorum, ancak sadece okuyordum ve başka bir yerde bulduklarımı ekleyeceğimi düşündüm:

PHP 5'te mysqli, yukarıdaki yanıt için ekleme süresini hızlandırmanıza olanak tanıyan bazı iyi işlevlere sahip bir ojbecttir:

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

Çok sayıda satır eklerken otomatik tamamlamayı kapatmak, eklemeyi büyük ölçüde hızlandırır, bu nedenle onu kapatın, ardından yukarıda belirtildiği gibi çalıştırın veya noktalı virgüllerle ayrılmış birçok ekleme deyimi olan ve çoklu sorgu bunları iyi işleyen bir dize (sqlCombined) oluşturun.

Umarım bu, birisinin zamandan tasarruf etmesine yardımcı olur (arama ve ekleme!)

R,


Fikrinizi kullanırken aldığım hata bu: "Önemli hata: 30. satırda /homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.php'de null'da bir üye işlevi autocommit () çağrısı"
user3217883

8

Mysql'leri her zaman kullanabilirsiniz LOAD DATA:

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

bir grup INSERTifade kullanmak yerine toplu eklemeler yapmak için .


Bunu araştırmıştım, ancak verileri eklemeden önce değiştirmem gerekiyor. Bana çoğu sıfır olan 1400'e 1400 int değerlerinin Kartezyen çarpımı olarak verildi.
Yerden

İşledikten ve verileri yükleyen mysql ifadesini çağırdıktan sonra her zaman bir csv dosyası oluşturabilirsiniz
Alexander Jardim

Yolun SQL sunucunuzda değil, SQL istemciniz için yerel olduğunu bilmenin yararlı olacağını düşünüyorum. Dosya sunucuya yüklenir ve ardından onun tarafından okunur. Dosyanın zaten sunucuda olması gerektiğini düşündüm, bu durum böyle değil. Zaten sunucuda ise, LOCALbiti kaldırın .
Kyle

5

1000 sorgu çağrısı yürütmek istemezsiniz, ancak bunu yapmak iyidir:

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

Veri kaynağınıza bağlı olarak, diziyi doldurmak, bir dosyayı açmak ve içeriği aracılığıyla bir diziye dökmek kadar kolay olabilir file().


1
Bunu sorgunun üstüne taşırsanız ve if ($ k> 0) gibi bir şeye değiştirirseniz daha temiz olur.
cherouvim

@cherouvim ... Bu konuda haklısın. Girdiniz için teşekkürler. Verdiğim örneği yeniden okurken, ne demek istediğini göremiyorum. Detaylandırmaya dikkat edin (pastebin aracılığıyla, vb.). Teşekkürler-
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

Bunu kod işaretleyicide birkaç yolla yapabilirsiniz, örneğin

İlk döngü

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

İkinci - toplu iş ekleyerek

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

Üçüncü yol - Çoklu değer geçişi ile

$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

Bu soruyu cevaplamak için çok geç olmasına rağmen. İşte aynı cevabım.

CodeIgniter kullanıyorsanız, query_builder sınıfında tanımlanan dahili yöntemleri kullanabilirsiniz.

) (This-> db-> insert_batch, $

Sağladığınız verilere göre bir ekleme dizesi oluşturur ve sorguyu çalıştırır. İşleve bir dizi veya bir nesne iletebilirsiniz. İşte dizi kullanan bir örnek:

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

İlk parametre tablo adını içerecektir, ikincisi ise ilişkilendirilebilir değerler dizisidir.

Burada query_builder hakkında daha fazla ayrıntı bulabilirsiniz


0

Aşağıdaki gibi kullanılan çok satırlı bir sınıf oluşturdum:

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

sınıf aşağıdaki gibi tanımlanır:

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

Birden çok veri satırı eklemek için kod işaretleyicide toplu ekleme işlevini kullanın.

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

0

Sizlerin rahatlıkla kullanabileceğiniz bu basit işlevi yarattım. Tablo-adı ($tbl), tablo-alanını ($insertFieldsArr)veri ekleme, veri dizisine göre geçirmeniz gerekecektir ($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));
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.