为什么以下联接会显着增加查询时间?

时间:2013-09-23 23:08:20

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

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

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)

这有效地使我的查询的执行时间加倍。我的问题是,我从画面中遗漏了什么?我认为这么小的查找并没有造成查询执行时间的巨大差异。

3 个答案:

答案 0 :(得分:4)

用(推荐)显式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上有外键约束,则可以排除这种情况,这可以保证参照完整性,并在{NOT NULL约束 { {1}}。 如果是这样,在第一步中,简化为:

bi.impressions.os_id

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)

先分组,稍后加入。更多细节:
Aggregate a single column in query with many columns
PostgreSQL - order by an array

答案 1 :(得分:1)

HashAggregate  (cost=868719.08..868719.24 rows=16 width=10)
HashAggregate  (cost=1448560.83..1448564.99 rows=416 width=22)

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

答案 2 :(得分:1)

以下查询可在不增加查询执行时间的情况下解决问题。问题仍然存在,为什么执行时间会随着添加非常简单的连接而显着增加,但它可能是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;