【SQL开发实战技巧】系列(二十八):数仓报表场景☞人员分布问题以及不同组(分区)同时聚集如何实现

本文涉及的产品
云原生数据仓库AnalyticDB MySQL版,基础版 8ACU 100GB 1个月
简介: 【SQL开发实战技巧】这一系列博主当作复习旧知识来进行写作,毕竟SQL开发在数据分析场景非常重要且基础,面试也会经常问SQL开发和调优经验,相信当我写完这一系列文章,也能再有所收获,未来面对SQL面试也能游刃有余~。

# 前言



本篇文章讲解的主要内容是:***通过行转列实现人员空间分布问题(工作显示为一列,每位员工显示一行)、连续行转列应该注意的问题、通过执行计划看对不同组、分区同时实现聚集需求:要求在员工表的明细数据里列出员工所在部门及职位的人数!!***

【SQL开发实战技巧】这一系列博主当作复习旧知识来进行写作,毕竟SQL开发在数据分析场景非常重要且基础,面试也会经常问SQL开发和调优经验,相信当我写完这一系列文章,也能再有所收获,未来面对SQL面试也能游刃有余~。





---




## 一、人员在工作空间的分布问题


现在有一个需求:要求每种工作显示为一列,每位员工显示一行,员工与工作对应时显示为是,不对应则显示为空!

那这个需求怎么做呢?

其实我们可以活用PIVOT函数,按工作、员工分组,设对应位置为是:


```sql

SQL> select * from (select ename,job from emp)

 2  pivot(

 3  max('是')

 4  for job in(

 5    'ANALYST' as ANALYST,

 6    'CLERK' as CLERK,

 7    'MANAGER' as MANAGER,

 8    'PRESIDENT' as PRESIDENT,

 9    'SALESMAN' as SALESMAN

10    )

11  );


ENAME      ANALYST CLERK MANAGER PRESIDENT SALESMAN

---------- ------- ----- ------- --------- --------

ADAMS              是                      

ALLEN                                      是

BLAKE                    是                

CLARK                    是                

FORD       是                              

JAMES              是                      

JONES                    是                

KING                             是        

MARTIN                                     是

MILLER             是                      

SCOTT      是                              

SMITH              是                      

TURNER                                     是

WARD                                       是


14 rows selected

```


这个语句相当于`group by ename,job`。


## 二、创建稀疏矩阵


给上面问题的需求增加一下难度,现在的需求是:对应位置直接显示为员工姓名,且增加在部门间的分布,因未对数据进行汇总,所以仍可以用PIVOT来处理,查询语句如下:


```sql

SQL>

SQL> select *

 2    from (select empno, ename, ename as ename2, job, deptno from emp)

 3  pivot(max(ename)

 4     for deptno in(10 as d10, 20 as d20, 30 as d30))

 5  pivot(max(ename2)

 6     for job in('ANALYST' as ANALYST,

 7                'CLERK' as CLERK,

 8                'MANAGER' as MANAGER,

 9                'PRESIDENT' as PRESIDENT,

10                'SALESMAN' as SALESMAN

11                ));


EMPNO D10        D20        D30        ANALYST    CLERK      MANAGER    PRESIDENT  SALESMAN

----- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------

7900                       JAMES                 JAMES                            

7369            SMITH                            SMITH                            

7499                       ALLEN                                                  ALLEN

7521                       WARD                                                   WARD

7566            JONES                                       JONES                

7654                       MARTIN                                                 MARTIN

7698                       BLAKE                            BLAKE                

7782 CLARK                                                  CLARK                

7788            SCOTT                 SCOTT                                      

7839 KING                                                              KING      

7844                       TURNER                                                 TURNER

7876            ADAMS                            ADAMS                            

7902            FORD                  FORD                                        

7934 MILLER                                      MILLER                          


14 rows selected

```



注意:如果对数据有汇总,就不要用这种有两个PIOVT的方式。因为这种查询实际上相当于两个PIVOT的子句嵌套。

在之前的文章中有一个count的case when语句,具体如下:


```sql

SQL>

SQL> select count(case

 2                 when deptno = 10 then

 3                  ename

 4               end) as deptno_10,

 5         count(case

 6                 when deptno = 20 then

 7                  ename

 8               end) as deptno_20,

 9         count(case

10                 when deptno = 30 then

11                  ename

12               end) as deptno_30,

13         count(case

14                 when job = 'ANALYST' then

15                  job

16               end) as ANALYST,

17         count(case

18                 when job = 'CLERK' then

19                  job

20               end) as CLERK,

21         count(case

22                 when job = 'MANAGER' then

23                  job

24               end) as MANAGER,

25         count(case

26                 when job = 'PRESIDENT' then

27                  job

28               end) as PRESIDENT,

29         count(case

30                 when job = 'SALESMAN' then

31                  job

32               end) as SALESMAN

33    from emp;


DEPTNO_10  DEPTNO_20  DEPTNO_30    ANALYST      CLERK    MANAGER  PRESIDENT   SALESMAN

---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------

        3          5          6          2          4          3          1          4

```


