The*_*off 1 postgresql performance greatest-n-per-group distinct-on postgresql-performance
我有一个users包含字段id和 的表email。id是主键并且email也被索引。
database> \d users
+-----------------------------+-----------------------------+-----------------------------------------------------+
| Column | Type | Modifiers |
|-----------------------------+-----------------------------+-----------------------------------------------------|
| id | integer | not null default nextval('users_id_seq'::regclass) |
| email | character varying | |
+-----------------------------+-----------------------------+-----------------------------------------------------+
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"index_users_on_email" UNIQUE, btree (email)
Run Code Online (Sandbox Code Playgroud)
如果我在子查询中使用子句查询表,distinct on (email)我会得到显着的性能损失。
database> \d users
+-----------------------------+-----------------------------+-----------------------------------------------------+
| Column | Type | Modifiers |
|-----------------------------+-----------------------------+-----------------------------------------------------|
| id | integer | not null default nextval('users_id_seq'::regclass) |
| email | character varying | |
+-----------------------------+-----------------------------+-----------------------------------------------------+
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"index_users_on_email" UNIQUE, btree (email)
Run Code Online (Sandbox Code Playgroud)
将此与其distinct on (id)成本小于前一个查询的千分之一进行比较。
database> explain (analyze, buffers)
select
id
from (
select distinct on (id)
id
from
users
) as t
where id = 123
+-----------------------------------------------------------------------------------------------------------------------------+
| QUERY PLAN |
|-----------------------------------------------------------------------------------------------------------------------------|
| Unique (cost=0.29..8.31 rows=1 width=4) (actual time=0.021..0.022 rows=1 loops=1) |
| Buffers: shared hit=3 |
| -> Index Only Scan using users_pkey on users (cost=0.29..8.31 rows=1 width=4) (actual time=0.020..0.020 rows=1 loops=1) |
| Index Cond: (id = 123) |
| Heap Fetches: 1 |
| Buffers: shared hit=3 |
| Planning Time: 0.090 ms |
| Execution Time: 0.034 ms |
+-----------------------------------------------------------------------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)
为什么是这样?
我遇到的真正问题是我正在尝试创建一个视图来执行distinct on不唯一的索引列,并且性能非常糟糕。
两列id和email都是UNIQUE。但仅id是NOT NULL。(PRIMARY KEY列总是如此。)NULL值不被视为相等,具有约束(或索引)NULL的列中允许有多个值。UNIQUE这是根据标准 SQL 的。看:
但DISTINCT还是DISTINCT ON认为 NULL 值相等。手册:
显然,如果两行至少有一列值不同,则它们被视为不同。在此比较中,空值被视为相等。
大胆强调我的。进一步阅读:
在您的第二个查询中,distinct on (id)是一个逻辑无操作:保证结果与 without 相同DISTINCT ON。由于SELECT上的外部过滤器id = 123,Postgres 可以去除噪音并完成非常便宜的仅索引扫描。
另一方面,在您的第一个查询中,distinct on (email)如果有多个行带有email IS NULL. 然后 Postgres 必须id根据给定的排序顺序选择第一个。由于没有ORDER BY,因此会导致任意选择。SELECT但带有谓词的外部where id = 123可能取决于结果。整个查询在原则上与第一个查询不同 - 并且在设计上被破坏。
除此之外,还有两个“幸运”的发现:
Run Code Online (Sandbox Code Playgroud)Sort Method: external merge Disk: 2304kB
提及“磁盘”表示work_mem不足。看:
Run Code Online (Sandbox Code Playgroud)-> Seq Scan on users (cost=0.00..3494.80 rows=67380
在我的测试中,我总是在这里进行索引扫描。表示索引臃肿或您的设置存在其他问题。
比较没有任何结果。在切换 PK 和 UNIQUE 列的角色之后,我们可以通过比较第一个查询与此查询来学到一些东西:
Sort Method: external merge Disk: 2304kB
Run Code Online (Sandbox Code Playgroud)
或者将第二个查询与此查询进行比较 - 尝试使用 UNIQUE 列而不是 PK 列进行相同的操作:
-> Seq Scan on users (cost=0.00..3494.80 rows=67380
Run Code Online (Sandbox Code Playgroud)
我们了解到 PK 和 UNIQUE 约束对查询计划没有不同的影响。Postgres 不会使用元信息来偷工减料。PK实际上会产生影响GROUP BY。看:
所以这有效:
select email
from (select distinct on (id) email from users) t
where email = 'user123@foo.com';
Run Code Online (Sandbox Code Playgroud)
id但切换和后同样不起作用email。我在小提琴中添加了一些演示:
db<>在这里摆弄
由于不同的原因,这两个查询都是无稽之谈。我不知道他们如何帮助您解决实际问题:
我遇到的真正问题是我正在尝试创建一个在索引列上不同的视图,该索引列不是唯一的,并且性能非常糟糕。
我们需要查看您的真实查询以及您的设置的所有其他相关详细信息。可能有解决方案,但这可能远远超出了 SO 问题的范围。考虑聘请一名顾问。或者考虑以下方法之一来优化性能:
| 归档时间: |
|
| 查看次数: |
2260 次 |
| 最近记录: |