这个问题是关于Doctrine和Symfony2。 我用Doctrine DQL做了一个查询。和Doctrine这样生成一个SQL;DSL和SQL查询很慢
SELECT f0_.id AS id0, f0_.nom AS nom1, f0_.prenom AS prenom2, f0_.email AS email3, p1_.move_distance AS move_distance4, a2_.adresse1 AS adresse15, a2_.adresse2 AS adresse26, p3_.nom AS nom7, v4_.nom AS nom8, v4_.url AS url9, v4_.cp AS cp10, v4_.insee AS insee11, v4_.lat AS lat12, v4_.lng AS lng13, COUNT(f0_.id) AS sclr17
FROM person_teacher p1_
INNER JOIN fos_user f0_ ON p1_.id = f0_.id
LEFT JOIN person_lesson p7_ ON f0_.id = p7_.person_id
LEFT JOIN lesson l6_ ON l6_.id = p7_.lesson_id AND (l6_.id = 1)
LEFT JOIN person_teacher_language p9_ ON p1_.id = p9_.personteacher_id
LEFT JOIN language l8_ ON l8_.id = p9_.language_id AND (l8_.id = 1)
LEFT JOIN note_value n10_ ON p1_.id = n10_.personTeacher_id
LEFT JOIN pays p3_ ON f0_.id_pays = p3_.id
LEFT JOIN note n5_ ON n10_.id_note = n5_.id
LEFT JOIN person_teacher_adresse p11_ ON p1_.id = p11_.personteacher_id
LEFT JOIN adresse a2_ ON a2_.id = p11_.adresse_id
LEFT JOIN ville v4_ ON a2_.id_ville = v4_.id
GROUP BY f0_.id LIMIT 2147483647 OFFSET 0;
的问题是关于这些联接:
LEFT JOIN person_lesson p7_ ON f0_.id = p7_.person_id
LEFT JOIN lesson l6_ ON l6_.id = p7_.lesson_id AND (l6_.id = 1)
LEFT JOIN person_teacher_language p9_ ON p1_.id = p9_.personteacher_id
LEFT JOIN language l8_ ON l8_.id = p9_.language_id AND (l8_.id = 1)
如果我删除它们,请求工作。长时间的请求,但有效。 随着连接,请求是无限的(5mn后MySQL使用99.9%的CPU时间)或者可能很长,但无论如何,太长了。
如何优化此查询?
(PS:我认为AND (l6_.id = 1)
和AND (l8_.id = 1)
将作为“过滤器”,并立即删除不必要的行,但是没有,这让事情变得更糟:如果我删除这些条件,并在末尾添加where
条款它的速度更快,是这样的:WHERE (l6_.id = 1) AND (l8_.id = 1)
)
这里是我的DQL代码:
$retour = $this->createQueryBuilder('p')
->select(array(
'p.id',
'p.nom',
'p.prenom',
'p.email',
'p.moveDistance',
'a.adresse1',
'a.adresse2',
'pn.nom as pays',
'v.nom AS ville_nom',
'v.url',
'v.cp',
'v.insee',
'v.lat',
'v.lng',
'ROUND(' .
$mul.' * ' .
'ACOS(' .
'COS(RADIANS('.$lat.')) * '.
'COS(RADIANS(v.lat )) * '.
'COS(RADIANS(v.lng)-radians('.$lng.')) + '.
'SIN(RADIANS('.$lat.')) * '.
'SIN(RADIANS(v.lat)) ' .
')'.
',2) AS distance',
($in_kilometers?'\'km\'':'\'miles\'').' AS unit',
'ROUND(AVG(n.importance), 1) AS importance',
'COUNT(p.id) AS total'
))
->leftJoin('p.noteValues', 'nv')
->leftJoin('p.paysNaissance', 'pn')
->leftJoin('nv.note', 'n')
->leftJoin('p.adresses', 'a')
->leftJoin('a.ville', 'v');
/* (!) Optimizer: find out why if I do a join "ON"
* it endlessly query. I did classical "join" then a "WHERE"
* at the end. Find out why this method is faster:
*/
if ($lesson_id>0) {
$retour = $retour
->leftJoin('p.lessons', 'le');
}
if ($language_id>0) {
$retour = $retour
->leftJoin('p.languages', 'ln');
}
if (($lesson_id>0) && ($language_id>0)) {
$retour = $retour
->where('le.id = :lesson_id')
->andWhere('ln.id = :language_id');
}
elseif ($lesson_id>0) {
$retour = $retour
->where('le.id = :lesson_id');
}
elseif ($language_id>0) {
$retour = $retour
->where('ln.id = :language_id');
}
$retour = $retour
->groupBy('p.id')
->having('distance>:dmin')
->andHaving('distance<=:dmax')
->addOrderBy($order_by_1, $order_sens_1)
->addOrderBy($order_by_2, $order_sens_2);
$params=array(
'dmin' => $distance_min,
'dmax' => $distance_max
);
if ($lesson_id>0) {
$params['lesson_id']= $lesson_id;
}
if ($language_id>0) {
$params['language_id']= $language_id;
}
$retour = $retour->setParameters($params);
$retour = $retour
->setFirstResult($offset)
->setMaxResults($limit);
return $retour;
可以请您展示您的原始DQL查询吗?你是不是应该使用'LEFT INNER JOIN'而不是'LEFT JOIN'?为什么'LIMIT'被设置为'2147483647'? –
LIMIT被设置为巨大的限制,因为有时我需要这个参数,有时并不需要任何限制,所以我将它设置为巨大的数字。此外,你想*总是*使用'OFFSET'(就像我一样),'LIMIT'成为强制性的。 –
我可以问为什么你使用'AND(l6_.id = 1)'和'AND(l8_.id = 1)'?如果'id'与1不同,'language'和'lesson'将为空,但查询仍然会返回该行。如果你需要这种行为,你应该在代码中执行,否则在'where'子句中。 DQL查询是怎样的?你也许想在多个查询中拆分你的查询。 –