我们尝试用PIOVT来改写,看会出现什么问题。原始PIOVT语句如下:


```sql

SQL>

SQL>  select *

 2     from (select  ename, ename as ename2, job, deptno from emp)

 3   pivot(count(ename)

 4      for deptno in(10 as d10, 20 as d20, 30 as d30))

 5   pivot(count(ename2)

 6      for job in('ANALYST' as ANALYST,

 7                 'CLERK' as CLERK,

 8                 'MANAGER' as MANAGER,

 9                 'PRESIDENT' as PRESIDENT,

10                 'SALESMAN' as SALESMAN

11                 ));


      D10        D20        D30    ANALYST      CLERK    MANAGER  PRESIDENT   SALESMAN

---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------

        0          0          1          0          1          1          0          4

        0          1          0          2          2          1          0          0

        1          0          0          0          1          1          1          0


SQL>

```


可以看到数据,与case when的结果不一致。下面改为嵌套的方式来分析。

嵌套第一步:


```sql

SQL> with t as (

 2  select *

 3     from (select  ename, ename as ename2, job, deptno from emp)

 4   pivot(count(ename)

 5      for deptno in(10 as d10, 20 as d20, 30 as d30))

 6  )

 7  select * from t;


ENAME2     JOB              D10        D20        D30

---------- --------- ---------- ---------- ----------

FORD       ANALYST            0          1          0

KING       PRESIDENT          1          0          0

WARD       SALESMAN           0          0          1

ADAMS      CLERK              0          1          0

ALLEN      SALESMAN           0          0          1

BLAKE      MANAGER            0          0          1

CLARK      MANAGER            1          0          0

JAMES      CLERK              0          0          1

JONES      MANAGER            0          1          0

SCOTT      ANALYST            0          1          0

SMITH      CLERK              0          1          0

MARTIN     SALESMAN           0          0          1

MILLER     CLERK              1          0          0

TURNER     SALESMAN           0          0          1


14 rows selected

```


第一步相当于`group by empno,job`。

嵌套示例第二步:


```sql

SQL> with t as

 2   (select *

 3      from (select ename, ename as ename2, job, deptno from emp)

 4    pivot(count(ename)

 5       for deptno in(10 as d10, 20 as d20, 30 as d30)))

 6  select *

 7    from t

 8  pivot (count(ename2) for job in('ANALYST' as ANALYST,

 9                             'CLERK' as CLERK,

10                             'MANAGER' as MANAGER,

11                             'PRESIDENT' as PRESIDENT,

12                             'SALESMAN' as SALESMAN));


      D10        D20        D30    ANALYST      CLERK    MANAGER  PRESIDENT   SALESMAN

---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------

        0          0          1          0          1          1          0          4

        0          1          0          2          2          1          0          0

        1          0          0          0          1          1          1          0


SQL>

```


因第一步返回的列为(ENAME2,JOB,D10,D20,D30)去掉(ENAME2,JOB)后,剩余的是(D10,D20,D30)。所以第二

步相当于`group by D10,D20,D30`。

但是我们想要的是对emp表根据job分组求count和根据部门分组求count,它们是对emp表两个组合的统计合并,而不是在条件1的基础上,再进行分组count。


## 三、对不同组、分区同时实现聚集


现在有个需求:要求在员工表的明细数据里列出员工所在部门及职位的人数。


没用分析函数前,这种需求要用自关联:


```sql

SQL> with t as

 2   (select count(*) as cnt from emp),

 3  t1 as

 4   (select deptno, count(*) as dcnt from emp group by deptno),

 5  t2 as

 6   (select job, count(*) as jcnt from emp group by job)

 7  select emp.ename,

 8         emp.deptno,

 9         t1.dcnt,

10         emp.job,

11         t2.jcnt,

12         (select * from t) as cnt

13    from emp

14   inner join t1

15      on (emp.deptno = t1.deptno)

16   inner join t2

17      on (emp.job = t2.job);


ENAME      DEPTNO       DCNT JOB             JCNT        CNT

---------- ------ ---------- --------- ---------- ----------

FORD           20          5 ANALYST            2         14

SCOTT          20          5 ANALYST            2         14

MILLER         10          3 CLERK              4         14

JAMES          30          6 CLERK              4         14

ADAMS          20          5 CLERK              4         14

SMITH          20          5 CLERK              4         14

CLARK          10          3 MANAGER            3         14

BLAKE          30          6 MANAGER            3         14

JONES          20          5 MANAGER            3         14

KING           10          3 PRESIDENT          1         14

TURNER         30          6 SALESMAN           4         14

MARTIN         30          6 SALESMAN           4         14

WARD           30          6 SALESMAN           4         14

ALLEN          30          6 SALESMAN           4         14


14 rows selected



SQL>

```


