![](/img/trans.png)
[英]How to store the result of a query in an existing column using PostgreSql
[英]How to store and query version of same document in PostgreSQL?
我在PostgreSQL 9.4中存储文档的版本。 每次用户创建新版本时,它都会插入一行,以便我可以跟踪所有更改。 每行与前一行共享一个reference_id
列。 有些行获得批准,有些仍然作为草稿。 每行还有一个viewable_at
时间。
id | reference_id | approved | viewable_at | created_on | content
1 | 1 | true | 2015-07-15 00:00:00 | 2015-07-13 | Hello
2 | 1 | true | 2015-07-15 11:00:00 | 2015-07-14 | Guten Tag
3 | 1 | false | 2015-07-15 17:00:00 | 2015-07-15 | Grüß Gott
最常见的查询是获取按reference_id分组的行,其中approved
为true
且viewable_at
小于当前时间 。 (在这种情况下,行ID 2将包含在结果中)
到目前为止,这是我提出的最好的查询,不需要我添加其他列:
SELECT DISTINCT ON (reference_id) reference_id, id, approved, viewable_at, content
FROM documents
WHERE approved = true AND viewable_at <= '2015-07-15 13:00:00'
ORDER BY reference_id, created_at DESC`
我有一个关于reference_id的索引和一个关于approved和viewable_at的多列索引。
只有15,000行,它仍然在我的本地机器上平均几百毫秒(140 - 200)。 我怀疑不同的电话或订单可能会减慢速度。
存储此信息的最有效方法是什么,以便SELECT查询性能最高?
EXPLAIN(BUFFERS,ANALYZE)的结果:
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Unique (cost=6668.86..6730.36 rows=144 width=541) (actual time=89.862..99.613 rows=145 loops=1)
Buffers: shared hit=2651, temp read=938 written=938
-> Sort (cost=6668.86..6699.61 rows=12300 width=541) (actual time=89.861..97.796 rows=13184 loops=1)
Sort Key: reference_id, created_at
Sort Method: external merge Disk: 7488kB
Buffers: shared hit=2651, temp read=938 written=938
-> Seq Scan on documents (cost=0.00..2847.80 rows=12300 width=541) (actual time=0.049..40.579 rows=13184 loops=1)
Filter: (approved AND (viewable_at < '2015-07-20 06:46:55.222798'::timestamp without time zone))
Rows Removed by Filter: 2560
Buffers: shared hit=2651
Planning time: 0.218 ms
Execution time: 178.583 ms
(12 rows)
文件使用说明:
文档是手动编辑的,我们还没有每X秒或任何东西自动保存文档,因此音量会相当低。 此时,每个reference_id 平均有7个版本 , 平均只有2个已批准的版本 。 (〜30%)
在最小和最大方面,绝大多数文档将有1或2个版本,并且似乎不太可能有任何文档超过30或40。有一个垃圾收集过程来清理超过一周的未批准版本,所以版本总数应该保持很低。
对于检索和实际使用,我可以在查询上使用限制/偏移,但在我的测试中没有产生巨大的差异。 理想情况下,这是一个填充视图或其他内容的基本查询,以便我可以在这些结果之上进行其他查询,但我不完全确定这会对结果性能产生什么影响并且对建议持开放态度。 我的印象是,如果我能够尽可能简单/快速地获得此存储/查询,那么从这一点开始的所有其他查询都可以得到改进,但可能是我错了,并且每个查询都需要更多的独立思考。
查看您的解释输出,看起来您正在获取documents
表中的大部分内容,因此它可以合理地执行顺序扫描。 您的行数估计是合理的,这里似乎没有任何统计数据问题。
它正在磁盘上进行外部合并排序,因此您可能会通过增加work_mem
中的work_mem
来看到性能的显着提高,例如
SET work_mem = '12MB'
(reference_id ASC, created_at DESC) WHERE (approved)
上的索引可能有用,因为它允许以所需的顺序获取结果。
您还可以尝试将viewable_at
添加到索引中。 我认为它可能必须是最后一栏,但我不确定。 甚至通过附加viewable_at, id, content
并从结果集中省略不必要的approved
列,使其成为覆盖索引。 这可能允许仅索引扫描,但涉及DISTINCT ON
我不确定。
@Craig已经涵盖了大多数选项,可以更快地进行此查询。 会话的更多work_mem
可能是最有效的项目。
以来:
有一个垃圾收集过程来清除超过一周的未批准版本
排除未批准版本的部分索引不会太多。 但是,如果使用索引,仍会排除那些不相关的行。
因为每个reference_id
似乎只有很少的版本:
绝大多数文档将有1或2个版本
您已经拥有DISTINCT ON
的最佳查询技术:
随着越来越多的版本,其他技术将越来越优越:
查询中唯一有点非常规的元素是谓词在viewable_at
,但是你接着使用最新的created_at
行,这就是你的索引实际上是:
(reference_id, viewable_at ASC, created_at DESC) WHERE (approved)
假设所有列都被定义为NOT NULL
。 viewable_at
和created_at
之间的交替排序顺序很重要。 然后,虽然每个reference_id
有这么少的行,但我不希望任何索引有多大用处。 无论如何都必须读取整个表格,顺序扫描的速度一样快。 增加的指数维护成本甚至可能超过其利益。
但是,因为:
理想情况下,这是一个填充视图或其他内容的基本查询,以便我可以在这些结果之上执行其他查询
我还有一个建议:从查询中创建一个MATERIALIZED VIEW
,为您提供给定时间点项目的快照。 如果磁盘空间不是问题而快照可能会重复使用,您甚至可能会收集其中的几个:
CREATE MATERIALIZED VIEW doc_20150715_1300 AS
SELECT DISTINCT ON (reference_id)
reference_id, id, approved, viewable_at, content
FROM documents
WHERE approved -- simpler expression for boolean column
AND viewable_at <= '2015-07-15 13:00:00'
ORDER BY reference_id, created_at DESC;
或者,如果所有其他查询都在同一会话中发生,请使用临时表(在会话结束时自动死亡):
CREATE TEMP TABLE doc_20150715_1300 AS ...;
ANALYZE doc_20150715_1300;
确保在临时表上运行ANALYZE
(如果在创建查询后立即运行查询,也在MV上运行):
无论哪种方式,在支持后续查询的快照上创建一个或多个索引都 可能需要付费。 取决于数据和查询。
请注意,当前版本1.20.0的pgAdmin不显示MV的索引。 这已经被修复 ,等待下一版本发布。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.