В вашем подзапросе вы должны вернуть и name
, который вы уже возвращаете, и cities.name as cname
.Затем вы должны сделать ilike
на cname
вместо name
.Проблема в том, что сейчас PostgreSQL не может рассчитывать на то, что, поскольку 'ashville%'
не содержит запятых, он может просто посмотреть на название города в подзапросе, поэтому он действительно должен(и, основываясь на вашем объяснении) итерируйте и постройте каждую возможную строку, чтобы выполнить этот последний фильтр.Если вы вернете cities.name
обратно к верхнему запросу, это значительно улучшит производительность, поскольку сейчас он серьезно не может использовать ни один из тех индексов, которые у вас есть.
На самом деле, вы должны пройти весь путь здесь,и просто удалите конкатенацию строк внутри запроса и верните то, что вы действительно хотели: select cities.name as city, regions.name as region, countries.code as country
, и измените сортировку на order by t1.city, t1.region, t1.country
.
Кроме того, вы действительно запрашиваете города, которыеиметь 'ashville%'
, или это просто косвенный способ поиска городов, которые 'ashville'
, но вам приходится иметь дело с разграничением запятых внутри?Затем, снаружи, используйте lower(t1.city) = 'ashville'
(обратите внимание, что =
: lower(x) ilike 'lower'
бессмысленно медленен).
Кроме того, вам нужно исправить эти индексы: то, что вы действительно хотите, это create index whatever on cities((lower(name)))
, так как это то, чтовы на самом деле ищете, а не name
: вы не сможете использовать эти индексы, если вы ищете что-то, что не имеет отношения к тому, что у вас есть в индексе.
(Выможно было бы взглянуть на order by name
позже и подумать, что оно больше не будет ускоряться, но это нормально: цель здесь - быстро отфильтровать от множества возможных мест до крошечного набора тех, которыми вы собираетесь управлятьна; то, что осталось, может быть быстро отсортировано в памяти, поскольку вы, вероятно, имеете дело с 10-20 результатами.)
Из-за этого, поскольку regions.id
и countries.id
, вероятно, primary key
s,другие индексы можно удалить, если они существуют только для этого запроса.
Наконец, выровняйте запрос до одного уровня, удалите group by
и замените его на distinct
.Проблема в том, что мы хотим убедиться, что не заставляем PostgreSQL генерировать полный набор перед попыткой фильтрации: мы хотим убедиться, что у него достаточно знаний о цели, чтобы иметь возможность использовать индекс города для быстрого сканирования напрямуюв города, которые могут совпадать, и , а затем переходят к заполнению информации о регионе и стране.
(PostgreSQL, как правило, очень очень хорош в этом, дажечерез подзапрос, но поскольку у нас есть предложение group by
через having
, я могу видеть ситуации, когда он больше не сможет выводить.)
(правка) На самом деле, подождите: выиметь уникальный индекс на cities (name, region_id)
, поэтому вам даже не нужен distinct
... все, что он делает, это делает запрос бессмысленно более сложным.Я просто удалил его из запроса: результат будет таким же, так как вы не сможете получить результат, когда у вас будет один и тот же город в одном регионе / стране, который будет возвращен дважды.
select
cities.name as city,
regions.name as region,
countries.code as country
from cities
join regions on
regions.id = cities.region_id
join countries on
countries.id = regions.country_id
where
lower(cities.name) = 'asheville'
order by
cities.name,
regions.name,
countries.code
limit 10;
create index "cities(lower(name))" on cities ((lower(name)));
(правка) Если, кстати, вы на самом деле намереваетесь выполнить сопоставление префиксов, то вам нужно изменить = 'asheville'
обратно на like 'ashevill%'
(обратите внимание на like
: нет i
),и измените указанный индекс следующим образом:
create index "cities(lower(name))" on cities ((lower(name)) text_pattern_ops);