Postgres:按不可变函数索引排序不使用索引

时间:2015-10-25 10:28:16

标签: postgresql indexing sql-order-by

我有一张简单的桌子。

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函数中可能有一些非常复杂算术的索引?

编辑:基于@Егор-Рогов的建议,我稍微更改了一下查询,看看我是否可以使用索引。仍然没有运气。

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的第二个订单,我似乎没有使用索引。

1 个答案:

答案 0 :(得分:1)

我可以看到一些不鼓励计划者使用索引的要点。

1。 在解释输出中查看这一行:

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

它说计划者认为表中只有一行。在这种情况下,使用索引扫描是没有意义的,因为顺序扫描更快。

尝试向表中添加更多行,执行analyze并重试。您还可以通过set enable_seqscan=off;暂时禁用顺序扫描来测试它。

2。 您可以使用该函数对结果进行排序。因此规划人员可能决定使用索引以便以正确的顺序获取元组ID。但是它需要从表中获取每个元组以获取所有列的值(因为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。{/ p>

缺点是指数大小增加了。