2014-09-23 41 views
1

我有一个样本日历,如应用程序存储事件,它们的重复和事件重复规则。这里是PostgreSQL中的数据库模式:Seq Scan在桌上扫描的原因是什么?

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); 

模式的总体描述:每个事件都有重复规则与否。每个事件都可以用标签进行标记(与标签表有多对多关系)。并且每个事件(单个或重复)的所有时间在event_time表中,所以关系是1到很多。 event_time表中(event_id, start_time, end_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)能满足这个查询吗?

+0

我们通常要求CREATE TABLE和INSERT语句,所以我们可以把时间花在你的问题上,而不是花时间对你的模式进行逆向工程。你的CREATE TABLE语句不可用,因为它们的立场;我们没有用户定义的类型(repeat_t等)。 – 2014-09-23 11:26:51

+0

@ MikeSherrill'CatRecall'实际的问题是如何以正确的方式使用索引。但我不能指望你在没有查看模式的情况下回答......所以最重要的字段是“PRIMARY KEY”和“FOREIGN KEY”和索引列。 – 2014-09-23 11:54:28

+0

“start_date bigint”,数据类型DATE或TIMESTAMP出了什么问题?阅读,使用和测试更容易。 – 2014-09-23 13:32:44

回答

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”列除了减慢查询速度外没有其他用途。(它没有意义,它使表格变宽,数据页面上的行数减少;这是多余的,因为另一对列被声明为唯一的;等等)