2014-09-23 42 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」列除了減慢查詢速度外沒有其他用途。(它沒有意義,它使表格變寬,數據頁面上的行數減少;這是多餘的,因爲另一對列被聲明爲唯一的;等等)