看一下执行计划:


```sql

Plan Hash Value  :


------------------------------------------------------------------------------

| Id  | Operation               | Name      | Rows | Bytes | Cost | Time     |

------------------------------------------------------------------------------

|   0 | SELECT STATEMENT        |           |   14 |   868 |   12 | 00:00:01 |

|   1 |   VIEW                  |           |    1 |    13 |    1 | 00:00:01 |

|   2 |    SORT AGGREGATE       |           |    1 |       |      |          |

|   3 |     INDEX FULL SCAN     | IDX_EMPNO |   15 |       |    1 | 00:00:01 |

| * 4 |   HASH JOIN             |           |   14 |   868 |   11 | 00:00:01 |

| * 5 |    HASH JOIN            |           |   13 |   559 |    7 | 00:00:01 |

|   6 |     VIEW                |           |    3 |    78 |    4 | 00:00:01 |

|   7 |      SORT GROUP BY      |           |    3 |     9 |    4 | 00:00:01 |

|   8 |       TABLE ACCESS FULL | EMP       |   15 |    45 |    3 | 00:00:01 |

| * 9 |     TABLE ACCESS FULL   | EMP       |   13 |   221 |    3 | 00:00:01 |

|  10 |    VIEW                 |           |    5 |    95 |    4 | 00:00:01 |

|  11 |     SORT GROUP BY       |           |    5 |    40 |    4 | 00:00:01 |

|  12 |      TABLE ACCESS FULL  | EMP       |   15 |   120 |    3 | 00:00:01 |

------------------------------------------------------------------------------


Predicate Information (identified by operation id):

------------------------------------------

* 4 - access("EMP"."JOB"="T2"."JOB")

* 5 - access("EMP"."DEPTNO"="T1"."DEPTNO")

* 9 - filter("EMP"."JOB" IS NOT NULL AND "EMP"."DEPTNO" IS NOT NULL)

```


这种写法比较复杂,而且要对表emp访问四次(因为我建了索引。所以有一个走了索引)。

如果改用分析函数,语句就较简单:


```sql

SQL> select emp.ename,

 2         emp.deptno,

 3         count(*) over(partition by deptno) dcnt,

 4         emp.job,

 5         count(*) over(partition by job) jcnt,

 6         count(*) over() as cnt

 7    from emp

 8  ;


ENAME      DEPTNO       DCNT JOB             JCNT        CNT

---------- ------ ---------- --------- ---------- ----------

MILLER         10          3 CLERK              4         14

KING           10          3 PRESIDENT          1         14

CLARK          10          3 MANAGER            3         14

SMITH          20          5 CLERK              4         14

SCOTT          20          5 ANALYST            2         14

ADAMS          20          5 CLERK              4         14

FORD           20          5 ANALYST            2         14

JONES          20          5 MANAGER            3         14

WARD           30          6 SALESMAN           4         14

MARTIN         30          6 SALESMAN           4         14

TURNER         30          6 SALESMAN           4         14

ALLEN          30          6 SALESMAN           4         14

JAMES          30          6 CLERK              4         14

BLAKE          30          6 MANAGER            3         14


14 rows selected

```


看执行计划:


```sql

Plan Hash Value  : 4086863039


----------------------------------------------------------------------

| Id | Operation             | Name | Rows | Bytes | Cost | Time     |

----------------------------------------------------------------------

|  0 | SELECT STATEMENT      |      |   15 |   255 |    5 | 00:00:01 |

|  1 |   WINDOW SORT         |      |   15 |   255 |    5 | 00:00:01 |

|  2 |    WINDOW SORT        |      |   15 |   255 |    5 | 00:00:01 |

|  3 |     TABLE ACCESS FULL | EMP  |   15 |   255 |    3 | 00:00:01 |

----------------------------------------------------------------------

```


从执行计划看,就扫描了一次表。

但是我前面不是有两篇文章不是一直在说用分析函数要慎重嘛?为什么我这里又推荐大家用了?

遇到这种多次访问同一个表的情况时,可以尝试看一下能否用分析函数改写,以及改写后的效率如何,如果像我现在这样通过分析执行计划得到"性能提升很明显"这个结论,

那你这个场景当然就可以用啦,当然还有最重要的一点是:改写完了别忘了核对数据!这可是非常重要的一点。


---


# 总结


本篇文章讲解的主要内容是:***通过行转列实现人员空间分布问题(工作显示为一列,每位员工显示一行)、连续行转列应该注意的问题、通过执行计划看对不同组、分区同时实现聚集需求:要求在员工表的明细数据里列出员工所在部门及职位的人数!!***

