0

次の MySQL クエリを最適化しようとしています。

SELECT 
    `l`.`id` AS `l__id`, `l`.`latitude` AS `l__latitude`, `l`.`longitude` AS `l__longitude`, `l`.`name` AS `l__name`, `f`.`id` AS `f__id`, `f`.`day` AS `f__day`, `f`.`temperature_low` AS `f__temperature_low`, `f`.`temperature_high` AS `f__temperature_high`, `c`.`id` AS `c__id`, `c`.`name` AS `c__name` 
FROM `location` `l` 
INNER JOIN `forecast` `f` ON `l`.`id` = `f`.`location_id` 
INNER JOIN `condition` `c` ON `f`.`condition_id` = `c`.`id` 
WHERE (
    `f`.`day` IN ('2012-12-28', '2012-12-29') AND 
    EXISTS (
        SELECT 
            `f2`.`id` AS `f2__id` 
        FROM `forecast` `f2` 
        WHERE (
            `f2`.`location_id` = `l`.`id` AND `f2`.`day` = "2012-12-28" AND `f2`.`condition_id` IN (6, 7, 9, 10, 11, 12, 13, 14, 18) AND `f2`.`temperature_high` <= 30 AND `f2`.`temperature_low` >= 0
        )
    ) AND 
    EXISTS (
        SELECT 
            `f3`.`id` AS `f3__id` 
        FROM `forecast` `f3` 
        WHERE (
            `f3`.`location_id` = `l`.`id` AND `f3`.`day` = "2012-12-29" AND `f3`.`condition_id` IN (6, 7, 9, 10, 11, 12, 13, 14, 18) AND `f3`.`temperature_high` <= 30 AND `f3`.`temperature_low` >= 0
        )
    ) 
    AND `l`.`latitude` IS NOT NULL AND `l`.`longitude` IS NOT NULL
);

EXPLAIN を実行すると、次の出力が得られます。

+----+--------------------+-------+--------+--------------------------------------+-----------------+---------+------------------------+------+----------------------------------------------+
| id |    select_type     | table |  type  |            possible_keys             |       key       | key_len |          ref           | rows |                    Extra                     |
+----+--------------------+-------+--------+--------------------------------------+-----------------+---------+------------------------+------+----------------------------------------------+
|  1 | PRIMARY            | f     | range  | location_id_idx,condition_id_idx,day | day             |       3 | NULL                   | 1298 | Using where; Using temporary; Using filesort |
|  1 | PRIMARY            | l     | eq_ref | PRIMARY                              | PRIMARY         |       8 | weather.f.location_id  |    1 | Using where                                  |
|  1 | PRIMARY            | c     | eq_ref | PRIMARY                              | PRIMARY         |       8 | weather.f.condition_id |    1 |                                              |
|  3 | DEPENDENT SUBQUERY | f3    | ref    | location_id_idx,condition_id_idx,day | location_id_idx |       9 | weather.l.id           |  276 | Using where                                  |
|  2 | DEPENDENT SUBQUERY | f2    | ref    | location_id_idx,condition_id_idx,day | location_id_idx |       9 | weather.l.id           |  276 | Using where                                  |
+----+--------------------+-------+--------+--------------------------------------+-----------------+---------+------------------------+------+----------------------------------------------+

問題は「whereの使用;一時的な使用;filesortの使用」とサブクエリであることは理解していますが、パフォーマンスのためにこれを書き直す方法がわかりません。

今後このような問題を解決するのに役立つヒントを事前にありがとうございます。

ピート

4

1 に答える 1

1

クエリでは、セットを制限する述語を指定していforecastますが、テーブルを順不同で結合しています。mysql オプティマイザーはいくつかの作業を行い、予測でキーを使用し、他のテーブルを結合する前にday使用する予測セットを制限します。f.day IN ('2012-12-28', '2012-12-29')オプティマイザーは、依存するサブクエリ (不要に見える) を使用して、 によって設定された予測をさらに制限することはできません。condition_id結合temperature_highするtemperature_low前に、これはすべての結合が行われた後に行われます。クエリを改善するには、依存サブクエリを削除し、最初に予測セットを制限する必要があります。

SELECT *
FROM forecast f 
INNER JOIN location l 
  ON l.id = f.location_id AND l.latitude IS NOT NULL AND l.longitude IS NOT NULL
INNER JOIN condition c
  ON f.condition_id = c.id
WHERE f.day IN ('2012-12-28', '2012-12-29') 
  AND f.condition_id IN (6, 7, 9, 10, 11, 12, 13, 14, 18) 
  AND f.temperature_high <= 30 AND f.temperature_low >= 0
于 2013-02-16T01:05:27.150 に答える