Запретить полное сканирование таблицы для запроса с несколькими предложениями where - PullRequest
1 голос
/ 15 мая 2010

Некоторое время назад я опубликовал сообщение об оптимизации запроса в MySQL. С тех пор я перенес данные и запросы в PostgreSQL, но теперь у PostgreSQL та же проблема. Решение в MySQL состояло в том, чтобы заставить оптимизатор не оптимизировать, используя STRAIGHT_JOIN. PostgreSQL не предлагает такой опции.

Обновленное обновление

Я выделил часть запроса, которая решает проблему (d.month_ref_id = 1):

select
  d.*
from
  daily d
join month_ref m on m.id = d.month_ref_id 
join year_ref y on y.id = m.year_ref_id
where
  m.category_id = '001' and
  d.month_ref_id = 1 

Однако я не могу жестко запрограммировать месячную ссылку на 1. Запрос, который производит полное сканирование таблицы:

select
  d.*
from
  daily d
join month_ref m on m.id = d.month_ref_id 
join year_ref y on y.id = m.year_ref_id
where
  m.category_id = '001'

Индекс на daily.month_ref_id:

CREATE INDEX daily_month_ref_idx
  ON climate.daily
  USING btree
  (month_ref_id);

Почему запрос выполняет полное сканирование таблицы и что можно сделать, чтобы этого избежать?

Спасибо!

Ответы [ 3 ]

3 голосов
/ 15 мая 2010
  1. Даже если это не сильно повлияет на производительность, я бы использовал предложения Join для объединения таблиц вместо кросс-соединений и предложение Where.
  2. Вы вызываете функцию в предложении Where, которая заставит систему выполнить сканирование таблицы. Неважно, какую базу данных вы используете, это будет правдой.
  3. Почему левые присоединяются к городу? Знаете ли вы, что данный идентификатор будет существовать (в данном случае 10663? Если это так, вам следует использовать внутреннее соединение.
  4. Возможно, вы сможете дать подсказки компилятору о том, как сформулировать запрос, используя круглые скобки (я не уверен, что Postgres выполнит их).
Select  avg(d.amount) AS amount,  y.year
From (station s
        Left Join city c -- You want to cross join on city? Why not use an Inner join?
            On c.id = 10663
                And 6371.009 
                  * SQRT( 
                        POW(RADIANS(c.latitude_decimal - s.latitude_decimal), 2) 
                        + (
                            COS(RADIANS(c.latitude_decimal + s.latitude_decimal) / 2) 
                            * POW(RADIANS(c.longitude_decimal - s.longitude_decimal), 2)
                            )
                        ) <= 50)
    Join station_district sd
        On sd.Id = s.station_district_id
    Join year_ref y
        On y.station_district_id = sd.id
    Join month_ref m
        On m.year_ref_id = y.id
    Join daily d
        On d.month_ref_id = m.id
Where s.elevation Between 0 And 2000 
    And y.year Between 1980 And 2000
    And m.month = 12
    And m.category_id = '001'
    And d.daily_flag_id <> 'M'
Group By y.year

Поскольку в результатах вы не используете станцию, станцию ​​или стол города, вы можете переместить их в существующий оператор:

Select  avg(d.amount) AS amount,  y.year
From year_ref y
    Join month_ref m
        On m.year_ref_id = y.id
    Join daily d
        On d.month_ref_id = m.id
Where y.year Between 1980 And 2000
    And m.month = 12
    And m.category_id = '001'
    And d.daily_flag_id <> 'M'
    And Exist   (
                Select 1
                From station s1
                    Join city c1
                        On c1.id = 10663
                Where 6371.009 
                      * SQRT( 
                            POW(RADIANS(c1.latitude_decimal - s1.latitude_decimal), 2) 
                            + (
                                COS(RADIANS(c1.latitude_decimal + s1.latitude_decimal) / 2) 
                                * POW(RADIANS(c1.longitude_decimal - s1.longitude_decimal), 2)
                                )
                            ) <= 50
                    And S1.station_district_id = y.station_district_id
                )
Group By y.year
1 голос
/ 15 мая 2010

Я не знаю, какие другие варианты запроса вы пробовали, но JOIN on City кажется немного странным - вы пытались заменить его предложением WHERE? Кроме того, отношения между различными таблицами в настоящее время находятся в предложении WHERE - их, вероятно, лучше всего реализовать как INNER JOIN.

Отказ от ответственности: я не знаю PostreSQL конкретно.

РЕДАКТИРОВАТЬ: Вот ссылка, которая описывает изменение предложений WHERE на JOIN для влияния на порядок соединения, и обсуждает join_collapse_limit, чтобы заставить оптимизатор использовать указанный вами порядок соединения. http://www.postgresql.org/docs/8.2/static/explicit-joins.html

EDIT2. Другой альтернативой является вложение операторов SELECT, что также может заставить оптимизатор построить запрос в указанном (обратном) порядке вложения.

0 голосов
/ 02 мая 2014

Я думаю, что FTS происходит из-за того, как вы параметризовали свой запрос / объединения. Под этим я подразумеваю, что у вас есть два параметра: один сравнивается со столбцом в таблице «daily», а другой сравнивается со столбцом в таблице «month-ref». Однако оба эти значения можно использовать для фильтрации строк в одной таблице «месяц-ссылка». Сделайте эту таблицу основной таблицей в вашем запросе и перепишите запрос следующим образом:

select
  d.*
from month_ref m
join daily d on d.month_ref_id = m.id
join year_ref y on y.id = m.year_ref_id
where
  m.category_id = '001' and
  m.id = 1 

Таким образом, база данных может легко найти все необходимые строки в таблице month-ref, основываясь исключительно на значениях входных параметров, а строки в ежедневной таблице могут быть легко расположены над указанным соединением с помощью индекса, который вы описали. В зависимости от количества строк, которые могут быть найдены в таблице month-ref, и от того, содержат ли какие-либо из столбцов, указанных выше, различные значения, может потребоваться создать индекс для таблицы month-ref.

...