【SQL开发实战技巧】系列(二十三):数仓报表场景☞ 如何对数据排列组合去重以及通过如何找到包含最大值和最小值的记录这个问题再次用执行计划给你证明分析函数性能不一定高

简介: 怎样对数据组合重新排列并去重的问题、通过如何找到包含最大值和最小值的记录这个问题再次用执行计划给你证明分析函数性能不一定高【SQL开发实战技巧】这一系列博主当作复习旧知识来进行写作,毕竟SQL开发在数据分析场景非常重要且基础,面试也会经常问SQL开发和调优经验,相信当我写完这一系列文章,也能再有所收获,未来面对SQL面试也能游刃有余~。本篇文章主要介绍的两个方面,第一个方面曾经有好几个网友和同事问我,第二个问题真的是很多同行的通病,认为分析函数是万金油,一股脑用。

前言

本篇文章讲解的主要内容是:怎样对数据组合重新排列并去重的问题、通过如何找到包含最大值和最小值的记录这个问题再次用执行计划给你证明分析函数性能不一定高
【SQL开发实战技巧】这一系列博主当作复习旧知识来进行写作,毕竟SQL开发在数据分析场景非常重要且基础,面试也会经常问SQL开发和调优经验,相信当我写完这一系列文章,也能再有所收获,未来面对SQL面试也能游刃有余~。


一、排列组合去重

下面介绍一个数据组合去重的问题。数据环境模拟如下:

drop table test purge;
create table test(id,t1,t2,t3)  as 
select '1','1','3','2' from dual union all
select '2','1','3','2' from dual union all
select '3','3','2','1' from dual union all
select '4','4','2','1' from dual;

上述测试表中前三列tl、t2、t3的数据组合是重复的(都是1、2、3),要求用查询语句找出这些重复的数据,并只保留一行。我们可以用以下步骤达到需求。
1、把tl、t2、t3这三列用列转行合并为一列。

SQL> select * from test
  2  unpivot(b2 for b3 in (t1,t2,t3));

ID B3 B2
-- -- --
1  T1 1
1  T2 3
1  T3 2
2  T1 1
2  T2 3
2  T3 2
3  T1 3
3  T2 2
3  T3 1
4  T1 4
4  T2 2
4  T3 1

12 rows selected

unpivot的具体用法将在后面介绍。
2、通过listagg函数对各组字符排序并合并。

SQL> with t as
  2   (select * from test unpivot(b2 for b3 in(t1, t2, t3)))
  3  select id, listagg(b2, ',') within group(order by b2) as nb2
  4    from t
  5   group by id;

ID NB2
-- --------------------------------------------------------------------------------
1  1,2,3
2  1,2,3
3  1,2,3
4  1,2,4

3、执行常用的去重语句。

SQL> with t as
  2   (select * from test unpivot(b2 for b3 in(t1, t2, t3))),
  3   t1 as (
  4  select id, listagg(b2, ',') within group(order by b2) as nb2
  5    from t
  6   group by id
  7   )
  8   select t1.*,row_number()over(partition by nb2 order by id) as rn
  9   from t1;

ID NB2                                                                                      RN
-- -------------------------------------------------------------------------------- ----------
1  1,2,3                                                                                     1
2  1,2,3                                                                                     2
3  1,2,3                                                                                     3
4  1,2,4                                                                                     1

SQL> 

如上所示,如果要去掉重复的组合数据,只需要保留RN=1的行即可。

二、找到包含最大值和最小值的记录

构建数据如下:

drop table test purge;
create table test as select * from dba_objects;
create index idx_test_object_id on test(object_id);
begin 
  dbms_stats.gather_table_stats(ownname =>'ZYD' ,tabname => 'TEST',estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE,cascade => TRUE,method_opt =>'FOR ALL COLUMNS SIZE REPEAT');
  end;
  

要求返回最大or最小的object_id及对应的object_name,在有分析函数以前,可以用下面的查询:

SQL> select/*zyd*/ object_name,object_id
  2  from test
  3  where object_id in(
  4  select max(object_id) from test
  5  UNION ALL
  6  select min(object_id) from test
  7  );

