2

この質問は、Doctrine と Symfony2 の両方に関するものです。Doctrine DQL を使用してクエリを作成しました。Doctrine は次のような 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)

それらを削除すると、リクエストは機能します。長いリクエストですが、機能します。結合により、リクエストは無限 (5 分後に MySQL によって使用される 99.9% の CPU 時間) になるか、非常に長くなる可能性がありますが、とにかく長すぎます

このクエリを最適化する方法は?

(PS : とは「フィルター」として機能し、不要な行をすぐに削除するAND (l6_.id = 1)と思っていましたが、そうではありません。事態はさらに悪化します。これらの条件を削除し、最後に句を追加すると、次のように高速になります: )AND (l8_.id = 1)whereWHERE (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;
4

2 に答える 2

0

select の FROM 部分の後に行を追加してテーブルを配置し、「LEFT JOIN lesson l6_ ON l6_.id = p7_.lesson_id AND (l6_.id = 1)」という行を次のように変更することをお勧めします。強制的にl6_.id=1になるため、p7に参加する必要はないので、これに変更します

「LEFT JOIN レッスン l6_ON (l6_.id = 1)」

この助けを願っています。

于 2013-10-19T01:39:27.973 に答える
0

MySQL テーブル定義を見ずに、クエリを高速化するには、結合に関係する各列にインデックスを定義していることを確認してください。そうしないと、mysql はインデックスを欠いているテーブルの各レコードを評価する必要があります。

質問に詳細を追加してください。回答を編集して変更を反映させます。

于 2013-10-18T16:35:20.790 に答える