2013-02-25 9 views
18

Szukam wielu wstawek przy użyciu PHP PDO.wstawianie PDO wsadowego wielu wierszy z symbolami zastępczymi

Najbliżej odpowiedź znalazłem jest to jeden

how-to-insert-an-array-into-a-single-mysql-prepared-statement

Jednak przykład ów dano zastosowań ?? zamiast prawdziwych symboli zastępczych.

Mam spojrzał na przykładach na stronie PHP doc dla posiadaczy miejsce

php.net pdo.prepared-statements

$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)"); 
$stmt->bindParam(':name', $name); 
$stmt->bindParam(':value', $value); 

Teraz powiedzmy, że chciałem, aby osiągnąć wyżej ale z tablicą

$valuesToInsert = array(
    0 => array('name' => 'Robert', 'value' => 'some value'), 
    1 => array('name' -> 'Louise', 'value' => 'another value') 
); 

Co mam zrobić z PDO i wieloma wkładkami na transakcję?

Wyobrażam sobie, że zaczyna się od pętli?

$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)"); 

foreach($valuesToInsert as $insertRow){ 

    // now loop through each inner array to match binded values 
    foreach($insertRow as $column => value){ 
     $stmt->bindParam(":{$column}", value); 
    } 
} 
$stmt->execute(); 

Jednakże powyższe nie działa, ale mam nadzieję, że pokaże im, co staramy się osiągnąć

+2

trzeba wykonać wewnątrz pętli. w przeciwnym razie po prostu nadpisujesz związany parametr i kończysz wiązanie tylko wartości LAST. –

+1

jednak jeśli wykonam, to będzie transakcji DB jeden wiersz na raz? Próbuję to zrobić w partii –

+0

tak, ale jeśli próbujesz emulować rozszerzone mysql 'insert ... values ​​(...), (...), (...)' wstaw składnię, to jest nie jak się do tego zabrać. musielibyście wstępnie zbudować instrukcję zapytania zawierającą symbol zastępczy dla każdego wstawianego zestawu wartości, przygotować go, powiązać parametry, a następnie wykonać. skończyłoby się robić tyle samo pracy, co wielokrotne uruchamianie pojedynczego przygotowanego wkładu. –

Odpowiedz

24

Przede wszystkim ? symbole prawdziwe miejsce, uchwyty (większość kierowców pozwalają na zastosowanie zarówno składnie, pozycyjne i oznaczone miejsce posiadaczy). Po drugie, przygotowane instrukcje są niczym innym, jak tylko narzędziem do wprowadzania surowych danych wejściowych do instrukcji SQL - nie ma to wpływu na składnię instrukcji SQL. Masz już wszystkie elementy potrzebne są:

  • Jak wstawić wiele wierszy za pomocą pojedynczego zapytania
  • Jak wygenerować SQL dynamicznie
  • Jak korzystać z przygotowanych sprawozdań oznaczone miejsce posiadaczy.

To dość trywialne je wszystkie połączyć:

$sql = 'INSERT INTO table (memberID, programID) VALUES '; 
$insertQuery = array(); 
$insertData = array(); 
$n = 0; 
foreach ($data as $row) { 
    $insertQuery[] = '(:memberID' . $n . ', :programID' . $n . ')'; 
    $insertData['memberID' . $n] = $memberid; 
    $insertData['programID' . $n] = $row; 
    $n++; 
} 

if (!empty($insertQuery)) { 
    $sql .= implode(', ', $insertQuery); 
    $stmt = $db->prepare($sql); 
    $stmt->execute($insertData); 
} 
-4

Move wykonać wewnątrz pętli.

$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)"); 
foreach($valuesToInsert as $insertRow) 
{ 
    $stmt->execute($insertRow);  
} 

Jeśli wystąpią jakieś problemy z tym taki zalecany sposób, trzeba zadać pytanie, opisując te pewne problemy.

+1

Nie sądzę, że powyższe robi to w partii wygląda tak, jak robi to po jednym na raz? –

+0

To nie jest wsadowy wsad, bardzo wyraźnie korzystasz z tej samej instrukcji SQL wiele razy w wierszu, ale wciąż robisz pojedynczą wstawkę na raz. –

7

Jestem zakładając używasz InnoDB więc ta odpowiedź jest ważna tylko dla tego silnika (lub jakikolwiek inny silnik transakcyjnej-zdolny, co oznacza MyISAM ISN w tym).

