优化连接->;PostgreSQL中的分组查询:所有索引都已存在
至少有两个类似的问题(但不完全相同)。在这些问题中,查询性能的问题在于缺少索引或谓词过多 但我的情况简单明了:3个表,每个表引用另一个表。每个被引用的表行上都有b树索引。以下是表格:优化连接->;PostgreSQL中的分组查询:所有索引都已存在,sql,postgresql,join,group-by,query-optimization,Sql,Postgresql,Join,Group By,Query Optimization,至少有两个类似的问题(但不完全相同)。在这些问题中,查询性能的问题在于缺少索引或谓词过多 但我的情况简单明了:3个表,每个表引用另一个表。每个被引用的表行上都有b树索引。以下是表格: CREATE TABLE region( id serial PRIMARY KEY, title VARCHAR (50) NOT NULL ); CREATE TABLE unit( id serial PRIMARY KEY, region_id INT NOT NULL REFER
CREATE TABLE region(
id serial PRIMARY KEY,
title VARCHAR (50) NOT NULL
);
CREATE TABLE unit(
id serial PRIMARY KEY,
region_id INT NOT NULL REFERENCES region(id)
);
CREATE TABLE unit_usage(
id serial PRIMARY KEY,
title VARCHAR (50) NOT NULL,
unit_id INT NOT NULL REFERENCES unit(id)
);
CREATE INDEX ON unit ((region_id));
CREATE INDEX ON unit_usage ((unit_id));
CREATE INDEX ON unit_usage ((title));
unit_usage表中有30000000+行,unit表中有50000+行,region表中有65000+行。
我想要的是查询每个单位使用的区域的计数。大概是这样的:
WITH x AS
(
select u.region_id as region_id, t.title as title
from unit_usage t join unit u
on t.unit_id = u.id
)
SELECT title, count(region_id) as found_in_regions
FROM x GROUP BY title;
在这里
此查询大约运行5分钟。太多了,我的极限是10秒左右。
我所尝试的:
重新塑造查询,如:
select u.region_id, t.title, count(t.id)
from unit_usage t join unit u
on t.unit_id = u.id group by u.region_id, t.title;
执行时间相同
- 设置enable_hashjoin=off;我已经去掉了Hash连接和Seq扫描,但这不会影响执行时间
- 我会首先尝试正确的逻辑。如果您想计算不同区域的数量,那么我希望:
我想要的是查询每单位使用的区域数
这不会加快查询速度。但至少它应该返回正确的结果。如果是这样,那么您可以开始考虑如何使其正确。此查询的结果与第二个查询的结果相同。它可以更快,因为要连接的行更少:
with uu as (
select u.unit_id, u.title, count(*) cnt
from unit_usage u
group by u.unit_id, u.title
)
select u.region_id, uu.title, sum(cnt)
from uu
inner join unit u
on uu.unit_id = u.id
group by u.region_id, uu.title
此索引可能有助于此查询(最好使用和不使用索引进行测试):
对不起,我在写这个示例时出错了。我修复了topic/fiddle中的SQL。SQL(已修复)是正确的,它提供了所需的结果:与unit_用法中的每个单独标题相关的区域。
count(distinct u.region_id)
将始终等于1,因为您按主标题进行分组key@Sergey94 . . . 我不明白你的评论u.region\u id
与unit\u usage
上的主键无关。请按uu.id查看您的分组。您正在按单元使用的主键进行分组。这就是为什么计数总是等于1。@Sergey94。谢谢。是OLAP还是OLTP?这是一个OLAP部分。查询不可能扫描+3亿行并在10秒内执行。您应该使用聚合创建其他表,或者使用使用计数将聚合列添加到unit表中。并在ETL期间计算此聚合。然后,您的查询将变成一个表的简单完全扫描。看起来是这样,但至少我很高兴有一半的时间。您的两个查询似乎并不相等,因为在第一个查询中,您按标题分组,而在第二个查询中,您按标题和区域id分组。在group by中包含标题是否如此重要?或者你可以按区域id分组?
with uu as (
select u.unit_id, u.title, count(*) cnt
from unit_usage u
group by u.unit_id, u.title
)
select u.region_id, uu.title, sum(cnt)
from uu
inner join unit u
on uu.unit_id = u.id
group by u.region_id, uu.title
create index unit_usage_ix on unit_usage(unit_id, title);