2013-09-06 15 views
18

Jestem nowy w Doctrine i nadal są dla mnie niewyraźne obszary. W tym przypadku wstawiam nowy rekord do bazy danych za pomocą pętli i menedżera encji. Działa dobrze, ale zauważyłem, że Doctrine tworzy jedno zapytanie wstawiane przez jednostkę, która może stać się całkiem olbrzymia.Doctrine2 - Wiele wstawek w jednym ujęciu

Korzystając z Doctrine2 i Symfony 2.3, chciałbym się dowiedzieć, jak możemy go skonfigurować, aby zrobił tylko jedno zapytanie wstawiające ze wszystkimi wartościami (oczywiście mówimy tylko o 1 encji).

Co to znaczy zmienia się następująco:

INSERT INTO dummy_table VALUES (x1, y1)  
INSERT INTO dummy_table VALUES (x2, y2) 

Do

INSERT INTO dummy_table VALUES (x1, y1), (x2, y2) 

Oto mój kod:

$em = $this->container->get('doctrine')->getManager(); 

foreach($items as $item){ 
    $newItem = new Product($item['datas']); 
    $em->persist($newItem); 
} 

$em->flush(); 
+0

Na co sprawa, że ​​można połączyć te kwerendy w jeden? – Touki

+0

Myślałem o poprawie wydajności. To tylko przykład, najprawdopodobniej jest to najprawdopodobniej około 20 elementów do wstawienia. Tak więc tylko połączenie byłoby znacznie szybsze niż n połączeń. EDYCJA: Znalazłem [tę odpowiedź] (http://stackoverflow.com/questions/1793169/which-is-faster-multiple-single-inserts-or-one-multiple-row-insert) na ten temat. – Molkobain

+0

Mogę cię ostrzec, doktryna dodaje sporo nakładu pracy na każdą z wkładek, które robisz (zarządza stanem i podobnymi), więc w przypadku naprawdę dużych wstawek zamiast nich wybierałem zapytania DBAL zamiast relacji ORM. // tylko moje 2 centy. –

Odpowiedz

34

Według this answer, Doctrine2 nie pozwala na łączenie wielu INSERT do jednego:

Niektórzy ludzie wydają się zastanawiać, dlaczego nie używa Doktryna multi-wkładki (insert into (...) wartości (...), (...), (...), ...

Po pierwsze, ta składnia jest obsługiwana tylko w wersjach mysql i nowszych postgresql. Po drugie, nie ma łatwego sposobu aby uzyskać wszystkie wygenerowane identyfikatory w takiej wielowłókience przy użyciu AUTO_INC REMENT lub SERIAL i ORM potrzebują identyfikatorów do zarządzania obiektami. Wreszcie wydajność wstawiania rzadko jest wąskim gardłem ORM. Prawidłowe wstawki są wystarczająco szybkie, aby w większości przypadków były wystarczające, a jeśli naprawdę chcesz robić szybkie wkłady luzem, to wielowątkowa wstawka nie jest najlepszą metodą, tzn. Postgres COPY lub Mysql LOAD DATA INFILE są o kilka rzędów wielkości szybsze .

To jest powód, dla którego nie jest warte wysiłku, aby zaimplementować abstrakcję , która wykonuje multi-inserty na mysql i postgresql w ORM .

Możesz przeczytać więcej o Doctrine2 przetwarzania wsadowego tutaj: http://www.doctrine-project.org/blog/doctrine2-batch-processing.html

Można też przełączyć się DBAL lub uciekania się do przetwarzania danych w małych partiach przez przepłukanie swojego kierownika jednostki po ustalonej ilości wkładek:

$batchSize = 20; 

foreach ($items as $i => $item) { 
    $product = new Product($item['datas']); 

    $em->persist($product); 

    // flush everything to the database every 20 inserts 
    if (($i % $batchSize) == 0) { 
     $em->flush(); 
     $em->clear(); 
    } 
} 

// flush the remaining objects 
$em->flush(); 
$em->clear(); 
+0

Dzięki, teraz wiem, że nie jest to możliwe dzięki Doctrine. – Molkobain

+3

+1 Opróżnij pozostałe obiekty ... dokumentacja powinna naprawdę zawierać to tak, jakby rozmiar twojej partii był mniejszy niż liczba wyników nic się nie dzieje – Carlton

+0

Zwróć uwagę, że '$ em-> clear()' może mieć złe efekty z innymi obiektami nie zajmujesz się swoją funkcją w bezpośredni sposób. Na przykład, możesz uzyskać wyjątek w stosunku do swojego użytkownika kategorii produktów ... Dlatego bardziej wygodne jest usuwanie tylko tych elementów, które mają pożądany typ. W tym przypadku będzie to '$ em-> clear ('Product')' – FlameStorm

-6

nie testowałem, ale to wydaje się możliwe, aby to zrobić z kolekcją.

$collection = new Doctrine_Collection('tablename'); 
$collection->add($record1); 
$collection->add($record2); 
$collection->add($record3); 
$collection->add($record4); 
$collection->save(); 

Oczywiście powinieneś mieć dodać w pętli.

+0

Spróbuję, ale jeśli będziemy musieli określić nazwę tabeli za każdym razem, gdy chcę wstawić wiele wartości, staje się to trochę zbyt "skomplikowane" dla rozwiązania. – Molkobain

2

Możesz wypróbować ten widelec https://github.com/stas29a/doctrine2. Wprowadza dokładnie to, co chcesz. Przetestowałem go w MySQL i działa dobrze i 5 razy szybciej niż to przetwarzanie wsadowe. Ten fork otrzymuje pierwszy wstawiony identyfikator i inkrementuje go w php, aby uzyskać inne id. Działa w większości przypadków, ale nie we wszystkich. Musisz więc zrozumieć, co robisz, korzystając z tego widelca.

0

Możesz użyć interfejsu executeUpdate($query, array $params = array(), array $types = array()) interfejsu DriverConnection, aby wykonać tę akcję. Jednak wiązanie wielu parametrów jest bardzo trudne.

danych: metoda aktualizacji

$postMetaData = [ 
    [ 
     'post_id' => $product->getId(), 
     'meta_key' => '_visibility', 
     'meta_value' => 'visible', 
    ], 
    [ 
     'post_id' => $product->getId(), 
     'meta_key' => '_stock_status', 
     'meta_value' => $insert['in_stock'] ? 'instock' : 'outofstock', 
    ] 
]; 

nasypowa:

public function updateOrCreateBulk($posts, \Doctrine\DBAL\Connection $connection) 
{ 

    $placeholders = []; 
    $values = []; 
    $types = []; 

    foreach ($posts as $columnName => $value) { 
     $placeholders[] = '(?)'; 
     $values[] = array_values($value); 
     $types[] = \Doctrine\DBAL\Connection::PARAM_INT_ARRAY; 
    } 

    return $connection->executeUpdate(
     'INSERT INTO `wp_postmeta` (`post_id`, `meta_key`, `meta_value`) VALUES ' . implode(', ', $placeholders) . ' ON DUPLICATE KEY UPDATE `meta_value` = VALUES(`meta_value`)', 
     $values, 
     $types 
    ); 
} 
Powiązane problemy