Domyślnie InnoDB działa w trybie automatycznego zatwierdzania. Oznacza to, że każde zapytanie traktowane jest jako jego własna zawarta transakcja.

Aby przetłumaczyć to na coś, co nasi śmiertelnicy mogą zrozumieć, oznacza to, że każde wysyłane zapytanie INSERT wymusi na dysku twardym zatwierdzenie zapisania informacji o zapytaniu. Biorąc pod uwagę, że mechaniczne dyski twarde są bardzo wolne, ponieważ ich operacja wejścia-wyjścia na sekundę jest niska (jeśli się nie mylę, średnia wynosi 300-óch IO), oznacza to, że 50 000 zapytań będzie - no, bardzo wolno.

Co więc robisz? Zatwierdzasz wszystkie swoje kwerendy 50k w pojedynczej transakcji. To może nie być najlepsze rozwiązanie do różnych celów, ale będzie szybkie.

Robisz to tak:

$dbh->beginTransaction(); 

$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)"); 

foreach($valuesToInsert as $insertRow) 
{  
    // now loop through each inner array to match bound values 
    foreach($insertRow as $column => value) 
    { 
     $stmt->bindParam(":$column", value); 
     $stmt->execute(); 
    } 
} 


$dbh->commit(); 
+0

Mam około 50k wierszy do wstawienia i zamierzałem to zrobić w partiach po 25 egzemplarzy. Spróbuję tego przykładu, ponieważ wygląda na to, że może być tym, czego szukam. –

+1

Ta odpowiedź jest poprawna, ale nie mówi o wielowątkowej składni MySQL, która z mojego doświadczenia może naprawdę przyspieszyć duże partie. –

+0

Aby uzyskać najlepszą wydajność, "LOAD DATA INFILE" jest nadal najszybszą opcją, a skonstruowanie pliku do użycia z funkcją "LOAD DATA INFILE" powinno być dość proste. Zapomniałem dodać to do odpowiedzi, a jak wspomniałem @ ÁlvaroG.Vicario - 'INSERT INTO .... VALUES..' jest szybsze niż wykonanie przygotowanej instrukcji w pętli. –

1

A małe modyfikacje w roztworze świadczone przez NB
$ stmt-> execute() powinna być na zewnątrz pętli wewnętrznej, ponieważ może mieć jedną lub więcej kolumn, które muszą bind przed wywołaniem $ stmt-> execute() W przeciwnym razie otrzymasz wyjątek "Nieprawidłowy numer parametru: liczba powiązanych zmiennych nie jest zgodna z liczbą tokena".
W drugiej zmiennej "wartości" brakowało znaków dolara.

function batchinsert($sql,$params){ 
    try { 
       db->beginTransaction(); 

       $stmt = db->prepare($sql); 

       foreach($params as $row) 
       {  
        // now loop through each inner array to match bound values 
        foreach($row as $column => $value) 
        {       
         $stmt->bindParam(":$column", $value);       
        } 
        $stmt->execute(); 
       }          
       db->commit();     

     } catch(PDOExecption $e) { 
      $db->rollback();     
     } 
} 

Test:

$sql = "INSERT INTO `test`(`name`, `value`) VALUES (:name, :value)" ; 

$data = array();  

array_push($data, array('name'=>'Name1','value'=>'Value1')); 

array_push($data, array('name'=>'Name2','value'=>'Value2')); 

array_push($data, array('name'=>'Name3','value'=>'Value3')); 

array_push($data, array('name'=>'Name4','value'=>'Value4')); 

array_push($data, array('name'=>'Name5','value'=>'Value5')); 

batchinsert($sql,$data); 
-1

Twój kod był rzeczywiście ok, ale miał problem w $stmt->bindParam(":$column", value); Należy $stmt->bindValue(":{$column}", $value); i będzie działać doskonale. Pomoże to innym w przyszłości.

Pełny kod:

foreach($params as $row) 
{ 
    // now loop through each inner array to match bound values 
    foreach($row as $column => $value) 
    { 
     $stmt->bindValue(":{$column}", $value); //EDIT 
    } 
    // Execute statement to add to transaction 
    $stmt->execute(); 
} 
+0

Nie ma jednego problemu z tą częścią składni. Twoja sugestia jest zbędna i wprowadzająca w błąd. –

Powiązane problemy