带窗口函数的简单SQL查询优化

Posted

技术标签:

【中文标题】带窗口函数的简单SQL查询优化【英文标题】:Optimization of simple SQL query with window function 【发布时间】:2014-09-17 16:45:48 【问题描述】:

我有一个 PostgreSQL 表构造为

  a  |  b  |  c
-----+-----+-----
   3 |   2 |   1
   1 |   5 |   1
   8 |   4 |   1
   2 |   5 |   1
   4 |   4 |   2
   2 |   5 |   2
   9 |   3 |   2
   3 |   5 |   3
   2 |   5 |   3
   4 |   4 |   3
   5 |   6 |   3
   9 |   7 |   3

我想为c 的每个值计算a 的平均值,其中b 低于给定值——例如b 的平均值。

这是我的查询:

SELECT avg(a) FROM mytable t WHERE b<(SELECT avg(b) FROM mytable WHERE c=t.c) GROUP BY c;

我实际上有两个问题,但我相信它们都属于这个问题(第一个问题实际上允许我更新标题):

    这种查询是否有特定的名称或表达式(我的意思是,主查询中的子选择和重新集成操作,或类似的东西)?我什至找不到如何在线搜索解决方案... => 好的,窗口函数。

    这个查询很慢,如何优化?我使用的是 9.3.5,b 已经按数字顺序排序。

谢谢。

更新:编辑 user17130 的答案 was rejected,但这个答案不会从头开始工作,所以这里是工作代码:

explain select 
   avg(a) 
   from  
   (
       select  
        avg(b) over (partition by c) as b_avg,
        a,
        b,
        c 
        from mytable
    ) as t 
    where b<b_avg 
    group by c;
                                     QUERY PLAN                                     
------------------------------------------------------------------------------------
 GroupAggregate  (cost=135.34..202.46 rows=67 width=8)
   Subquery Scan on t  (cost=135.34..198.39 rows=647 width=8)
     Filter: ((t.b)::numeric < t.b_avg)
     ->  WindowAgg  (cost=135.34..169.29 rows=1940 width=12)
           ->  Sort  (cost=135.34..140.19 rows=1940 width=12)
                 Sort Key: mytable.c
                 ->  Seq Scan on mytable  (cost=0.00..29.40 rows=1940 width=12)

【问题讨论】:

查看窗口函数和 HAVING 子句。 搜索正确的术语确实要容易得多,谢谢!我会相应地更新问题。 发布解释分析(首选使用 explain.depesz.com)。 “非常慢”有点太模糊了。 【参考方案1】:

我认为这就是你的意思。这只有一个使用窗口函数的表扫描。正如您所看到的,您在下面的查询估计比这个要花费更多的运行时间。如果没有任何选择性条件,您将至少扫描一次表。

 explain select                                                                     
    a_avg
    from
    (
        select
         avg(a) over (partition by c) as a_avg  
        ,avg(b) over (partition by c) as b_avg
        ,c
        ,b
        from mytable
    ) as t
    where b < b_avg
;
                                  QUERY PLAN                                  
──────────────────────────────────────────────────────────────────────────────
 Subquery Scan on t  (cost=135.34..203.24 rows=647 width=32)
   Filter: ((t.b)::numeric < t.b_avg)
   ->  WindowAgg  (cost=135.34..174.14 rows=1940 width=12)
         ->  Sort  (cost=135.34..140.19 rows=1940 width=12)
               Sort Key: mytable.c
               ->  Seq Scan on mytable  (cost=0.00..29.40 rows=1940 width=12)
 Planning time: 0.128 ms
(7 rows)

...

crow@test=# explain SELECT avg(a) FROM mytable t WHERE b<(SELECT avg(b) FROM mytable WHERE c=t.c) GROUP BY c;
                                 QUERY PLAN                                  
─────────────────────────────────────────────────────────────────────────────
 HashAggregate  (cost=66560.08..66560.92 rows=67 width=8)
   Group Key: t.c
   ->  Seq Scan on mytable t  (cost=0.00..66556.85 rows=647 width=8)
         Filter: ((b)::numeric < (SubPlan 1))
         SubPlan 1
           ->  Aggregate  (cost=34.28..34.29 rows=1 width=4)
                 ->  Seq Scan on mytable  (cost=0.00..34.25 rows=10 width=4)
                       Filter: (c = t.c)
 Planning time: 0.191 ms
(9 rows)

【讨论】:

您的代码并没有完全按照我的预期从头开始,但是您的回答非常有帮助,所以我用更正的代码对其进行了编辑。 编辑被拒绝,查看工作代码的更新问题。

以上是关于带窗口函数的简单SQL查询优化的主要内容,如果未能解决你的问题,请参考以下文章

Greenplum优化--SQL调优篇

数据库的简单优化

SQL Server 使用分区函数实现查询优化

SQL 逻辑优化 case when 转为 union all

万字长文带你走进MySql优化(系统层面优化软件层面优化SQL层面优化)

使用函数优化 SQL 查询