相关实践学习
AnalyticDB MySQL海量数据秒级分析体验
快速上手AnalyticDB MySQL,玩转SQL开发等功能!本教程介绍如何在AnalyticDB MySQL中,一键加载内置数据集,并基于自动生成的查询脚本,运行复杂查询语句,秒级生成查询结果。
阿里云云原生数据仓库AnalyticDB MySQL版 使用教程
云原生数据仓库AnalyticDB MySQL版是一种支持高并发低延时查询的新一代云原生数据仓库,高度兼容MySQL协议以及SQL:92、SQL:99、SQL:2003标准,可以对海量数据进行即时的多维分析透视和业务探索,快速构建企业云上数据仓库。 了解产品 https://www.aliyun.com/product/ApsaraDB/ads
相关文章
|
16天前
|
SQL 关系型数据库 OLAP
云原生数据仓库AnalyticDB PostgreSQL同一个SQL可以实现向量索引、全文索引GIN、普通索引BTREE混合查询,简化业务实现逻辑、提升查询性能
本文档介绍了如何在AnalyticDB for PostgreSQL中创建表、向量索引及混合检索的实现步骤。主要内容包括:创建`articles`表并设置向量存储格式,创建ANN向量索引,为表增加`username`和`time`列,建立BTREE索引和GIN全文检索索引,并展示了查询结果。参考文档提供了详细的SQL语句和配置说明。
32 1
|
4月前
|
消息中间件 Java Kafka
实时数仓Kappa架构:从入门到实战
【11月更文挑战第24天】随着大数据技术的不断发展,企业对实时数据处理和分析的需求日益增长。实时数仓(Real-Time Data Warehouse, RTDW)应运而生,其中Kappa架构作为一种简化的数据处理架构,通过统一的流处理框架,解决了传统Lambda架构中批处理和实时处理的复杂性。本文将深入探讨Kappa架构的历史背景、业务场景、功能点、优缺点、解决的问题以及底层原理,并详细介绍如何使用Java语言快速搭建一套实时数仓。
572 4
|
5月前
|
SQL
数仓规范之sql编写规范
编写SQL时,应遵循以下规范:所有关键字小写,表别名按a, b, c...顺序使用,复杂逻辑多行书写,提高可读性。SELECT字段需逐行列出,避免使用*,GROUP BY字段同样处理。WHERE条件多于一个时,每条件一行。JOIN子表推荐使用嵌套查询方式1,明确关联条件,避免笛卡尔积。关键逻辑需注释,INSERT SELECT后最外层字段加注释说明用途。示例中展示了推荐的JOIN替代子查询的写法,以提高代码的可读性和维护性。
234 1
|
6月前
|
SQL 安全 Go
SQL注入不可怕,XSS也不难防!Python Web安全进阶教程,让你安心做开发!
在Web开发中,安全至关重要,尤其要警惕SQL注入和XSS攻击。SQL注入通过在数据库查询中插入恶意代码来窃取或篡改数据,而XSS攻击则通过注入恶意脚本来窃取用户敏感信息。本文将带你深入了解这两种威胁,并提供Python实战技巧,包括使用参数化查询和ORM框架防御SQL注入,以及利用模板引擎自动转义和内容安全策略(CSP)防范XSS攻击。通过掌握这些方法,你将能够更加自信地应对Web安全挑战,确保应用程序的安全性。
126 3
|
6月前
|
SQL 分布式计算 大数据
大数据开发SQL代码编码原则和规范
这段SQL编码原则强调代码的功能完整性、清晰度、执行效率及可读性,通过统一关键词大小写、缩进量以及禁止使用模糊操作如select *等手段提升代码质量。此外,SQL编码规范还详细规定了代码头部信息、字段与子句排列、运算符前后间隔、CASE语句编写、查询嵌套、表别名定义以及SQL注释的具体要求,确保代码的一致性和维护性。
196 0
|
7月前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
785 0
|
6月前
|
关系型数据库 MySQL 网络安全
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
|
8月前
|
SQL 存储 监控
SQL Server的并行实施如何优化?
【7月更文挑战第23天】SQL Server的并行实施如何优化?
204 13
|
8月前
|
SQL
解锁 SQL Server 2022的时间序列数据功能
【7月更文挑战第14天】要解锁SQL Server 2022的时间序列数据功能,可使用`generate_series`函数生成整数序列,例如:`SELECT value FROM generate_series(1, 10)。此外,`date_bucket`函数能按指定间隔(如周)对日期时间值分组,这些工具结合窗口函数和其他时间日期函数,能高效处理和分析时间序列数据。更多信息请参考官方文档和技术资料。
121 9
|
8月前
|
SQL 存储 网络安全
关系数据库SQLserver 安装 SQL Server
【7月更文挑战第26天】
102 6

热门文章

最新文章