#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 запросе, что значительно ускоряет гидратацию объекта.