Reputation: 1496
My table structure contains a lot of tables which are in relation with the user table:
user
user_mails
user_addresses
user_phonenumbers
[...]
So a user can have as many mails, addresses, phone numbers as he wants to. There's an extensive search form to search members, including joined values from the other tables; for example it's possible to search for a users address or his phone number.
To make it possible to search for values in other tables I'm joining all related tables when searching a user.
Since some users have a decent amount of data this means the amount of rows from the query is around ~30k. Actually that's no problem, the query is executed in < 1 second, which is totally fine.
It starts getting weird as soon as the Paginator
component comes into play. My repository method looks similar to this:
// User Repository
public function findUserByCriteria($criteria, $offset, $limit) {
$qb = $this->createQueryBuilder('u');
$qb->leftJoin('u.emails', 'e')->addSelect('u');
$qb->leftJoin('u.addresses', 'adr')->addSelect('adr');
$qb->leftJoin('u.phonenumbers', 'phn')->addSelect('phn');
// 4 more joins
// ...
// the actual search implementation goes here...
// ....
$qb->setFirstResult($offset);
$qb->setMaxResults($limit);
return new Paginator($qb, true);
}
The problem seems to come from the second argument of the Paginator
constructor, fetchJoinCollection
. As long as it's on true
it takes around ~10s to find some users (those with a lot of data), while it just takes around ~1s when it's on false
. The problem when it's set to false
is that the entire pagination is messed up.
I've found out that getting rid of the joins at the top of the method also decreases execution time to around ~1s, but then it's no longer possible to search within these tables.
What is happening here?
Upvotes: 1
Views: 1213
Reputation: 2349
You can try to avoid use Paginator
, it is a pain with large amount of data.
In that cases I do the next: Firstly I calculate the total of data (COUNT) and use that value for my pagination and secondly I get the data with the query.
This example is using DQL instead of Query builder but you can get the idea:
$countDql = "SELECT COUNT (e.id)
FROM YourBundle:Entity e
WHERE e.condition = :yourCondition";
$dataDql = "SELECT e
FROM YourBundle:Entity e
WHERE e.condition = :yourCondition";
$total = $em->createQuery($countDql)->getSingleScalarResult(); //Add also setParameter() if needed
$data = $em->createQuery($dataDql)->getResult();
Hope it helps.
Upvotes: 1
Reputation: 572
Joins are heavy, and Left Joins are even heavier.
You could change your findUserByCriteria
method to add the joins as they are needed according to the $criteria
argument. If there's no criteria for address for example, you don't need to join with the Address
entity.
And if your queries are still way too slow, you can directly use PDO to create them, which is faster than using the querybuilder or DQL.
Upvotes: 0