我对 Postgres 的经验几乎为零,我的电脑是 2010 年的。所以不是最快的,但仍然很强大。
我查看了推荐的答案,但我(还)没有得到它们 - 我想了解我在做什么。
我有一个 Postgres 数据库,其中包含
names
和 number_of_occurrences
列(加上一些其他每个名称都有唯一值的列)。数据库有大约3亿行(将增加到大约60亿行),大多数名称只出现一次,而有些可能出现数千甚至数百万次。
到目前为止,数据库只包含名称,现在我的工作是填写
number_of_occurrences
列。
最有效的方法是什么?
通常,我想我应该先在
DISTINCT
上使用 names
然后对每个不同的值运行计数,然后将其保存到新表中,最后丢弃旧表。
但是,这是不可能的,因为表中的每一行还有其他唯一值,因此必须将计数放回原始表中。
我实际上是想帮助我妻子解决工作中的问题。她研究的 X 染色体长约 1.4 亿个“字母”。问题是要找到满足某些特性(例如 GC 含量、Tm 等)、彼此相距一定距离(例如 100-150 个字母)并且在X 染色体,但不会出现在 Y 染色体上(理想情况下也不会出现在任何其他染色体上)。
第一步是创建引物(所有 1.4 亿 - X 染色体的 20,因为引物来自位置 1-20、2-21、3-22 等)并计算稍后所需的相关值(例如GC 含量,具有生物学背景的任何人的 Tm)。
表的主键是 id,一个自动生成的 UUID(所有其他表都相同)。
还没有创建索引——据我所知,在添加行的同时保持索引更新是昂贵的,而且创建完整的表然后索引它要快得多。
Schema 和其他表与问题无关,只会让头脑混乱。
我认为
number_of_occurrences
应该在创建表之后完成,但是有了使 number_of_occurrences
成为外键的想法,我实际上可以在每次添加行时设置或增加值,所以问题就消失了。
在
UPDATE
的子查询中使用普通聚合并返回names
。
假设新列已经存在:
UPDATE tbl t
SET number_of_occurrences = ct.ct
FROM (
SELECT names, count(*) AS ct
FROM tbl
GROUP BY 1
) ct
WHERE t.names = ct.names;
此操作不需要任何索引,因为无论如何都会读取和更新所有行。你以后创建索引是对的。
实际上,如果你可以自由地这样做,那么只创建一个新表可能更便宜。假设新列还不存在:
CREATE TABLE tl2 AS
SELECT *, count(*) OVER (PARTITION BY name) AS number_of_occurrences
FROM tbl
ORDER BY names; -- optional, but possibly beneficial.
DROP TABLE tbl;
这次使用
count()
作为窗口函数,它不聚合行,而是保留每个输入行。
旁白:“名字”是一个狡猾的名字。