Если я хочу выбрать 0,5% строк или даже 5% строк из следующей таблицы с помощью ПК, планировщик запросов правильно решит использовать индекс PK. Вот таблица:Почему PostgreSql не использует индекс PK?
create table weather as
with numbers as(
select generate_series as id from generate_series(0,1048575))
select id,
50 + 50*sin(id) as temperature_in_f,
50 + 50*sin(id) as humidity_in_percent
from numbers;
alter table weather
add constraint pk_weather primary key(id);
vacuum analyze weather;
В статы уточненный, и следующий запрос действительно использует индекс PK:
explain analyze select sum(w.id), sum(humidity_in_percent), count(*)
from weather as w
where w.id between 1 and 66720;
Предположим, однако, что мы должны присоединиться к этому столу другой, гораздо меньше, один:
create table lightnings
as
select id as weather_id
from weather
where humidity_in_percent between 99.99 and 100;
alter table lightnings
add constraint pk_lightnings
primary key(weather_id);
analyze lightnings;
Вот мой присоединиться, в четырех логически эквивалентных формах:
explain analyze select sum(w.id), count(*) from weather as w
where w.humidity_in_percent between 99.99 and 100
and exists(select * from lightnings as l
where l.weather_id=w.id);
explain analyze select sum(w.id), count(*)
from weather as w
join lightnings as l
on l.weather_id=w.id
where w.humidity_in_percent between 99.99 and 100;
explain analyze select sum(w.id), count(*)
from lightnings as l
join weather as w
on l.weather_id=w.id
where w.humidity_in_percent between 99.99 and 100;
-- replaced explicit join with where clause
explain analyze select sum(w.id), count(*)
from lightnings as l, weather as w
where w.humidity_in_percent between 99.99 and 100
and l.weather_id=w.id;
К сожалению, планировщик запросова прибегает к сканируя всю погодную таблицу: оценка
"Aggregate (cost=22645.68..22645.69 rows=1 width=4) (actual time=167.427..167.427 rows=1 loops=1)"
" -> Hash Join (cost=180.12..22645.52 rows=32 width=4) (actual time=2.500..166.444 rows=6672 loops=1)"
" Hash Cond: (w.id = l.weather_id)"
" -> Seq Scan on weather w (cost=0.00..22407.64 rows=5106 width=4) (actual time=0.013..158.593 rows=6672 loops=1)"
" Filter: ((humidity_in_percent >= 99.99::double precision) AND (humidity_in_percent <= 100::double precision))"
" Rows Removed by Filter: 1041904"
" -> Hash (cost=96.72..96.72 rows=6672 width=4) (actual time=2.479..2.479 rows=6672 loops=1)"
" Buckets: 1024 Batches: 1 Memory Usage: 235kB"
" -> Seq Scan on lightnings l (cost=0.00..96.72 rows=6672 width=4) (actual time=0.009..0.908 rows=6672 loops=1)"
"Planning time: 0.326 ms"
"Execution time: 167.581 ms"
планировщик запросов на сколько строк в таблице погоды будет выбраны строки = 5106. Это более или менее близко к точному значению 6672. Если я выберу это небольшое количество строк в таблице погоды через id, используется индекс PK. Если я выберу одну и ту же сумму через соединение с другой таблицей, планировщик запросов отправляется на сканирование таблицы.
Что мне не хватает?
select version()
"PostgreSQL 9.4.0"
Edit: если я удалю условие на влажность, планировщик запросов правильно распознает, что условие на weather.id довольно избирательно, и решает использовать индекс на ПК:
explain analyze select sum(w.id), count(*) from weather as w
where exists(select * from lightnings as l
where l.weather_id=w.id);
"Aggregate (cost=14677.84..14677.85 rows=1 width=4) (actual time=37.200..37.200 rows=1 loops=1)"
" -> Nested Loop (cost=0.42..14644.48 rows=6672 width=4) (actual time=0.022..36.189 rows=6672 loops=1)"
" -> Seq Scan on lightnings l (cost=0.00..96.72 rows=6672 width=4) (actual time=0.011..0.868 rows=6672 loops=1)"
" -> Index Only Scan using pk_weather on weather w (cost=0.42..2.17 rows=1 width=4) (actual time=0.005..0.005 rows=1 loops=6672)"
" Index Cond: (id = l.weather_id)"
" Heap Fetches: 0"
"Planning time: 0.321 ms"
"Execution time: 37.254 ms"
Еще добавление условия полностью путает планировщик запросов.
Не могли бы вы показать нам свой файл конфигурации? Особенно random_page_cost, это оказывает огромное влияние на последовательное чтение и индексное сканирование –
Я убедился, что set random_page_cost = 4; - Никакой разницы. – AlexC
Более низкое значение для random_page_cost выгодно сканирует индекс, ваш первичный ключ. установите random_page_cost равным 2.6; дает мне сканирование индекса по всем примерам запросов. –