PDO tek bir sorguda birden fazla satır ekler hazırlandı

9 Cevap php

Şu anda tek sorgu değerleri birden çok satır eklemek için MySQL SQL bu tür kullanıyorum:

INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...

PDO PDO okumalara, kullanım ifadeler bana statik sorguları daha iyi bir güvenlik vermelidir hazırlanmıştır.

Ben bu nedenle hazırlanmış deyimleri kullanarak "bir sorgu kullanarak değerleri birden çok satır ekleme" üretmek mümkün olup olmadığını bilmek istiyorum.

Evet, ben onu nasıl uygulayabilirsiniz biliyor olabilir?

9 Cevap

Çoklu Değerler PDO Hazırlanan Tablolar ile yerleştirin

Birinde birden fazla değerleri ekleme deyimini yürütmek. Bu sayfada göre düzenli uçlar daha hızlıdır çünkü neden. http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html

$datafields = array('fielda' => '', 'fieldb' => '' ... );

$data[] = array('fielda' => 'value', 'fieldb' => 'value' ....);
$data[] = array('fielda' => 'value', 'fieldb' => 'value' ....);

Daha fazla veri değerleri ya da muhtemelen verileri doldurur bir döngü var.

Hazırlanan uçlar ile size takıyor alanları bilmeniz gerekir, ve alanların sayısı oluşturmak için? Lütfen parametreleri bağlamak için tutucular.

insert into table (fielda, fieldb, ... ) values (?,?...), (?,?...)....

Yani, biz ekleme deyimi gibi bakmak istiyorum nasıl temelde.

Şimdi kodu ...

function placeholders($text, $count=0, $separator=","){
    $result = array();
    if($count > 0){
        for($x=0; $x<$count; $x++){
            $result[] = $text;
        }
    }

    return implode($separator, $result);
}
$pdo->beginTransaction() // also helps speed up your inserts
$insert_values = array();
foreach($data as $d){
 $question_marks[] = '('  . placeholders('?', sizeof($d)) . ')';
 $insert_values = array_merge($insert_values, array_values($d));
}

$sql = "INSERT INTO table (" . implode(",", array_keys($datafield) ) . ") VALUES " . implode(',', $question_marks);

$stmt = $pdo->prepare ($sql);
try {
    $stmt->execute($insert_values);
} catch (PDOException $e){
    echo $e->getMessage();
}
$pdo->commit();

Tek bir değer ile birden ekler ve düzenli hazırlanmış uçlar kullanırken benim testte, sadece 1 sn fark olmamasına rağmen.

Sayın Balagtas, biraz daha net ... gibi aynı cevap

Yeni sürümler MySQL ve PHP PDO do çoklu-satır INSERT ifadeleri destekler.

SQL Overview

SQL size INSERT İstediklerim 3 sütunlu bir tablo varsayarak, bu gibi bir şey olacaktır.

INSERT INTO tbl_name
            (colA, colB, colC)
     VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?) [,...]

ON DUPLICATE KEY UPDATE, hatta bir çok satır INSERT ile beklendiği gibi çalışır; Bu ekler:

ON DUPLICATE KEY UPDATE colA = VALUES(colA), colB = VALUES(colB), colC = VALUES(colC)

PHP Overview

PHP kodu olağan $pdo->prepare($qry) ve $stmt->execute($params) PDO aramaları takip edecek.

$params INSERT geçmek için all değerleri 1-boyutlu bir dizi olacak.

Yukarıdaki örnekte, 9 elemanlar içermelidir; PDO değerlerin tek bir satır olarak 3 her kümesini kullanır. (3 sütun, her = 9 eleman dizisi 3. satır eklemek.)

Implementation

Aşağıda kod açıklık, etkinlik değil için yazılmıştır. PHP Eğer isterseniz sizin veri üzerinden harita veya yürümek için daha iyi yollar array_*() fonksiyonları ile çalışır. Tabii ki işlemleri kullanabilirsiniz olmadığını MySQL tablo türüne bağlıdır.

Varsayarsak:

  • $tblName - tablonun dize ismi INSERT
  • $colNames - 1-dimensional array of the column names of the table These column names must be valid MySQL column identifiers; escape them with backticks (``) if they are not
  • $dataVals - Her öğe eklemek için değerlerin bir satır 1-d dizi çok-boyutlu bir dizi,

Sample Code

// setup data values for PDO
// memory warning: this is creating a copy all of $dataVals
$dataToInsert = array();

