我有一个样本日历,如应用程序,用于存储事件,重复和事件重复规则。这是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_id
和start_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 :(得分: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”除了减慢查询速度外没有其他用途。 (它没有意义;它使表格更宽,因此数据页面上的行数更少;它是多余的,因为另一对列被声明为唯一;等等。)