我的查询看起来像这样
SELECT DISTINCT
COALESCE(fa.id, fb.id) AS id,
COALESCE(fa.d_id, fb.d_id) AS d_id,
COALESCE(fa.name, fb.name) AS name,
COALESCE(fa.disabled, fb.disabled) AS disabled,
COALESCE(fa.deleted, fb.deleted) AS deleted
FROM (
SELECT * from table WHERE name LIKE '%'
AND d_id IS NULL AND deleted = false
) fa
FULL JOIN (
SELECT * from table WHERE name LIKE '%'
AND d_id = 1 AND deleted = false
) fb ON fa.name = fb.name
ORDER BY name;
其中id
是表的主键,name
是实际值。 d_id
是用户的ID。
基本上,该表有一个巨大的名称列表(大约400k +),如果它没有d_id
,则表示它是由系统自动生成的。如果它有d_id
,则表示它是用户生成的。
查询应该返回的是整个默认系统名称列表加上某个用户添加的名称(在这种情况下,用户使用d_id为1生成的所有名称)。这就是它为自己执行完全连接的原因。
我的问题是运行查询需要太长时间(在我的本地psql shell上大约30000~40000ms,在live上运行大约15000)。我运行了EXPLAIN ANALYZE并得到了这个
Unique (cost=8240.78..8272.13 rows=2090 width=42) (actual time=27591.662..28742.062 rows=418018 loops=1)
-> Sort (cost=8240.78..8246.01 rows=2090 width=42) (actual time=27591.659..28504.606 rows=418018 loops=1)
Sort Key: (COALESCE(table.name, table_1.name)), (COALESCE(table.id, table_1.id)), (COALESCE(table.d_id, table_1.d_id)), (COALESCE(table.disabled, table_1.disabled)), (COALESCE(table.deleted, table_1.deleted))
Sort Method: external merge Disk: 13680kB
-> Hash Full Join (cost=8.45..8125.53 rows=2090 width=42) (actual time=11.037..1479.053 rows=418018 loops=1)
Hash Cond: (table.name = table_1.name)
-> Seq Scan on table (cost=0.00..8109.23 rows=2090 width=27) (actual time=0.048..799.822 rows=418018 loops=1)
Filter: ((d_id IS NULL) AND (NOT deleted) AND (name ~~ '%'::citext))
-> Hash (cost=8.44..8.44 rows=1 width=27) (actual time=10.970..10.970 rows=0 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 8kB
-> Index Scan using table__d_id__name__idx on table table_1 (cost=0.42..8.44 rows=1 width=27) (actual time=10.970..10.970 rows=0 loops=1)
Index Cond: (d_id = 1)
Filter: ((NOT deleted) AND (name ~~ '%'::citext))
虽然我无法完全理解它,但我可以说,为什么花费太长时间的大多数原因都在于排序(ORDER BY
)函数。
我的索引如下:
Indexes:
"table_pkey" PRIMARY KEY, btree (id)
"table__d_id__name__idx" UNIQUE, btree (d_id, name)
"table__name__idx" gist (name gist_trgm_ops)
"table__id__idx" btree (id)
我尝试过使用不同的索引,重构查询并使用代码,但它仍然需要一段时间。我已经尝试删除除主键索引之外的所有索引,并且查询以某种方式加速到~23000ms。
此外,在应用程序中,用户可以选择一个字母,该字母将返回以该字母开头的所有结果,查询看起来像WHERE name LIKE 'a%'
。尽管还有成千上万的结果,但指定一个起始字母会大大减少加载时间到1000-2000ms。
我的目标是在5000到10000毫秒之间加载查询。任何帮助或建议将不胜感激!
答案 0 :(得分:2)
我认为您可以使用or
代替full join
。 distinct on (name)
仅选择唯一名称,order by name, d_id
在用户名之前选择系统名称。
select distinct on (name)
id, d_id, name, disabled, deleted
from table
where deleted = false
and (
d_id is null
or d_id = 1
)
order by name, d_id
答案 1 :(得分:0)
问题很大。
如果你没有使用DISTINCT
,你可以摆脱排序
我看到在你的情况下,行无论如何都是不同的,因为在应用Unique
之前和之后有418018行。
仔细考虑是否真的可以在您的情况下发生重复,或者如果您可以取消DISTINCT
并以这种方式解决问题。
如果您需要DISTINCT
,则至少应为此查询增加work_mem
,以便排序可以在内存中发生而不是溢出到磁盘。这将大大提高性能。