我正在运行 PostgresSQL 9.2 并且有一个 12 列的关系,大约有 6,700,000 行。它包含 3D 空间中的节点,每个节点都引用一个用户(创建它的人)。要查询哪个用户创建了多少个节点,我执行以下操作(添加explain analyze
以获取更多信息):
EXPLAIN ANALYZE SELECT user_id, count(user_id) FROM treenode WHERE project_id=1 GROUP BY user_id;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=253668.70..253669.07 rows=37 width=8) (actual time=1747.620..1747.623 rows=38 loops=1)
-> Seq Scan on treenode (cost=0.00..220278.79 rows=6677983 width=8) (actual time=0.019..886.803 rows=6677983 loops=1)
Filter: (project_id = 1)
Total runtime: 1747.653 ms
Run Code Online (Sandbox Code Playgroud)
如您所见,这大约需要 1.7 秒。考虑到数据量,这还算不错,但我想知道这是否可以改进。我尝试在用户列上添加 BTree 索引,但这没有任何帮助。
您有其他建议吗?
为了完整起见,这是完整的表定义及其所有索引(没有外键约束、引用和触发器):
Column | Type | Modifiers
---------------+--------------------------+------------------------------------------------------
id | bigint | not null default nextval('concept_id_seq'::regclass)
user_id | bigint …
Run Code Online (Sandbox Code Playgroud) 除了常规列之外,Postgres 表还有各种可用的系统列。其中之一,xmin
,存储用于创建行的事务 ID。它的数据类型是xid
,一个四字节整数,在某个点环绕(即不一定是唯一的)。该函数txid_current()
反过来返回当前事务 ID,但作为bigint
,因为它“使用“纪元”计数器进行了扩展,因此它不会在安装的生命周期内回绕”(引用手册)。
如果尚未发生交易回绕,则两个值似乎都匹配:
# CREATE TABLE test (label text);
CREATE TABLE
# INSERT INTO test VALUES ('test') RETURNING txid_current();
txid_current
--------------
674500
(1 row)
INSERT 0 1
# SELECT xmin FROM test;
xmin
--------
674500
(1 row)
Run Code Online (Sandbox Code Playgroud)
但我想知道:这两个值总是具有可比性吗?据我了解,txid_current()
将在交易 ID 环绕(最多 2^32 个交易)后继续提供唯一值,xmin
并将从零开始。这意味着两者都在那时开始返回不同的值?
如果这是真的,有没有办法来提取规则xid
一的txid_current()
结果,以便它匹配xmin
的表(例如铸造项目txid_current()
,以整数)?
编辑:更清楚地说明我关心事务 ID 环绕之后会发生什么,这很可能发生在 2^32 事务之前很久。感谢 Daniel Vérité 在评论中指出这一点。
我使用 Postgres 11 并想更改支持大表(约 7.5 亿行)主键的索引。主键是bigint
列id
,我想使用该INCLUDE
术语包含一个额外的列。这需要在没有表重写的情况下发生(即没有新的/更改的列)。
删除当前PRIMARY KEY
约束并不方便,因为很多其他表都引用了目标表。我想可以删除FOREIGN KEY
所有这些表的PRIMARY KEY
约束,然后删除约束,使用新索引重新创建,然后重新创建FOREIGN KEY
约束。有没有更好的方法来做到这一点?
就像另一个问题所示,我在 3D 空间中处理了很多(> 10,000,000)个点条目。这些点定义如下:
CREATE TYPE float3d AS (
x real,
y real,
z real);
Run Code Online (Sandbox Code Playgroud)
如果我没记错的话,需要 3*8 字节 + 8 字节填充(MAXALIGN
是 8)来存储这些点之一。有没有更好的方法来存储这种数据?在前面提到的问题中,有人指出复合类型涉及相当多的开销。
我经常做这样的空间查询:
SELECT t1.id, t1.parent_id, (t1.location).x, (t1.location).y, (t1.location).z,
t1.confidence, t1.radius, t1.skeleton_id, t1.user_id,
t2.id, t2.parent_id, (t2.location).x, (t2.location).y, (t2.location).z,
t2.confidence, t2.radius, t2.skeleton_id, t2.user_id
FROM treenode t1
INNER JOIN treenode t2 ON
( (t1.id = t2.parent_id OR t1.parent_id = t2.id)
OR (t1.parent_id IS NULL AND t1.id = t2.id))
WHERE (t1.LOCATION).z = 41000.0
AND (t1.LOCATION).x > 2822.6
AND (t1.LOCATION).x …
Run Code Online (Sandbox Code Playgroud) 在另一个问题中,我了解到我应该从我的一个表中优化布局以节省空间并获得更好的性能。我这样做了,但最终得到了比以前更大的表,并且性能没有改变。当然我做了一个VACUUM ANALYZE
. 怎么会?
(我看到如果我只索引单列,索引大小不会改变。)
这是我来自的表(我添加了尺寸 + 填充):
Table "public.treenode"
Column | Type | Size | Modifiers
---------------+--------------------------+------+-------------------------------
id | bigint | 8 | not null default nextval( ...
user_id | integer | 4+4 | not null
creation_time | timestamp with time zone | 8 | not null default now()
edition_time | timestamp with time zone | 8 | not null default now()
project_id | integer | 4 | not null
location | real3d | 36 | …
Run Code Online (Sandbox Code Playgroud) 我想通过在现有表名的基础上附加后缀来创建一个新表名。Postgres 9.5 PL/pgSQL 函数获取作为regclass
类型传入的现有表名,并以字符串形式返回新名称。我format()
用来构造新名称,通常会使用%I
占位符。只要传入的表名不匹配任何 PL/pgSQL 关键字,这就会起作用。在这种情况下,无论我选择(%I
或%s
)什么类型的组件,引用都是错误的。
考虑以下函数:
CREATE OR REPLACE FUNCTION make_name(old_name regclass)
RETURNS text
LANGUAGE plpgsql AS
$$
BEGIN
RETURN format('%I_new', old_name);
END;
$$;
Run Code Online (Sandbox Code Playgroud)
进一步假设有两个表:treenode
和overlay
。为这两个函数调用此函数会产生以下新名称:
SELECT make_name('overlay'::regclass);
make_name
-------------------
"""overlay"""_new
(1 row)
SELECT make_name('treenode'::regclass);
make_name
--------------
treenode_new
(1 row)
Run Code Online (Sandbox Code Playgroud)
事实证明overlay
它也是一个 PL/pgSQL 函数(就像format
,但treenode
不是),它似乎改变了引用行为。如果%s
与 一起使用format()
,结果将是"overlay"_new
。当我使用||
运算符时也会发生同样的情况。如果我使用text
作为输入参数类型,一切都按预期工作,但我更喜欢使用regclass
.
有没有办法用不带引号的关键字匹配regclass
表名(例如overlay …
postgresql ×6
datatypes ×3
index ×2
constraint ×1
count ×1
disk-space ×1
group-by ×1
index-tuning ×1
performance ×1
plpgsql ×1
regclass ×1
spatial ×1
syntax ×1
transaction ×1
vacuum ×1