2013-10-18 124 views
2

這個問題是關於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; 
+0

可以請您展示您的原始DQL查詢嗎?你是不是應該使用'LEFT INNER JOIN'而不是'LEFT JOIN'?爲什麼'LIMIT'被設置爲'2147483647'? –

+0

LIMIT被設置爲巨大的限制,因爲有時我需要這個參數,有時並不需要任何限制,所以我將它設置爲巨大的數字。此外,你想*總是*使用'OFFSET'(就像我一樣),'LIMIT'成爲強制性的。 –

+0

我可以問爲什麼你使用'AND(l6_.id = 1)'和'AND(l8_.id = 1)'?如果'id'與1不同,'language'和'lesson'將爲空,但查詢仍然會返回該行。如果你需要這種行爲,你應該在代碼中執行,否則在'where'子句中。 DQL查詢是怎樣的?你也許想在多個查詢中拆分你的查詢。 –

回答

0

我建議在選擇部分 的FROM部分放置更多行的表格,並通過這種方式更改行'LEFT JOIN課程l6_ ON l6_.id = p7_.lesson_id AND(l6_.id = 1)'' :看來你並不需要與P7的加入,因爲你強制將l6_.id = 1,所以我改變這個

'LEFT JOIN教訓l6_ ON(l6_.id = 1)'

希望得到這個幫助。

0

不看看你的MySQL表定義,以加快您的查詢確保你已經在參與加入每一列定義的索引,因爲否則mysql必須評估每條記錄缺少索引的表格。

請在您的問題中添加更多詳細信息,我將編輯答案以反映更改。

+0

你走了,我已經更新了完整的DQL。 –