Почему мой подзапрос выполняется даже для отфильтрованных строк? - PullRequest
2 голосов
/ 22 ноября 2011

У меня есть запрос, который выглядит примерно так (примечание: фактический запрос генерируется Hibernate и немного сложнее):

select * from outage_revisions orev
join outages o
    on orev.outage=o.id
    where o.observed_end is null
    and orev.observation_date =
        (select max(observation_date)
            from outage_revisions orev2
            where orev2.observation_date <= '2011-11-21 00:00:00'
            and orev2.outage = orev.outage);

Этот запрос выполняется очень медленно (около 15 минут),Однако, если я уберу часть предложения where с подзапросом, он вернется почти мгновенно (около 83 миллисекунд) только с 14 строками.

Кроме того, сам подзапрос очень быстрый (около31 миллисекунда):

select max(observation_date) from outage_revisions orev2
where orev2.observation_date <= '2011-11-21 00:00:00'
and orev2.outage = 1

У меня такой вопрос: если из полного запроса возвращается только 14 строк, исключая фильтр подзапроса, почему добавление подзапроса так сильно замедляет запрос?Разве подзапрос не должен добавлять самое большее приблизительно 31 * 14 миллисекунд?

Вот план полного запроса:

Nested Loop  (cost=0.00..71078813.16 rows=1 width=115)
   ->  Seq Scan on outagerevisions orev  (cost=0.00..71077624.67 rows=284 width=79)
         Filter: (observationdate = (SubPlan 2))
         SubPlan 2
           ->  Result  (cost=1250.56..1250.57 rows=1 width=0)
                 InitPlan 1 (returns $1)
                   ->  Limit  (cost=0.00..1250.56 rows=1 width=8)
                         ->  Index Scan Backward using idx_observationdate on outagerevisions orev2  (cost=0.00..2501.12 rows=2 width=8)
                               Index Cond: (observationdate <= '2011-11-21 00:00:00'::timestamp without time zone)
                               Filter: ((observationdate IS NOT NULL) AND (outage = $0))
   ->  Index Scan using outages_pkey on outages o  (cost=0.00..4.17 rows=1 width=36)
         Index Cond: (o.id = orev.outage)
         Filter: (o.observedend IS NULL)

1 Ответ

3 голосов
/ 22 ноября 2011

Я предполагаю, что PostgreSQL просто делает плохой выбор в отношении того, как он выполняет запрос. Хотя кажется очевидным, что он должен сузиться до 9 строк перед выполнением коррелированного подзапроса, он, вероятно, этого не делает, поэтому подзапрос должен выполняться 60000 раз. При этом он также должен отслеживать, какие строки перейдут к следующему шагу и т. Д.

Вот несколько других способов, которыми вы могли бы попытаться написать это:

SELECT
    <column list>
FROM
    Outage_Revisions OREV
JOIN Outages O ON
    OREV.outage = O.id
LEFT OUTER JOIN Outage_Revisions OREV2 ON
    OREV2.outage = OREV.outage AND
    OREV2.observation_date <= '2011-11-21 00:00:00' AND
    OREV2.observation_date > OREV.observation_date
WHERE
    O.observed_end IS NULL AND
    OREV2.outage IS NULL

или (при условии, что PostgreSQL и Hibernate поддерживают присоединение к подзапросам)

SELECT
    <column list>
FROM
    Outage_Revisions OREV
JOIN Outages O ON
    OREV.outage = O.id
JOIN (SELECT OREV2.outage, MAX(OREV2.observation_date) AS max_observation_date
      FROM Outage_Revisions OREV2
      WHERE OREV2.observation_date <= '2011-11-21 00:00:00'
      GROUP BY OREV2.outage) SQ ON
    SQ.outage = OREV.outage AND
    SQ.max_observation_date = OREV.observation_date
WHERE
    O.observed_end IS NULL

Вы можете изменить порядок соединений в последнем запросе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...