2015-10-25 4 views
0

У меня есть простая таблица.Postgres: Сортировка по индексу неизменной функции не использует индекс

CREATE TABLE posts 
(
    id uuid NOT NULL, 
    vote_up_count integer, 
    vote_down_count integer, 
    CONSTRAINT post_pkey PRIMARY KEY(id) 
); 

У меня есть IMMUTABLE функцию, которая делает простой (но может быть сложным) арифметика.

CREATE OR REPLACE FUNCTION score(
    ups integer, 
    downs integer) 
    RETURNS integer AS 
$BODY$ 
    select $1 - $2 
$BODY$ 
    LANGUAGE sql IMMUTABLE 
    COST 100; 
ALTER FUNCTION score(integer, integer) 
    OWNER TO postgres; 

создать индекс на posts таблицу, которая использует свою функцию.

CREATE INDEX posts_score_index ON posts(score(vote_up_count, vote_down_count), date_created); 

Когда я EXPLAIN следующий запрос, он не кажется, что с помощью индекса.

SELECT * FROM posts ORDER BY score(vote_up_count, vote_down_count), date_created 

Sort (cost=1.02..1.03 rows=1 width=310) 
    Output: id, date_created, last_edit_date, slug, sub_id, user_id, user_ip, type, title, content, url, domain, send_replies, vote_up_count, vote_down_count, verdict, approved_by, removed_by, verdict_message, number_of_reports, ignore_reports, number_of_com (...)" 
    Sort Key: ((posts.vote_up_count - posts.vote_down_count)), posts.date_created 
    -> Seq Scan on public.posts (cost=0.00..1.01 rows=1 width=310) 
     Output: id, date_created, last_edit_date, slug, sub_id, user_id, user_ip, type, title, content, url, domain, send_replies, vote_up_count, vote_down_count, verdict, approved_by, removed_by, verdict_message, number_of_reports, ignore_reports, number_ (...) 

Как мне получить ORDER BY, чтобы использовать индекс из IMMUTABLE функции, которые могут иметь некоторые очень сложную арифметику?

EDIT: Исходя из предложений @ Егор-Рогов, я немного меняю запрос, чтобы узнать, могу ли я заставить его использовать индекс. Еще не повезло.

set enable_seqscan=off; 
EXPLAIN VERBOSE select date_created from posts ORDER BY (hot(vote_up_count, vote_down_count, date_created),date_created); 

Вот вывод.

Sort (cost=10000000001.06..10000000001.06 rows=1 width=16) 
    Output: date_created, (ROW(round((((log((GREATEST(abs((vote_up_count - vote_down_count)), 1))::double precision) * sign(((vote_up_count - vote_down_count))::double precision)) + ((date_part('epoch'::text, date_created) - 1134028003::double precision)/4 (...) 
    Sort Key: (ROW(round((((log((GREATEST(abs((posts.vote_up_count - posts.vote_down_count)), 1))::double precision) * sign(((posts.vote_up_count - posts.vote_down_count))::double precision)) + ((date_part('epoch'::text, posts.date_created) - 1134028003::dou (...) 
    -> Seq Scan on public.posts (cost=10000000000.00..10000000001.05 rows=1 width=16) 
     Output: date_created, ROW(round((((log((GREATEST(abs((vote_up_count - vote_down_count)), 1))::double precision) * sign(((vote_up_count - vote_down_count))::double precision)) + ((date_part('epoch'::text, date_created) - 1134028003::double precision (...) 

EDIT2: Кажется, что я не использовал индекс из-за второго порядка по с date_created.

+0

Ну, вы читаете ** все ** строки со стола. Индекс, прежде всего, должен * уменьшить * количество строк. Это редко используется для сортировки. Если вам вообще нужен индекс покрытия, который включает все выбранные вами столбцы. –

ответ

1

Я вижу пару моментов, которые препятствуют планировщику использовать индекс.

1. Посмотрите на этой линии на выходе объяснить:

Seq Scan on public.posts (cost=0.00..1.01 rows=1 width=310) 

Это говорит о том, что планировщик считает, что существует только одна строка в таблице. В этом случае нет смысла использовать сканирование индексов, поскольку последовательное сканирование выполняется быстрее.

Попробуйте добавить еще несколько строк в таблицу, сделайте analyze и повторите попытку. Вы также можете протестировать его, временно отключив последовательное сканирование на set enable_seqscan=off;.

2. Вы используете функцию сортировки результатов. Поэтому планировщик может решить использовать индекс, чтобы получить идентификаторы кортежей в правильном порядке. Но тогда ему нужно получить каждый кортеж из таблицы, чтобы получить значения всех столбцов (из-за select *).

Вы можете сделать индекс более привлекательным для планировщика, добавив в него все необходимые столбцы, что позволит избежать сканирования таблицы. Это называется сканирование только по индексу.

CREATE INDEX posts_score_index ON posts(
    score(vote_up_count, vote_down_count), 
    date_created, 
    id,    -- do you actually need it in result set? 
    vote_up_count, -- do you actually need it in result set? 
    vote_down_count -- do you actually need it in result set? 
); 

И убедитесь, что вы запустите vacuum после вставки/обновления/удаления строк для обновления visibility map.

Недостатком является увеличенный размер индекса, конечно.

+0

Я только что обновил свой вопрос. Запрос '' 'set enable_seqscan = off; EXPLAIN VERBOSE select date_created from posts ORDER BY (hot (vote_up_count, vote_down_count, date_created), date_created); '' 'все еще не использовал индекс. –

+0

Что такое hot() здесь? Пожалуйста, предоставьте все сценарии, чтобы воспроизвести проблему. В исходном запросе используется индекс, я тестировал это. –

+0

Прошу прощения. У меня есть несколько функций, которые используют индекс. Я смешиваю их. Это все арифметика. –

Смежные вопросы