Разделите запрос с помощью doctrine правильно

#sql #performance #symfony #doctrine-orm #doctrine

#sql #Производительность #symfony #doctrine-orm #доктрина

Вопрос:

У меня есть запрос doctrine, который, например, выглядит следующим образом:

 $object = $this->createQueryBuilder('object')
    ->leftJoin('object.element', 'element')->addSelect('element')
    ->leftJoin('object.element2', 'element2')->addSelect('element2')
    ->leftJoin('object.many', 'many')->addSelect('many')
    ->leftJoin('many.element3', 'element3')->addSelect('element3')
    ->leftJoin('many.element4', 'element4')->addSelect('element4')
    ->where('object.id = 1')
    ->getQuery()
    ->getSingleResult();
  

Реальный запрос имеет больше соединений и требует много памяти, а производительность БД невелика. В родном SQL я бы разделил его и правильно загрузил. Что я хочу сделать, так это загрузить с первым запросом объект и некоторые базовые объединенные данные. Это будет выглядеть следующим образом:

 $object = $this->createQueryBuilder('object')
    ->leftJoin('object.element', 'element')->addSelect('element')
    ->leftJoin('object.element2', 'element2')->addSelect('element2')
    ->where('object.id = 1')
    ->getQuery()
    ->getSingleResult();
  

Теперь я также хочу загрузить many , many.element3 и many.element4 . В отдельном запросе. Когда я просто использую функцию отложенной загрузки doctrine, она создаст SQL-запрос для каждого, но я хочу это только как 1 запрос.

Я знаю, что можно было бы установить EAGER это отношение, но я хочу EAGER только временно для этого запроса, не всегда, когда кто-то присоединяется к объекту и получает к нему доступ.

Ответ №1:

Решил мою проблему следующим образом:

В моем объекте я добавил новую функцию set для коллекции.

 /**
 * Set manies.
 */
public function setManies($manies)
{
    // this clear and foreach is needed to keep it as ArrayCollection so doctrine dont need for the unitofwork request the db again
    $this->manies->clear();

    foreach ($manies as $many) {
        $this->manies->add($many);
    }

    return $this;
}
  

В репозитории мой код выглядит следующим образом:

 $object = $this->createQueryBuilder('object')
    ->leftJoin('object.element', 'element')->addSelect('element')
    ->leftJoin('object.element2', 'element2')->addSelect('element2')
    ->where('object.id = 1')
    ->getQuery()
    ->getSingleResult();

$object->setManies($this->getEntityManager()->getRepository(Many::class)->loadByObjectId(
        $object->getId()
    ));

return $object;
  

здесь функция во многих репозиториях

 // loadByObjectId in the many repository
$manies = $this->createQueryBuilder('many')
    ->leftJoin('many.element3', 'element3')->addSelect('element3')
    ->leftJoin('many.element4', 'element4')->addSelect('element4')
    ->where('many.object = 1')
    ->getQuery()
    ->getResult();
  

При этом SQL успешно разделяется в нескольких запросах. В моем случае вместо 60000 строк в 1 запросе у меня было только 40 строк, затронутых в 4 запросе, что значительно ускоряет гидратацию объекта.