Sql 在复杂查询中计算行的方式有什么问题?
我有一个数据库,有几个表,每个表都有几百万行,表都有索引。我需要计算表中的行数,但只计算外键字段指向另一个表的子集的行数。 以下是查询:Sql 在复杂查询中计算行的方式有什么问题?,sql,database,postgresql,count,postgresql-performance,Sql,Database,Postgresql,Count,Postgresql Performance,我有一个数据库,有几个表,每个表都有几百万行,表都有索引。我需要计算表中的行数,但只计算外键字段指向另一个表的子集的行数。 以下是查询: WITH filtered_title AS (SELECT top.id FROM title top WHERE ( top.production_year >= 1982 AND top.production_year <= 1984
WITH filtered_title
AS (SELECT top.id
FROM title top
WHERE ( top.production_year >= 1982
AND top.production_year <= 1984
AND top.kind_id IN( 1, 2 )
OR EXISTS(SELECT 1
FROM title sub
WHERE sub.episode_of_id = top.id
AND sub.production_year >= 1982
AND sub.production_year <= 1984
AND sub.kind_id IN( 1, 2 )) ))
SELECT Count(*)
FROM cast_info
WHERE EXISTS(SELECT 1
FROM filtered_title
WHERE cast_info.movie_id = filtered_title.id)
AND cast_info.role_id IN( 3, 8 )
现在回答我的问题。我做错了什么?我该如何修复它
我尝试了同一查询的几个变体:独占联接、联接/存在。一方面,这项工作似乎需要最少的时间来完成10倍的速度,但平均仍需要60秒。另一方面,与我的第一个查询在第二次运行时需要4-6秒不同,它总是需要60秒
WITH filtered_title
AS (SELECT top.id
FROM title top
WHERE top.production_year >= 1982
AND top.production_year <= 1984
AND top.kind_id IN( 1, 2 )
OR EXISTS(SELECT 1
FROM title sub
WHERE sub.episode_of_id = top.id
AND sub.production_year >= 1982
AND sub.production_year <= 1984
AND sub.kind_id IN( 1, 2 )))
SELECT Count(*)
FROM cast_info
join filtered_title
ON cast_info.movie_id = filtered_title.id
WHERE cast_info.role_id IN( 3, 8 )
免责声明:有太多的因素在起作用,给一个决定性的答案。有几个表的信息,每个表都有几百万行,表有索引,但不能将其剪切。它取决于基数、表定义、数据类型、使用模式以及可能最重要的索引。当然,还有数据库服务器的正确基本配置。所有这些都超出了一个问题的范围。从标记中的链接开始。或者请一位专业人士
我将在您的查询计划中为我介绍最突出的细节:
标题的顺序扫描?
->标题子标题上的顺序扫描成本=0.00..90471.23行=11657宽度=4实际时间=0.071..730.311行=16250循环=1
过滤器:生产年份>=1982和生产年份免责声明:有太多因素在起作用,无法给出结论性答案。有几个表的信息,每个表都有几百万行,表有索引,但不能将其剪切。它取决于基数、表定义、数据类型、使用模式以及可能最重要的索引。当然,还有数据库服务器的正确基本配置。所有这些都超出了一个问题的范围。从标记中的链接开始。或者请一位专业人士
我将在您的查询计划中为我介绍最突出的细节:
标题的顺序扫描?
->标题子标题上的顺序扫描成本=0.00..90471.23行=11657宽度=4实际时间=0.071..730.311行=16250循环=1
筛选:production\u year>=1982和production\u year编辑您的问题,并在慢速查询上粘贴运行explain Analysis的输出。您的查询中有太多相关子计划。重写它以便使用普通联接(带有group by和having子句)获得结果,它的性能应该会更好。尝试使用联接重写它,它的工作速度确实快了6倍,但对于查询来说仍然是90秒,这仍然是。。。令人失望:编辑您的问题,并在慢速查询上粘贴运行解释分析的输出。您的查询中有太多相关子计划。重写它以便使用普通联接(带有group by和having子句)获得结果,它的性能应该会更好。尝试使用联接重写它,它的工作速度确实快了6倍,但对于查询来说仍然是90秒,这仍然是。。。令人失望:谢谢你的回答。我只有对数据库的只读访问权限,因此我不能更改索引或使用临时表。@Sanko:您只需要创建临时表。另外,在我花时间尝试帮助之前,你应该先提到这一点和其他基本信息。不管怎样,我建议的查询仍然会有帮助——程度要小一些。对不起。是的,你的回答很有帮助。谢谢。@Sanko:我想知道建议的替代方案在比较中的表现如何。如果你不介意的话,请在这里留言。我一分钟前测试过。您的查询很好,大约20秒,而EXISTS的查询速度大约慢5%。这些是我可以接受的结果。谢谢你的回答。我只有对数据库的只读访问权限,因此我不能更改索引或使用临时表。@Sanko:您只需要创建临时表。另外,在我花时间尝试帮助之前,你应该先提到这一点和其他基本信息。不管怎样,我建议的查询仍然会有帮助——程度要小一些。对不起。是的,你的回答很有帮助。谢谢。@Sanko:我想知道建议的替代方案在比较中的表现如何。如果你不介意的话,请在这里留言。我一分钟前测试过。您的查询很好,大约20秒,而EXISTS的查询速度大约慢5%。这些是我可以接受的结果。
WITH filtered_title
AS (SELECT top.id
FROM title top
WHERE top.production_year >= 1982
AND top.production_year <= 1984
AND top.kind_id IN( 1, 2 )
OR EXISTS(SELECT 1
FROM title sub
WHERE sub.episode_of_id = top.id
AND sub.production_year >= 1982
AND sub.production_year <= 1984
AND sub.kind_id IN( 1, 2 )))
SELECT Count(*)
FROM cast_info
join filtered_title
ON cast_info.movie_id = filtered_title.id
WHERE cast_info.role_id IN( 3, 8 )
CREATE INDEX title_foo_idx ON title (kind_id, production_year, id, episode_of_id)
WITH t_base AS (
SELECT id, episode_of_id
FROM title
WHERE kind_id BETWEEN 1 AND 2
AND production_year BETWEEN 1982 AND 1984
)
, t_all AS (
SELECT id FROM t_base
UNION -- not UNION ALL (!)
SELECT id
FROM (SELECT DISTINCT episode_of_id AS id FROM t_base) x
JOIN title t USING (id)
)
SELECT count(*) AS ct
FROM cast_info c
JOIN t_all t ON t.id = c.movie_id
WHERE c.role_id IN (3, 8);
SELECT id
FROM title t
WHERE EXISTS (SELECT 1 FROM t_base WHERE t_base.episode_of_id = t.id)
CREATE TEMP TABLE t_tmp AS
WITH t_base AS (
SELECT id, episode_of_id
FROM title
WHERE kind_id BETWEEN 1 AND 2
AND production_year BETWEEN 1982 AND 1984
)
SELECT id FROM t_base
UNION
SELECT id FROM title t
WHERE EXISTS (SELECT 1 FROM t_base WHERE t_base.episode_of_id = t.id);
ANALYZE t_tmp; -- !
CREATE UNIQUE INDEX ON t_tmp (id); -- ! (unique is optional)
SELECT count(*) AS ct
FROM cast_info c
JOIN t_tmp t ON t.id = c.movie_id
WHERE c.role_id IN (3, 8);
-- More queries using t_tmp