【问题标题】:Postgres query left join take too timePostgres 查询左连接耗时太长
【发布时间】:2020-09-01 07:20:35
【问题描述】:

我对这个查询有疑问。它进入循环,我的意思是 15 分钟后查询未完成 但是如果删除其中一个左连接有效 我哪里错了?

Select distinct a.sito,
Count(distinct a.id_us) as us,
Count (distinct b.id_invmat) as materiali,
Count (distinct c.id_struttura) as Struttura,
Count(distinct d.id_tafonomia) as tafonomia
From us_table as a
Left join invetario_materiali as b on a.sito=b.sito
Left join struttura_table as c on a.sito=c.sito
Left join tafonomia_table as d on a.sito=d.sito
Group by a.sito
Order by us

谢谢 E

【问题讨论】:

  • 这里不需要做 SELECT DISTINCT,因为 GROUP BY 不返回重复项。
  • 请在代码问题中给出minimal reproducible example--包括剪切、粘贴和可运行的最小代码和以代码形式给出的最小代表性数据。对于包含 DBMS 和 DDL 的 SQL,包括约束、索引和表格初始化。对于包括 EXPLAIN 结果和统计信息的 SQL 性能。请研究和总结。对于包括优化/性能基础的 SQL——立即导致索引、计划、统计和 SARGability。 Tips for asking a good SQL question 在您学习并应用了这些基础知识后,请重新优化。 How to Ask
  • JOIN 和聚合需要格外小心。确保不要多次计算相同的项目。我会说,如果有疑问 - 在加入之前聚合。
  • @jarlh 谢谢你是对的。直到限制 128 运行良好,我的意思是在一秒钟内查询响应。在该 utilll 限制 140 之后,它需要 22-24 秒。从限制 150 我有一个错误:设备上没有剩余空间
  • 您可以尝试关联子查询而不是左连接。

标签: sql count left-join postgresql-11


【解决方案1】:

在这种情况下,相关子查询可能是最简单的方法:

select s.sito,
      (select count(*) from invetario_materiali m where s.sito = m.sito) as materiali,
      (select count(*) from struttura_tablest where s.sito = st.sito) as Struttura,
      (select count(*) from tafonomia_table t where s.sito = t.sito) as tafonomia
from (select sito, count(*) as us
      from us_table
      group by sito
     ) s
order by us;

这应该比您的版本快得多,原因有两个。首先,它避免了外部聚合。其次,它避免了表格之间的笛卡尔积。

您可以通过在sito 上的每个辅助表上创建索引来加快速度。

【讨论】:

  • 非常感谢,它运行得非常快!但是对于我的结果,我在 count(distinct..) 中更改了 count(*)
【解决方案2】:

假设id_usid_invmatid_strutturaid_tafonomia都是PRIMARY KEY CLUSTERED

您应该在连接列上添加索引:

CREATE INDEX IX_SITO ON us_table ( sito ASC) ;
CREATE INDEX IX_SITO ON invetario_materiali ( sito ASC) ;
CREATE INDEX IX_SITO ON struttura_table ( sito ASC) ;
CREATE INDEX IX_SITO ON tafonomia_table ( sito ASC) ;

您可以通过这种方式降低复杂性:

with
_us_table as (
    select sito, count(distinct a.id_us) us
    from us_table a
    group by sito
),
_invetario_materiali as (
    select sito, count(distinct b.id_invmat) materiali
    from invetario_materiali b
    group by sito
),
_struttura_table as (
    select sito, count(distinct c.id_struttura) Struttura
    from struttura_table c
    group by sito
),
_tafonomia_table as (
    select sito, count(distinct d.id_tafonomia) tafonomia
    from tafonomia_table d
    group by sito
)
Select a.sito, a.us, b.materiali, c.Struttura, d.tafonomia
From _us_table as a
Left join _invetario_materiali as b on a.sito=b.sito
Left join _struttura_table as c on a.sito=c.sito
Left join _tafonomia_table as d on a.sito=d.sito
Order by a.us;

应该更快

【讨论】:

  • 。 .我同意这应该更快。不过,CTE 中不应需要 count(distinct)s。
【解决方案3】:

不幸的是,COUNT(DISTINCT ...) 在使用索引时很难改进。但是,我们至少可以尝试添加涵盖查询中所有连接的索引:

CREATE INDEX inv_mat_idx ON invetario_materiali (sito, id_invmat);
CREATE INDEX strut_tbl_idx ON struttura_table (sito, id_struttura);
CREATE INDEX taf_tbl_idx ON tafonomia_table (sito, id_tafonomia);

请注意,上述索引只会帮助连接,不会影响 sito 的聚合步骤和每组的不同计数。正如@jarlh 在 cmets 中指出的那样,SELECT DISTINCT 是多余的,因为您使用的是 GROUP BY,所以只需简单的 SELECT

【讨论】:

  • 嗨,是的,选择 distinct 是多余的,我已经创建了索引。现在,如果我删除 distinct 它工作得很好,直到限制 128,限制 129 它需要 22 秒,限制 140 24 秒后进入循环。你知道为什么吗?
  • 如果您已经优化了查询,但仍然太慢,那么您可能只需要等待它完成。
猜你喜欢
  • 1970-01-01
  • 2015-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-28
  • 2011-02-07
相关资源
最近更新 更多