【发布时间】:2019-07-08 11:57:19
【问题描述】:
我有一个非常简单的查询,执行速度非常慢,原因是它在执行 JOIN 之后对视图进行扫描。看到这一点我有点惊讶,因为我希望 Postgres 在加入之前执行过滤,看到其中一个基础表在要过滤的列上有一个 INDEX。
有没有办法我可以以某种方式重新排序查询或提示计划者如何以不同的方式进行查询?
请注意,我确实知道如何通过直接访问基础表来解决问题,但视图隐藏了一些复杂性,最好避免查询。 p>
查询
select * from form where encounter_id= 23728 and type = 'vitals';
解释分析
Subquery Scan on form (cost=0.57..3439.07 rows=1 width=622) (actual time=8.187..8.187 rows=0 loops=1)
Filter: ((form.encounter_id = 23728) AND (form.type = 'vitals'::text))
Rows Removed by Filter: 12000
-> Unique (cost=0.57..3259.07 rows=12000 width=626) (actual time=0.008..7.612 rows=12000 loops=1)
-> Merge Join (cost=0.57..3229.07 rows=12000 width=626) (actual time=0.007..5.485 rows=12000 loops=1)
Merge Cond: (fd.form_id = f.id)
-> Index Scan using _idx_form_details on _form_details fd (cost=0.29..2636.78 rows=12000 width=603) (actual time=0.003..1.918 rows=12000 loops=1)
-> Index Scan using pk_form on _form f (cost=0.29..412.29 rows=12000 width=27) (actual time=0.002..1.214 rows=12000 loops=1)
Planning time: 0.170 ms
Execution time: 8.212 ms
TABLE 和 VIEW 定义
CREATE TABLE _form (
id INT NOT NULL,
encounter_id INT REFERENCES _encounter (id) NOT NULL,
type TEXT NOT NULL,
CONSTRAINT pk_form PRIMARY KEY (id),
FOREIGN KEY (cid) REFERENCES _user_in_role (id)
);
CREATE INDEX encounter_id ON _form (encounter_id, type);
CREATE TABLE _form_details (
id INT NOT NULL,
form_id INT REFERENCES _form (id) NOT NULL,
archived BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT pk_form_details PRIMARY KEY (id),
FOREIGN KEY (cid) REFERENCES _user_in_role (id)
);
CREATE VIEW form AS
SELECT DISTINCT ON (f.id)
f.id,
f.encounter_id,
f.type,
fd.archived,
f.cid
FROM _form f
JOIN _form_details fd
ON (f.id = fd.form_id)
ORDER BY f.id, fd.id DESC;
编辑:
有人发布了一个答案(随后被删除),其中包含一个重要的信息:即使基础表中的encounter_id 列已被索引,VIEW 中的ORDER BY 操作也无法实现其目的。不幸的是,我们无法摆脱ORDER BY,因为DISTINCT ON 需要它才能工作。
【问题讨论】:
-
8 毫秒并没有真正让我觉得“慢” - 你需要多快?
-
@a_horse_with_no_name 一切都是相对的。这是 O(N) 的性能,这意味着它与表单的数量呈线性关系。这只是使用我为某个
encounter_id(共 12000 个)创建的大约 300 个表单的测试数据集。我预计在生产中会有很多这些数字,以及每秒运行数百次的查询。 注意纳秒(查找)。 -
@a_horse_with_no_name 嗯,8ms 已经太慢了。我们使用的是 10.6 版。
-
您尝试在
(type, encounter_id)上建立索引了吗?您是否总是查询相同的type值?然后使用where type = 'vitals'的过滤索引可能会有所帮助。如果您只有几个不同的type值,为每种类型创建一个过滤索引也可能会有所帮助。work_mem的价值是多少?如果增加它有帮助吗?
标签: sql postgresql query-performance sql-view lateral-join