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

本文涉及的产品
云原生数据仓库AnalyticDB MySQL版,基础版 8ACU 100GB 1个月
简介: 怎样对数据组合重新排列并去重的问题、通过如何找到包含最大值和最小值的记录这个问题再次用执行计划给你证明分析函数性能不一定高【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,这里给大家再次用案例做了分享,感谢观看!

相关实践学习
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
相关文章
|
22天前
|
SQL 分布式计算 Serverless
EMR Serverless Spark:一站式全托管湖仓分析利器
本文根据2024云栖大会阿里云 EMR 团队负责人李钰(绝顶) 演讲实录整理而成
113 2
|
1月前
|
SQL Oracle 关系型数据库
SQL优化-使用联合索引和函数索引
在一次例行巡检中,发现一条使用 `to_char` 函数将日期转换为字符串的 SQL 语句 CPU 利用率很高。为了优化该语句,首先分析了 where 条件中各列的选择性,并创建了不同类型的索引,包括普通索引、函数索引和虚拟列索引。通过对比不同索引的执行计划,最终确定了使用复合索引(包含函数表达式)能够显著降低查询成本,提高执行效率。
|
1月前
|
SQL
数仓规范之sql编写规范
编写SQL时,应遵循以下规范:所有关键字小写,表别名按a, b, c...顺序使用,复杂逻辑多行书写,提高可读性。SELECT字段需逐行列出,避免使用*,GROUP BY字段同样处理。WHERE条件多于一个时,每条件一行。JOIN子表推荐使用嵌套查询方式1,明确关联条件,避免笛卡尔积。关键逻辑需注释,INSERT SELECT后最外层字段加注释说明用途。示例中展示了推荐的JOIN替代子查询的写法,以提高代码的可读性和维护性。
42 1
|
1月前
|
SQL 数据库 数据库管理
数据库SQL函数应用技巧与方法
在数据库管理中,SQL函数是处理和分析数据的强大工具
|
1月前
|
存储 SQL 分布式计算
湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
【10月更文挑战第7天】湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
68 1
|
1月前
|
SQL 数据库 索引
SQL中COUNT函数结合条件使用的技巧与方法
在SQL查询中,COUNT函数是一个非常常用的聚合函数,用于计算表中满足特定条件的记录数
|
1月前
|
DataWorks 数据挖掘 关系型数据库
基于hologres搭建轻量OLAP分析平台解决方案评测
一文带你详细了解基于hologres搭建轻量OLAP分析平台解决方案的优与劣
195 8
|
21天前
|
数据采集 分布式计算 OLAP
最佳实践:AnalyticDB在企业级大数据分析中的应用案例
【10月更文挑战第22天】在数字化转型的大潮中,企业对数据的依赖程度越来越高。如何高效地处理和分析海量数据,从中提取有价值的洞察,成为企业竞争力的关键。作为阿里云推出的一款实时OLAP数据库服务,AnalyticDB(ADB)凭借其强大的数据处理能力和亚秒级的查询响应时间,已经在多个行业和业务场景中得到了广泛应用。本文将从个人的角度出发,分享多个成功案例,展示AnalyticDB如何助力企业在广告投放效果分析、用户行为追踪、财务报表生成等领域实现高效的数据处理与洞察发现。
48 0
|
1月前
|
SQL 关系型数据库 MySQL
SQL日期函数
SQL日期函数
|
关系型数据库 MySQL Shell