foreach ($dataVals as $row => $data)
   foreach($data as $val)
      $dataToInsert[] = $val;

// (optional) setup the ON DUPLICATE column names
$updateCols = array();

foreach ($colNames as $curCol)
   $updateCols[] = $curCol . " = VALUES($curCol)";

$onDup = implode(', ', $updateCols);

// setup the placeholders - a fancy way to make the long "(?, ?, ?)..." string
$rowPlaces = '(' . implode(', ', array_fill(0, count($colNames), '?')) . ')';
$allPlaces = implode(', ', array_fill(0, count($dataVals), $rowPlaces));

$qry = "INSERT INTO $tblName
                    (" . implode(', ', $colNames) . "
             VALUES " . $allPlaces . "
        ON DUPLICATE KEY UPDATE $onDup";

// and then the PHP PDO boilerplate
$stmt = $pdo->prepare ($sql);

try {
   $stmt->execute($insert_values);
} catch (PDOException $e){
   echo $e->getMessage();
}

$pdo->commit();

Bu değer ne için, ben çok sayıda kullanıcı yerine seçilen cevap olduğu gibi, tek bir dize sorgu olarak inşa INSERT deyimleri yinelenmesi tavsiye gördük. Ben sadece iki alanları ve bir çok temel insert deyimi ile basit bir test çalıştırmak için karar:

<?php
require('conn.php');

$fname = 'J';
$lname = 'M';

$time_start = microtime(true);
$stmt = $db->prepare('INSERT INTO table (FirstName, LastName) VALUES (:fname, :lname)');

for($i = 1; $i <= 10; $i++ )  {
    $stmt->bindParam(':fname', $fname);
    $stmt->bindParam(':lname', $lname);
    $stmt->execute();

    $fname .= 'O';
    $lname .= 'A';
}


$time_end = microtime(true);
$time = $time_end - $time_start;

echo "Completed in ". $time ." seconds <hr>";

$fname2 = 'J';
$lname2 = 'M';

$time_start2 = microtime(true);
$qry = 'INSERT INTO table (FirstName, LastName) VALUES ';
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?)";

$stmt2 = $db->prepare($qry);
$values = array();

for($j = 1; $j<=10; $j++) {
    $values2 = array($fname2, $lname2);
    $values = array_merge($values,$values2);

    $fname2 .= 'O';
    $lname2 .= 'A';
}

$stmt2->execute($values);

$time_end2 = microtime(true);
$time2 = $time_end2 - $time_start2;

echo "Completed in ". $time2 ." seconds <hr>";
?>

Genel sorgu kendisi milisaniye veya daha az olurken, ikinci (tek dize) sorgu sürekli 8 kat daha hızlı ya da daha fazla oldu. Bu çok daha fazla sütunlar üzerinde satır binlerce bir ithalat yansıtmak demek için inşa edilmiş ise, fark çok büyük olabilir.

Bu sadece size hazır deyimleri kullanmak yolu bu değil.

Farklı parametrelerle bir hazırlanmış deyimi birden çok kez yürütebilirsiniz çünkü sorgu başına bir satır eklemek için mükemmel tamam. Bu size bir, verimli, güvenli ve rahat bir şekilde satır çok sayıda eklemek sağlar gibi Aslında bu büyük avantajlarından biridir.

Yani belki mümkün düzenini en az satır sabit bir dizi için, öneren, ama neredeyse bu ne istediğiniz değil gerçekten garanti uygulamak.

$ Data dizi küçük olduğunda Herbert Balagtas tarafından Kabul cevap iyi çalışıyor. Büyük $ veri dizileri ile array_merge işlevi engelleyici yavaş olur. $ Veri dizisi oluşturmak için benim test dosyası 28 cols sahiptir ve yaklaşık 80.000 satır. Nihai komut 41s tamamlamak için aldı.

array_push() yerine array_merge $ insert_values ​​oluşturmak için () kullanarak icra süresi ile 100X speed up ile sonuçlanmıştır 0.41s.

Sorunlu array_merge ():

$insert_values = array();

foreach($data as $d){
 $question_marks[] = '('  . placeholders('?', sizeof($d)) . ')';
 $insert_values = array_merge($insert_values, array_values($d));
}

Array_merge ihtiyacını () ortadan kaldırmak için, bunun yerine aşağıdaki iki dizi oluşturabilirsiniz:

