代码改变世界

PostgreSQL 高级SQL(二) filter子句

  abce  阅读(5999)  评论(1编辑  收藏  举报

本文是转载,原文地址是:https://www.jianshu.com/p/aad5b7265674

 

本章所用到案例数据来自于上一章节,如果有想使用该数据的读者可以查看上一章节。

这一章节我们想要了解的是PG聚合操作中使用到的filter子句,这个filter子句是ANSI SQL标准中的关键字,并不是PG的专用SQL关键字。如果我们想了解中国、美国、日本、法国、德国、加拿大从1960~2018年中每隔十年的GDP总值情况,我们可能会写出着这样的SQL,

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT
    country_name,
    SUM ( CASE WHEN YEAR >= 1960 AND YEAR < 1970 THEN gdp ELSE NULL END ) AS "1960~1969",
    SUM ( CASE WHEN YEAR >= 1970 AND YEAR < 1980 THEN gdp ELSE NULL END ) AS "1970~1979",
    SUM ( CASE WHEN YEAR >= 1980 AND YEAR < 1990 THEN gdp ELSE NULL END ) AS "1980~1989",
    SUM ( CASE WHEN YEAR >= 1990 AND YEAR < 2000 THEN gdp ELSE NULL END ) AS "1990~1999",
    SUM ( CASE WHEN YEAR >= 2000 THEN gdp ELSE NULL END ) AS "2000~至今"
FROM
    country_gdp_year_final
WHERE
    country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' )
GROUP BY
    country_name;

从上图可以看出美国的经济体量和我们中国的经济体量不是在一个数量级的,中国每隔十年的GDP实现一倍的增速,美国一直飞速发展时期,中国要实现美国的GDP的话粗略估计需要至少30年的时间甚至更久;

回归正题,我们今天的主角是filter子句,ANSI SQL加入filter关键词的主要目的就是替代case when子句,简化case when参与的聚合语句,增加可可读性,我们用同样的filter 子句实现上面的case when参与的聚合操作。

1
2
3
4
5
6
7
8
9
10
11
SELECT
    country_name,
    SUM ( gdp )     FILTER ( WHERE  YEAR >= 1960 AND YEAR < 1970 ) AS "1960~1969",
    SUM ( gdp )   FILTER ( WHERE  YEAR >= 1970 AND YEAR < 1980 ) AS "1970~1979",
    SUM ( gdp )   FILTER ( WHERE  YEAR >= 1980 AND YEAR < 1990 )   AS "1980~1989",
    SUM ( gdp )   FILTER ( WHERE  YEAR >= 1990 AND YEAR < 2000 ) AS "1990~1999",
    SUM ( gdp )   FILTER ( WHERE  YEAR >= 2000 ) AS "2000~至今"
FROM
    country_gdp_year_final  WHERE  country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' )
GROUP BY
    country_name;

从上面的结果我们可以看出filter子句和case when 子句在聚合函数中使用是等价的,并且filter子句的可读性更好,让人一眼就能看出SQL的目的和作用,

下面我们看一下上面俩个语句的的执行计划:

从上面的结果我们可以看出来俩种语句不仅结果一样而且产生的执行计划也是一致的,并且俩个语句值进行了一次全表扫描就计算出了结果,在平时的开发中,很多开发者为了实现相同的结果可能要进行五次全表扫描,很可能会写出以下的相同查询结果但是不同性能的SQL 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
SELECT ff.country_name,ff."1960~1969",aa."1970~1979",bb."1980~1989",cc."1990~1999",dd."2000~至今"
FROM
    (
    SELECT country_name,SUM ( gdp ) AS "1960~1969" 
    FROM
        country_gdp_year_final ff
        WHERE  YEAR >= 1960 AND YEAR < 1970 AND  country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' )  
    GROUP BY
        country_name
    ) AS ff 
    LEFT JOIN (
    SELECT country_name,SUM ( gdp ) AS "1970~1979" 
    FROM
        country_gdp_year_final
        WHERE  YEAR >= 1970 AND YEAR < 1980 AND  country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' )  
    GROUP BY
        country_name
    ) AS aa  ON aa.country_name = ff.country_name
    LEFT JOIN (
    SELECT country_name,SUM ( gdp ) AS "1980~1989" 
    FROM
        country_gdp_year_final
        WHERE  YEAR >= 1980 AND YEAR < 1990 AND  country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' )
    GROUP BY
        country_name
    ) AS bb  ON bb.country_name = ff.country_name 
    LEFT JOIN (
    SELECT country_name,SUM ( gdp ) AS "1990~1999" 
    FROM
        country_gdp_year_final
        WHERE  YEAR >= 1990 AND YEAR < 2000and  country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' )  
    GROUP BY
        country_name
    ) AS cc  ON cc.country_name = ff.country_name 
    LEFT JOIN (
    SELECT country_name,SUM ( gdp ) AS "2000~至今" 
    FROM
        country_gdp_year_final
    WHERE   YEAR >= 2000 AND  country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' )  
    GROUP BY
    country_name
    ) AS dd  ON dd.country_name = ff.country_name;

我也相信很多人开发者写出来的SQL和上面的SQL基本差不多,这种SQL不仅很长而且很难都,更致命的是这种SQL进行了五次全表扫描,在不考虑缓存命中的的情况下,这种SQL的查询时间是上面filter和case when子句的五倍,我们可以看一下这个长SQL的查询计划。

从上面的查询计划我们可以看出来经过了五次全表扫描,五次聚合,如果这个表的数据量很大,那么性能可想而知。

最后我想说的是filter适合所有的聚合函数,不仅仅是PG内置的的聚合函数,还支持安装扩展包的聚合函数,总之filter子句非常的棒!!!

编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2016-07-12 TOMCAT 关闭报错:Tomcat did not stop in time. PID file was not removed
2015-07-12 Open SSH原理
点击右上角即可分享
微信分享提示