OBJECT_NAME                                                                       OBJECT_ID
-------------------------------------------------------------------------------- ----------
C_OBJ#                                                                                    2
TEST                                                                                1578856

需要对员工表扫描三次。但用如下分析函数只需要对员工表扫描一次:

SQL> select/*zyd*/ object_name, object_id
  2    from (select object_name,
  3                 object_id,
  4                 min(object_id) over() min_id,
  5                 max(object_id) over() max_id
  6            from test) x
  7   where object_id in (min_id, max_id);

OBJECT_NAME                                                                       OBJECT_ID
-------------------------------------------------------------------------------- ----------
C_OBJ#                                                                                    2
TEST                                                                                1578856

如果大家形成惯性思维,认为分析函数的效率最高,那就错了。我们通过autotrace看下PLAN(可以多执行几次)。

第一个语句的执行计划,如下图所示:

SQL> set timing on;
SQL> set autotrace traceonly;

SQL> alter session set current_schema=zyd;

Session altered.

Elapsed: 00:00:00.01
SQL> select/*zyd*/ object_name,object_id
from test
where object_id in(
select max(object_id) from test
UNION ALL
select min(object_id) from test
);  2    3    4    5    6    7

Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
Plan hash value: 2072175425

------------------------------------------------------------------------------------------------------
| Id  | Operation            | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |             |       2 |      86 |       8   (0)| 00:00:01 |
|   1 |  NESTED LOOPS            |             |       2 |      86 |       8   (0)| 00:00:01 |
|   2 |   NESTED LOOPS            |             |       2 |      86 |       8   (0)| 00:00:01 |
|   3 |    VIEW             | VW_NSO_1         |       2 |      26 |       4   (0)| 00:00:01 |
|   4 |     SORT UNIQUE         |             |       2 |      10 |       4   (0)| 00:00:01 |
|   5 |      UNION-ALL            |             |         |         |          |         |
|   6 |       SORT AGGREGATE        |             |       1 |       5 |          |         |
|   7 |        INDEX FULL SCAN (MIN/MAX)| IDX_TEST_OBJECT_ID |       1 |       5 |       2   (0)| 00:00:01 |
|   8 |       SORT AGGREGATE        |             |       1 |       5 |          |         |
|   9 |        INDEX FULL SCAN (MIN/MAX)| IDX_TEST_OBJECT_ID |       1 |       5 |       2   (0)| 00:00:01 |
|* 10 |    INDEX RANGE SCAN        | IDX_TEST_OBJECT_ID |       1 |         |       1   (0)| 00:00:01 |
|  11 |   TABLE ACCESS BY INDEX ROWID    | TEST             |       1 |      30 |       2   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

  10 - access("OBJECT_ID"="MAX(OBJECT_ID)")


Statistics
----------------------------------------------------------
      1  recursive calls
      0  db block gets
     11  consistent gets
      0  physical reads
      0  redo size
    685  bytes sent via SQL*Net to client
    552  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      1  sorts (memory)
      0  sorts (disk)
      2  rows processed

可以看到,第一个语句虽然访问了三次,但三次都是用的索引,所以效率并不低。第二个语句执行计划如下图所示:

SQL> select/*zyd*/ object_name, object_id
  from (select object_name,
               object_id,
               min(object_id) over() min_id,
               max(object_id) over() max_id
          from test) x
 where object_id in (min_id, max_id);  2    3    4    5    6    7

Elapsed: 00:00:00.12

Execution Plan
----------------------------------------------------------
Plan hash value: 1093040662

----------------------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time       |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |       |   115K|    11M|   540   (1)| 00:00:01 |
|*  1 |  VIEW            |       |   115K|    11M|   540   (1)| 00:00:01 |
|   2 |   WINDOW BUFFER     |       |   115K|  3375K|   540   (1)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| TEST |   115K|  3375K|   540   (1)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJECT_ID"="MIN_ID" OR "OBJECT_ID"="MAX_ID")


Statistics
----------------------------------------------------------
      0  recursive calls
      0  db block gets
       1947  consistent gets
      0  physical reads
      0  redo size
    685  bytes sent via SQL*Net to client
    552  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      1  sorts (memory)
      0  sorts (disk)
      2  rows processed

