Подтвердить что ты не робот

Doctrine2 - множественная вставка одним выстрелом

Я новичок в Доктрине, и для меня все еще есть некоторые размытые области. В этом случае я вставляю новую запись в базу данных с помощью цикла и менеджера сущностей. Он отлично работает, но я заметил, что Doctrine делает один запрос вставки сущностью, которая может стать довольно огромной.

Используя Doctrine2 и Symfony 2.3, я хотел бы знать, как мы можем настроить его, чтобы он сделал только один запрос на вставку со всеми значениями в нем (речь идет только об одном объекте, конечно).

Что я имею в виду - это:

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

В

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

Вот мой код:

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

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

$em->flush();
4b9b3361

Ответ 1

В соответствии с этим ответом Doctrine2 не позволяет объединить несколько инструкций INSERT в один:

Некоторые люди, похоже, задаются вопросом, почему Doctrine не использует multi-inserts (вставлять в (...) значения (...), (...), (...),...

Прежде всего, этот синтаксис поддерживается только для mysql и newer postgresql версии. Во-вторых, нет простого способа овладеть всеми сгенерированные идентификаторы в такой мульти-вставке при использовании AUTO_INCREMENT или SERIAL и ORM нужны идентификаторы для идентификации управление объектами. Наконец, производительность вставки редко узкое место ORM. Нормальные вставки более чем достаточно быстро для в большинстве ситуаций, и если вы действительно хотите делать быстрые объемные вставки, multi-insert - это не лучший способ, т.е. Postgres COPY или Mysql LOAD DATA INFILE на несколько порядков быстрее.

Вот причины, по которым не стоит абстракция, которая выполняет множественные вставки на mysql и postgresql в ОРМ.

Подробнее о пакетной обработке Doctrine2 вы можете прочитать здесь: http://www.doctrine-project.org/blog/doctrine2-batch-processing.html

Вы можете либо переключиться на DBAL, либо прибегнуть к обработке ваших данных небольшими партиями, сбросив ваш менеджер объектов после заданного количества вставок:

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

Ответ 2

Вы можете попробовать эту вилку https://github.com/stas29a/doctrine2. Он реализует именно то, что вы хотите. Я тестировал его в MySQL, и он работает нормально и в 5 раз быстрее, чем пакетная обработка. Эта вилка получает первый вставленный id и увеличивает его в php для получения других идентификаторов. Он работает в большинстве случаев, но не во всех. Поэтому вам нужно понять, что вы делаете при использовании этой вилки.

Ответ 3

Для выполнения этого действия вы можете использовать executeUpdate($query, array $params = array(), array $types = array()) метод интерфейса DriverConnection. Однако немного сложно связать несколько параметров.

Данные:

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

Метод массового обновления:

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

Ответ 4

Я не тестировал его, но это возможно сделать с помощью коллекции.

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

Конечно, вы должны иметь добавление в цикле.