web-dev-qa-db-ja.com

Doctrine2-ワンショットでの複数挿入

Doctrineが初めてで、まだぼやけた領域がいくつかあります。この場合、ループとエンティティマネージャを使用してデータベースに新しいレコードを挿入しています。 Doctrineエンティティごとに1つの挿入クエリを作成することに注意してください。これはかなり大きくなる可能性があります。

Doctrine2とSymfony 2.3を使用して、すべての値を持つ1つの挿入クエリのみを作成するように設定する方法を知りたいと思います(もちろん1つのエンティティについてのみ話します)。

私が意味するのはこれを変えることです:

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();
23
Molkobain

この答え によると、Doctrine2は複数のINSERTステートメントを1つに結合することを許可しません:

一部の人々は、なぜDoctrineは複数挿入((...)値への挿入(...)、(...)、(...)、 ...

まず、この構文はmysql以降のバージョンのpostgresqlでのみサポートされています。第二に、AUTO_INCREMENTまたはSERIALを使用し、ORMがオブジェクトのID管理に識別子を必要とする場合、このようなマルチ挿入で生成されたすべての識別子を保持する簡単な方法はありません。最後に、挿入のパフォーマンスがORMのボトルネックになることはめったにありません。通常の挿入はほとんどの状況で十分に高速であり、本当に高速な一括挿入を実行したい場合は、とにかくマルチ挿入は最善の方法ではありません。つまり、Postgres COPYまたはMysql LOAD DATA INFILEは数桁高速です。

これらが、ORMの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();
43
ukliviu

このフォークを試すことができます https://github.com/stas29a/doctrine2 。それはまさにあなたが望むものを実装しています。 MySQLでテストしたところ、正常に動作し、そのバッチ処理よりも5倍高速です。このフォークは、最初に挿入されたIDを取得し、他のIDを取得するためにphpでインクリメントします。ほとんどの場合は機能しますが、すべてではありません。そのため、このフォークを使用するときに何をしているのかを理解する必要があります。

2
s29a

DriverConnectionインターフェースのexecuteUpdate($query, array $params = array(), array $types = array())メソッドを使用して、このアクションを実行できます。ただし、複数のパラメーターをバインドするのは少し難しいです。

データ:

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