【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
相关文章
|
4天前
|
SQL 存储 缓存
Flink SQL Deduplication 去重以及如何获取最新状态操作
Flink SQL Deduplication 是一种高效的数据去重功能,支持多种数据类型和灵活的配置选项。它通过哈希表、时间窗口和状态管理等技术实现去重,适用于流处理和批处理场景。本文介绍了其特性、原理、实际案例及源码分析,帮助读者更好地理解和应用这一功能。
53 14
|
27天前
|
消息中间件 Java Kafka
实时数仓Kappa架构:从入门到实战
【11月更文挑战第24天】随着大数据技术的不断发展,企业对实时数据处理和分析的需求日益增长。实时数仓(Real-Time Data Warehouse, RTDW)应运而生,其中Kappa架构作为一种简化的数据处理架构,通过统一的流处理框架,解决了传统Lambda架构中批处理和实时处理的复杂性。本文将深入探讨Kappa架构的历史背景、业务场景、功能点、优缺点、解决的问题以及底层原理,并详细介绍如何使用Java语言快速搭建一套实时数仓。
137 4
|
1月前
|
SQL 数据库 UED
SQL性能提升秘籍:5步优化法与10个实战案例
在数据库管理和应用开发中,SQL查询的性能优化至关重要。高效的SQL查询不仅可以提高应用的响应速度,还能降低服务器负载,提升用户体验。本文将分享SQL优化的五大步骤和十个实战案例,帮助构建高效、稳定的数据库应用。
47 3
|
1月前
|
SQL 缓存 监控
SQL性能提升指南:五大优化策略与十个实战案例
在数据库性能优化的世界里,SQL优化是提升查询效率的关键。一个高效的SQL查询可以显著减少数据库的负载,提高应用响应速度,甚至影响整个系统的稳定性和扩展性。本文将介绍SQL优化的五大步骤,并结合十个实战案例,为你提供一份详尽的性能提升指南。
49 0
|
2月前
|
SQL Oracle 关系型数据库
SQL优化-使用联合索引和函数索引
在一次例行巡检中,发现一条使用 `to_char` 函数将日期转换为字符串的 SQL 语句 CPU 利用率很高。为了优化该语句,首先分析了 where 条件中各列的选择性,并创建了不同类型的索引,包括普通索引、函数索引和虚拟列索引。通过对比不同索引的执行计划,最终确定了使用复合索引(包含函数表达式)能够显著降低查询成本,提高执行效率。
|
2月前
|
SQL 存储 数据可视化
手机短信SQL分析技巧与方法
在手机短信应用中,SQL分析扮演着至关重要的角色
|
2月前
|
SQL 数据库 数据库管理
数据库SQL函数应用技巧与方法
在数据库管理中,SQL函数是处理和分析数据的强大工具
|
2月前
|
SQL 数据库 索引
SQL中COUNT函数结合条件使用的技巧与方法
在SQL查询中,COUNT函数是一个非常常用的聚合函数,用于计算表中满足特定条件的记录数
|
2月前
|
SQL 关系型数据库 MySQL
sql注入原理与实战(三)数据库操作
sql注入原理与实战(三)数据库操作
sql注入原理与实战(三)数据库操作
|
2月前
|
SQL 关系型数据库 MySQL
SQL日期函数
SQL日期函数

热门文章

最新文章

下一篇
DataWorks