从执行计划可以看出来,走的是全表扫描,因为用的是分析函数,效率反而更低。所以,除语句的改写外,大家还要学会分析PLAN。


总结

本篇文章主要介绍的两个方面,第一个方面曾经有好几个网友和同事问我,第二个问题真的是很多同行的通病,认为分析函数是万金油,一股脑用。实际解决问题中,优化过很多同类问题的sql,这里给大家再次用案例做了分享,感谢观看!

相关实践学习
数据库实验室挑战任务-初级任务
本场景介绍如何开通属于你的免费云数据库,在RDS-MySQL中完成对学生成绩的详情查询,执行指定类型SQL。
阿里云云原生数据仓库AnalyticDB MySQL版 使用教程
云原生数据仓库AnalyticDB MySQL版是一种支持高并发低延时查询的新一代云原生数据仓库,高度兼容MySQL协议以及SQL:92、SQL:99、SQL:2003标准,可以对海量数据进行即时的多维分析透视和业务探索,快速构建企业云上数据仓库。 了解产品 https://www.aliyun.com/product/ApsaraDB/ads
相关文章
|
6天前
|
SQL 数据可视化 算法
SQL Server聚类数据挖掘信用卡客户可视化分析
SQL Server聚类数据挖掘信用卡客户可视化分析
|
6天前
|
SQL JSON 分布式计算
实时数仓 Hologres产品使用合集之在执行SQL语句时,在插入语句后面直接跟上了insert,insert操作就会报错如何解决
实时数仓Hologres是阿里云推出的一款高性能、实时分析的数据库服务,专为大数据分析和复杂查询场景设计。使用Hologres,企业能够打破传统数据仓库的延迟瓶颈,实现数据到决策的无缝衔接,加速业务创新和响应速度。以下是Hologres产品的一些典型使用场景合集。
46 0
实时数仓 Hologres产品使用合集之在执行SQL语句时,在插入语句后面直接跟上了insert,insert操作就会报错如何解决
|
6天前
|
SQL 分布式计算 DataWorks
实时数仓 Hologres产品使用合集之查询分区表的生命周期(即之前设置的'auto_partitioning.num_retention'值)的SQL语句,可以使用什么查询
实时数仓Hologres是阿里云推出的一款高性能、实时分析的数据库服务,专为大数据分析和复杂查询场景设计。使用Hologres,企业能够打破传统数据仓库的延迟瓶颈,实现数据到决策的无缝衔接,加速业务创新和响应速度。以下是Hologres产品的一些典型使用场景合集。
19 0
|
6天前
|
SQL 数据库
数据库SQL语言实战(六)
本次实战的重点就在于对表格本身的一些处理,包括复制表格、修改表格结构、修改表格数据
|
6天前
|
SQL Oracle 关系型数据库
数据库SQL语言实战(五)(数据库系统概念第三章练习题)
本文的SQL语言适用的是Oracle数据库与mySQL可能存在略微不同
|
6天前
|
SQL Oracle 关系型数据库
数据库SQL语言实战(四)(数据库系统概念第三章练习题)
本文的SQL语言适用的是Oracle数据库与mySQL可能存在略微不同
数据库SQL语言实战(四)(数据库系统概念第三章练习题)
|
6天前
|
SQL Oracle 关系型数据库
数据库SQL语言实战(三)
本篇文章重点在于SQL中的各种删除操作
|
6天前
|
SQL 分布式计算 数据可视化
数据分享|Python、Spark SQL、MapReduce决策树、回归对车祸发生率影响因素可视化分析
数据分享|Python、Spark SQL、MapReduce决策树、回归对车祸发生率影响因素可视化分析
|
6天前
|
SQL 机器学习/深度学习 数据采集
数据分享|SQL Server、Visual Studio、tableau对信贷风险数据ETL分析、数据立方体构建可视化
数据分享|SQL Server、Visual Studio、tableau对信贷风险数据ETL分析、数据立方体构建可视化

热门文章

最新文章