//Note that these fields are empty, but the field count should match the fields in $datafields.
$data[] = array('','','','',... n ); 

//getting rid of array_merge()
array_push($insert_values, $value1, $value2, $value3 ... n ); 

Şöyle Bu diziler daha sonra kullanılabilir:

function placeholders($text, $count=0, $separator=","){
    $result = array();
    if($count > 0){
        for($x=0; $x<$count; $x++){
            $result[] = $text;
        }
    }

    return implode($separator, $result);
}

$pdo->beginTransaction();

foreach($data as $d){
 $question_marks[] = '('  . placeholders('?', sizeof($d)) . ')';
}

$sql = "INSERT INTO table (" . implode(",", array_keys($datafield) ) . ") VALUES " . implode(',', $question_marks);

$stmt = $pdo->prepare ($sql);
try {
    $stmt->execute($insert_values);
} catch (PDOException $e){
    echo $e->getMessage();
}
$pdo->commit();

İki olası yaklaşımlar:

$stmt = $pdo->prepare('INSERT INTO foo VALUES(:v1_1, :v1_2, :v1_3),
    (:v2_1, :v2_2, :v2_3),
    (:v2_1, :v2_2, :v2_3)');
$stmt->bindValue(':v1_1', $data[0][0]);
$stmt->bindValue(':v1_2', $data[0][1]);
$stmt->bindValue(':v1_3', $data[0][2]);
// etc...
$stmt->execute();

Veya:

$stmt = $pdo->prepare('INSERT INTO foo VALUES(:a, :b, :c)');
foreach($data as $item)
{
    $stmt->bindValue(':a', $item[0]);
    $stmt->bindValue(':b', $item[1]);
    $stmt->bindValue(':c', $item[2]);
    $stmt->execute();
}

Tüm satırlar için verileri tek bir dizide iseniz, ben ikinci çözümü kullanmak istiyorsunuz.

Daha kısa bir cevap: O sütun tarafından sipariş edilen verilerin dizi düzleştirmek

//$array = array( '1','2','3','4','5', '1','2','3','4','5');
$arCount = count($array);
$rCount = ($arCount  ? $arCount - 1 : 0);
$criteria = sprintf("(?,?,?,?,?)%s", str_repeat(",(?,?,?,?,?)", $rCount));
$sql = "INSERT INTO table(c1,c2,c3,c4,c5) VALUES$criteria";

1000 ya da öylesine kayıtları takarken size ihtiyacınız olan tüm değerlerin bir sayım olduğu zaman onları eklemek için her kaydın döngü zorunda istemiyorum.

İşte temizleme seçeneği ile birden ekler yapmak yazdığı bir sınıf bulunuyor:

<?php

/**
 * $pdo->beginTransaction();
 * $pmi = new PDOMultiLineInserter($pdo, "foo", array("a","b","c","e"), 10);
 * $pmi->insertRow($data);
 * ....
 * $pmi->insertRow($data);
 * $pmi->purgeRemainingInserts();
 * $pdo->commit();
 *
 */
class PDOMultiLineInserter {
    private $_purgeAtCount;
    private $_bigInsertQuery, $_singleInsertQuery;
    private $_currentlyInsertingRows  = array();
    private $_currentlyInsertingCount = 0;
    private $_numberOfFields;
    private $_error;
    private $_insertCount = 0;

    function __construct(\PDO $pdo, $tableName, $fieldsAsArray, $bigInsertCount = 100) {
        $this->_numberOfFields = count($fieldsAsArray);
        $insertIntoPortion = "INSERT 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;
    }
}

Bu fonksiyon ile, tek bir sorguda birden çok satır ekleyebilirsiniz:

function insertMultiple($query,$rows) {
    if (count($rows)>0) {
        $args = array_fill(0, count($rows[0]), '?');

        $params = array();
        foreach($rows as $row)
        {
            $values[] = "(".implode(',', $args).")";
            foreach($row as $value)
            {
                $params[] = $value;
            }
        }

        $query = $query." VALUES ".implode(',', $values);
        $stmt = $PDO->prepare($query);
        $stmt->execute($params);
    }
}

$row is an array of arrays of values. In your case you would call the function with

insertMultiple("INSERT INTO tbl (`key1`,`key2`)",array(array('r1v1','r1v2'),array('r2v1','r2v2')));

Bu tek bir sorgu ile birden çok satır ekleyerek ederken, prepared statements kullanmak yararı vardır. Güvenlik!