Seq Scan在桌子上的原因是什么?

时间:2014-09-23 10:21:10

标签: sql postgresql indexing

我有一个样本日历,如应用程序,用于存储事件,重复和事件重复规则。这是PostgreSQL中的数据库模式:

CREATE TABLE event
(
  id serial NOT NULL,
  title character varying(2000) NOT NULL,
  description character varying(2000) DEFAULT NULL::character varying,
  location character varying(2000) DEFAULT NULL::character varying,
  CONSTRAINT pk_event_id PRIMARY KEY (id)
)

CREATE TABLE event_repeat_rule
(
  id serial NOT NULL,
  event_id integer NOT NULL,
  start_date bigint NOT NULL,
  end_date bigint,
  count integer,
  repeat_type repeat_t NOT NULL,
  fixed_interval integer NOT NULL,
  day_of_month integer[] NOT NULL,
  day_of_week integer[] NOT NULL,
  week_of_month week_of_month_t[] NOT NULL,
  month_of_year integer[] NOT NULL,
  CONSTRAINT pk_event_repeat_rule PRIMARY KEY (id),
  CONSTRAINT fk_event_repeat_rule FOREIGN KEY (event_id)
      REFERENCES event (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT uq_event_repeat_rule_event_id UNIQUE (event_id)
)

-- each event can be labeled with multiple tags. Tag table is not shown here.
CREATE TABLE event_tag
(
  id serial NOT NULL,
  event_id integer NOT NULL,
  tag_id integer NOT NULL,
  CONSTRAINT pk_event_tag_id PRIMARY KEY (id),
  CONSTRAINT fk_event_tag_event_id FOREIGN KEY (event_id)
      REFERENCES event (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT fk_event_tag_tag_id FOREIGN KEY (tag_id)
      REFERENCES tag (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT uq_evnet_tag_event_id_tag_id UNIQUE (event_id, tag_id)
)

CREATE INDEX idx_event_tag_tag_id
  ON event_tag
  USING btree
  (tag_id);

CREATE TABLE event_time
(
  id serial NOT NULL,
  event_id integer NOT NULL,
  start_time bigint NOT NULL,
  end_time bigint,
  CONSTRAINT pk_event_time_id PRIMARY KEY (id),
  CONSTRAINT fk_event_time_event_id FOREIGN KEY (event_id)
      REFERENCES event (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
)

CREATE INDEX idx_event_time_event_id_start_time_end_time
  ON event_time
  USING btree
  (event_id, start_time, end_time);

架构的整体描述:每个事件都有重复规则。每个事件都可以用标签标记。(与Tag表有多对多的关系)。每个事件(单个或重复)的所有时间都在event_time表中,因此关系是1到多个。 (event_id, start_time, end_time)表中的event_time上有一个索引。

我根据tag_idstart_time查询此架构。这是我的问题:

SELECT * FROM 
event_time 
JOIN event ON event_time.event_id = event.id 
JOIN event_tag ON event_tag.event_id = event.id 
LEFT OUTER JOIN event_repeat_rule ON event.id = event_repeat_rule.event_id 
WHERE event_tag.tag_id = 1 
AND event_time.start_time <= 1411465037 
AND event_time.end_time >= 1408873037;

当我在此查询中使用EXPLAIN运行此查询时,我得到了这个:

Nested Loop Left Join  (cost=3.08..15.75 rows=2 width=587)
  ->  Hash Join  (cost=2.93..9.75 rows=2 width=423)
        Hash Cond: (event_time.event_id = event.id)
        ->  Seq Scan on event_time  (cost=0.00..6.69 rows=22 width=24)
              Filter: ((start_time <= 1411465037) AND (start_time >= 1408873037))
        ->  Hash  (cost=2.87..2.87 rows=5 width=399)
              ->  Hash Join  (cost=1.52..2.87 rows=5 width=399)
                    Hash Cond: (event.id = event_tag.event_id)
                    ->  Seq Scan on event  (cost=0.00..1.17 rows=17 width=386)
                    ->  Hash  (cost=1.45..1.45 rows=6 width=13)
                          ->  Seq Scan on event_tag  (cost=0.00..1.45 rows=6 width=13)
                                Filter: (tag_id = 1)
  ->  Index Scan using uq_event_repeat_rule_event_id on event_repeat_rule  (cost=0.15..2.99 rows=1 width=164)
        Index Cond: (event.id = event_id)

我几乎在所有牌桌上都获得了Seq Scan。记录数量可能是原因。但我不想根据估算进行设计。 event_time表上(event_id, start_time, end_time)的索引是否可以满足此查询?

1 个答案:

答案 0 :(得分:2)

  

我基于tag_id和start_time查询此架构。

您查询“tag_id”和“start_time”。您想知道您的查询是否可以在{“event_id”,“start_time”,“end_time”}上使用索引?

不,它不能使用那个索引。该索引根本不包含“tag_id”,“start_time”不是第一列。但是,在WHERE子句中使用“event_id”和“start_time”的查询应该使用该索引。

列“tag_id”和“start_time”位于不同的表中。列对{{event_tag'。“event_id”,“event_tag”。“tag_id”上有一个现有索引。 (这些列的UNIQUE约束是使用唯一索引实现的。)但该索引不适用于仅引用“event_tag”的查询。“tag_id”。

“start_time”相同。 “event_id”列是索引中的第一个,因此索引可能不会用于不引用“event_time”的查询。“event_id”。

我会尝试添加这两个索引。 。

create index on event_tag (tag_id);
create index on event_time (start_time, end_time);

然后加载一百万行随机数据,分析表格,再次查看查询计划。


我认为“start_time”和“end_time”没有令人信服的理由在一个单独的表中。考虑将这些列移动到表“事件”中。

事件标题未声明为唯一。这意味着您可能(将)最终拥有多个具有相同标题的事件。

在每个表上使用id号是反模式。例如,列“event_tag”。“id”除了减慢查询速度外没有其他用途。 (它没有意义;它使表格更宽,因此数据页面上的行数更少;它是多余的,因为另一对列被声明为唯一;等等。)