【问题标题】:Why does the following join increase the query time significantly?为什么下面的连接会显着增加查询时间?
【发布时间】:2013-09-29 00:07:09
【问题描述】:

我在这里有一个星型模式,我正在查询事实表并想加入一个非常小的维度表。我无法真正解释以下内容:

EXPLAIN ANALYZE SELECT 
  COUNT(impression_id), imp.os_id 
  FROM bi.impressions imp 
  GROUP BY imp.os_id;

                                                                  QUERY PLAN
    --------------------------------------------------------------------------------------------------------------------------------------
     HashAggregate  (cost=868719.08..868719.24 rows=16 width=10) (actual time=12559.462..12559.466 rows=26 loops=1)
       ->  Seq Scan on impressions imp  (cost=0.00..690306.72 rows=35682472 width=10) (actual time=0.009..3030.093 rows=35682474 loops=1)
     Total runtime: 12559.523 ms
    (3 rows)

这需要大约 12600 毫秒,但当然没有连接数据,所以我无法将 imp.os_id “解析”为有意义的东西,所以我添加了一个连接:

EXPLAIN ANALYZE SELECT 
  COUNT(impression_id), imp.os_id, os.os_desc 
  FROM  bi.impressions imp, bi.os_desc os 
  WHERE imp.os_id=os.os_id 
  GROUP BY imp.os_id, os.os_desc;
                                                                     QUERY PLAN
    --------------------------------------------------------------------------------------------------------------------------------------------
     HashAggregate  (cost=1448560.83..1448564.99 rows=416 width=22) (actual time=25565.124..25565.127 rows=26 loops=1)
       ->  Hash Join  (cost=1.58..1180942.29 rows=35682472 width=22) (actual time=0.046..15157.684 rows=35682474 loops=1)
             Hash Cond: (imp.os_id = os.os_id)
             ->  Seq Scan on impressions imp  (cost=0.00..690306.72 rows=35682472 width=10) (actual time=0.007..3705.647 rows=35682474 loops=1)
             ->  Hash  (cost=1.26..1.26 rows=26 width=14) (actual time=0.028..0.028 rows=26 loops=1)
                   Buckets: 1024  Batches: 1  Memory Usage: 2kB
                   ->  Seq Scan on os_desc os  (cost=0.00..1.26 rows=26 width=14) (actual time=0.003..0.010 rows=26 loops=1)
     Total runtime: 25565.199 ms
    (8 rows)

这有效地使我的查询的执行时间加倍。我的问题是,我从图片中遗漏了什么?我认为如此小的查找不会导致查询执行时间的巨大差异。

【问题讨论】:

  • 你在impressions.os_idos.os_id 上都有索引吗?
  • 索引可能会产生位掩码索引扫描(但是,如果没有足够选择性的 WHERE 子句,则无论如何都需要所有行,假设 Impression_id 不在任何索引中)
  • 是的,两者都有索引(btree (os_id))

标签: sql postgresql join aggregate-functions postgresql-performance


【解决方案1】:

用(推荐的)显式 ANSI JOIN 语法重写:

SELECT COUNT(impression_id), imp.os_id, os.os_desc 
FROM   bi.impressions imp
JOIN   bi.os_desc os ON os.os_id = imp.os_id
GROUP  BY imp.os_id, os.os_desc;

首先,您的第二个查询可能是错误的,如果在os_desc 中找到的每行展示次数都多于或少于一个匹配项。
如果您在os_id 上设置了外键约束,以保证引用完整性,并且在bi.impressions.os_id 上设置NOT NULL 约束,则可以排除这种情况。如果是这样,第一步,简化为:

SELECT COUNT(*) AS ct, imp.os_id, os.os_desc 
FROM   bi.impressions imp
JOIN   bi.os_desc     os USING (os_id)
GROUP  BY imp.os_id, os.os_desc;

count(*)count(column) 快,如果列是 NOT NULL,则在此处等效。并为计数添加一个列别名。

更快,但:

SELECT os_id, os.os_desc, sub.ct
FROM  (
   SELECT os_id, COUNT(*) AS ct
   FROM   bi.impressions
   GROUP  BY 1
   ) sub
JOIN   bi.os_desc os USING (os_id)

先聚合,后加入。更多内容:

【讨论】:

  • 感谢 Erwin,我正在对这些内容进行解释分析,以了解性能影响,同时阅读您链接的文档。
  • 欧文,您的查询胜出,谢谢!还要感谢您提供的文档。非常感谢。
【解决方案2】:
HashAggregate  (cost=868719.08..868719.24 rows=16 width=10)
HashAggregate  (cost=1448560.83..1448564.99 rows=416 width=22)

嗯,从 10 到 22 的宽度是翻倍的。也许您应该在分组之后而不是之前加入?

【讨论】:

  • 嘿大卫,我该怎么做?
【解决方案3】:

以下查询在不增加查询执行时间的情况下解决了问题。问题仍然存在,为什么添加一个非常简单的连接会显着增加执行时间,但这可能是 Postgres 特有的问题,在该领域具有丰富经验的人最终可能会回答。

WITH 
  OSES AS (SELECT os_id,os_desc from bi.os_desc) 
SELECT 
  COUNT(impression_id) as imp_count, 
  os_desc FROM bi.impressions imp, 
  OSES os 
WHERE 
  os.os_id=imp.os_id 
GROUP BY os_desc 
ORDER BY imp_count;

【讨论】:

  • 做工作需要时间。做更多的工作需要更多的时间。一个简单的连接仍然是必须完成的工作。通过按两件事而不是一件事进行分组,您会对其施加更多的工作。顺便说一句,上面的查询可以在没有 WITH 的情况下更好地编写,只需将 bi.os_desc 直接合并到查询中即可。提高速度的关键不是 with,它是从 GROUP BY 中删除一个不必要的列。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-01
  • 2021-11-22
相关资源
最近更新 更多