2013-09-06 43 views
18

Tôi mới dùng Doctrine và vẫn còn một số vùng mờ cho tôi. Trong trường hợp này, tôi chèn bản ghi mới vào cơ sở dữ liệu bằng cách sử dụng vòng lặp và trình quản lý thực thể. Nó hoạt động tốt nhưng tôi nhận thấy rằng Doctrine thực hiện một truy vấn chèn theo thực thể, có thể trở nên khá lớn.Doctrine2 - Nhiều lần chèn vào một ảnh

Sử dụng Doctrine2 và Symfony 2.3, tôi muốn biết cách chúng tôi có thể thiết lập để nó chỉ tạo 1 truy vấn chèn với tất cả các giá trị trong đó (chúng tôi chỉ nói đến 1 thực thể).

Những gì tôi có nghĩa là đang thay đổi này:

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

Into

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

Đây là mã của tôi:

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

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

$em->flush(); 
+0

Vì cái gì vấn đề bạn sẽ kết hợp các truy vấn này thành một trong những đơn? – Touki

+0

Tôi đã suy nghĩ về cải tiến hiệu suất. Đây chỉ là một ví dụ, trong pratice nó có khả năng nhất là khoảng 20 thực thể để chèn. Vì vậy, việc chỉ kết nối sẽ nhanh hơn nhiều so với kết nối n. EDIT: Tôi tìm thấy [câu trả lời này] (http://stackoverflow.com/questions/1793169/which-is-faster-multiple-single-inserts-or-one-multiple-row-insert) về chủ đề này rất. – Molkobain

+0

Tôi có thể cảnh báo bạn, giáo lý bổ sung khá nhiều chi phí cho mỗi chèn bạn làm (quản lý nhà nước và như vậy), vì vậy đối với chèn thực sự lớn tôi muốn chọn truy vấn DBAL thay vì các mối quan hệ ORM thay thế. // chỉ 2 xu của tôi –

Trả lời

34

Theo this answer, Doctrine2 không cho phép bạn kết hợp nhiều câu lệnh INSERT vào một:

Một số người dường như tự hỏi tại sao học thuyết không sử dụng đa chèn (insert into (...) các giá trị (...), (...), (...), ...

Trước hết, cú pháp này chỉ được hỗ trợ trên mysql và mới hơn phiên bản postgresql. Thứ hai, không có cách nào dễ dàng để giữ tất cả số nhận dạng được tạo trong một đa chèn như vậy khi sử dụng AUTO_INC REMENT hoặc SERIAL và ORM cần số nhận dạng để nhận dạng quản lý đối tượng. Cuối cùng, chèn hiệu suất hiếm khi là nút cổ chai của ORM. Chèn bình thường là đủ nhanh cho hầu hết các trường hợp và nếu bạn thực sự muốn chèn nhanh hàng loạt, thì một cách đa phương tiện không phải là cách tốt nhất, tức là Postgres COPY hoặc Mysql LOAD DATA INFILE là một số đơn đặt hàng có cường độ nhanh hơn .

Đây là những lý do tại sao nó không phải là giá trị nỗ lực để thực hiện một sự trừu tượng thực hiện đa chèn trên mysql và postgresql trong một ORM.

Bạn có thể đọc thêm về Doctrine2 xử lý hàng loạt ở đây: http://www.doctrine-project.org/blog/doctrine2-batch-processing.html

Bạn có thể chuyển sang DBAL hoặc nghỉ mát để xử lý dữ liệu của bạn theo lô nhỏ bằng cách xả nước quản lý thực thể của bạn sau một khoảng chèn:

$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

Cảm ơn, bây giờ tôi biết điều đó là không thể thông qua Doctrine. – Molkobain

+3

+1 Làm sạch các đối tượng còn lại ... tài liệu nên thực sự bao gồm điều này như thể kích thước lô của bạn nhỏ hơn số kết quả không có gì xảy ra – Carlton

+0

Chú ý rằng '$ em-> clear()' có thể có hiệu ứng sai với các thực thể khác bạn không giao dịch với chức năng của mình theo cách trực tiếp. Ví dụ, bạn có thể nhận được một số ngoại lệ liên quan đến Người dùng Danh mục Sản phẩm của bạn ... Vì vậy, sẽ thuận tiện hơn nếu chỉ xóa các thực thể có loại bạn muốn. Trong trường hợp này, nó sẽ là '$ em-> clear ('Product')' – FlameStorm

-6

tôi đã không kiểm tra nó nhưng có vẻ như khả năng để làm điều này với một bộ sưu tập.

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

Tất nhiên bạn sẽ có thêm vòng lặp.

+0

Tôi sẽ thử nó nhưng nếu chúng ta phải chỉ định tên của bảng mỗi khi tôi muốn chèn nhiều giá trị, nó sẽ trở thành một chút "phức tạp" của một giải pháp cho tôi. – Molkobain

2

Bạn có thể thử cái nĩa này https://github.com/stas29a/doctrine2. Nó thực hiện chính xác những gì bạn muốn. Tôi đã thử nghiệm nó trong MySQL và nó hoạt động tốt và nhanh hơn 5 lần so với xử lý hàng loạt đó. Ngã ba này có được một id chèn đầu tiên và increments nó trong php để nhận id khác. Nó hoạt động cho hầu hết các trường hợp nhưng không phải trong tất cả. Vì vậy, bạn cần phải hiểu những gì bạn đang làm khi sử dụng ngã ba này.

0

Bạn có thể sử dụng phương thức executeUpdate($query, array $params = array(), array $types = array()) của giao diện DriverConnection để thực hiện tác vụ này. Tuy nhiên nó ít khó khăn để ràng buộc nhiều tham số.

dữ liệu:

$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', 
    ] 
]; 

cập nhật nhiều phương pháp:

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 
    ); 
} 
Các vấn đề liên quan