sql 查询 distinc用法

distinct 和group by都需要排序,一样的结果集从执行计划的成本代价来看差距不大,但group by 还涉及到统计,所以应该需要准备工作。所以单纯从等价结果来说,选择distinct比较效率一些。

其实二者没有什么可比性,但是对于不包含聚集函数的GROUP BY操作来说,和DISTINCT操作是等价的。不过虽然二者的结果是一样的,但是二者的执行计划并不相同。

在Oracle9i中:

SQL> SELECT * FROM V$VERSION;

BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production PL/SQL Release 9.2.0.4.0 - Production
CORE 9.2.0.3.0 Production
TNS for Linux: Version 9.2.0.4.0 - Production
NLSRTL Version 9.2.0.4.0 - Production

SQL> CREATE TABLE T AS SELECT ROWNUM ID, A.* FROM DBA_OBJECTS A;

表已创建。

SQL> CREATE INDEX IND_T_CREATED ON T (CREATED);

索引已创建。

SQL> ALTER TABLE T MODIFY CREATED NOT NULL;

表已更改。

SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';

会话已更改。

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'T')

PL/SQL 过程已成功完成。

SQL> SET AUTOT ON EXP
SQL> SELECT COUNT(*) FROM (SELECT DISTINCT CREATED FROM T);

COUNT(*)
----------
4794

执行计划
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=65 Card=1)
1 0 SORT (AGGREGATE)
2 1 VIEW (Cost=65 Card=4794)
3 2 SORT (UNIQUE) (Cost=65 Card=4794 Bytes=38352)
4 3 INDEX (FAST FULL SCAN) OF 'IND_T_CREATED' (NON-UNIQUE) (Cost=4 Card=41802 Bytes=334416)

SQL> SELECT COUNT(*) FROM (SELECT CREATED FROM T GROUP BY CREATED);

COUNT(*)
----------
4794

执行计划
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=65 Card=1 Bytes=2)
1 0 SORT (AGGREGATE)
2 1 VIEW (Cost=65 Card=4794 Bytes=9588)
3 2 SORT (GROUP BY) (Cost=65 Card=4794 Bytes=38352)
4 3 INDEX (FAST FULL SCAN) OF 'IND_T_CREATED' (NON-UNIQUE) (Cost=4 Card=41802 Bytes=334416)

从执行计划上看,DISTINCT的操作是SORT (UNIQUE),而GROUP BY是SORT (GROUP BY)。DISTINCT操作只需要找出所有不同的值就可以了。而GROUP BY操作还要为其他聚集函数进行准备工作。从这一点上将,GROUP BY操作做的工作应该比DISTINCT所做的工作要多一些。

除了这一点,基本上看不到DISTINCT和GROUP BY(没有聚集函数的情况)有什么区别,而且从执行效率上也看不到明显的差异。

不过从10g开始,二者的差异开始体现出来了。

SQL> CONN YANGTK/YANGTK@YTK已连接。
SQL> SET AUTOT OFF
SQL> SET TIMING OFF
SQL> CREATE TABLE T AS SELECT ROWNUM ID, A.* FROM DBA_OBJECTS A;

表已创建。

SQL> CREATE INDEX IND_T_CREATED ON T (CREATED);

索引已创建。

SQL> ALTER TABLE T MODIFY CREATED NOT NULL;

表已更改。

SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';

会话已更改。

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'T')

PL/SQL 过程已成功完成。

SQL> SET AUTOT ON
SQL> SET TIMING ON

建立好测试环境后,看一看标准分页函数中,两个操作的差异:

SQL> SELECT * 
2 FROM 
3 (
4 SELECT ROWNUM RN, A.* 
5 FROM 
6 (
7 SELECT CREATED 
8 FROM T 
9 GROUP BY CREATED
10 ) A
11 WHERE ROWNUM < 20
12 ) 
13 WHERE RN >= 10;

RN CREATED
---------- -------------------
10 2005-12-19 17:07:57
11 2005-12-19 17:07:58
12 2005-12-19 17:08:24
13 2005-12-19 17:08:25
14 2005-12-19 17:08:26
15 2005-12-19 17:08:27
16 2005-12-19 17:08:28
17 2005-12-19 17:08:29
18 2005-12-19 17:08:33
19 2005-12-19 17:08:35

已选择10行。

已用时间: 00: 00: 00.06

执行计划
----------------------------------------------------------
Plan hash value: 3639065582

-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19 | 418 | 1 (0)|
|* 1 | VIEW | | 19 | 418 | 1 (0)|
|* 2 | COUNT STOPKEY | | | | |
| 3 | VIEW | | 969 | 8721 | 1 (0)|
|* 4 | SORT GROUP BY STOPKEY| | 969 | 7752 | 1 (0)|
| 5 | INDEX FULL SCAN | IND_T_CREATED | 969 | 7752 | 1 (0)|
-------------------------------------------------------------------------------

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

1 - filter("RN">=10)
2 - filter(ROWNUM<20)
4 - filter(ROWNUM<20)

统计信息
----------------------------------------------------------
1 recursive calls
0 db block gets
67 consistent gets
0 physical reads
0 redo size
642 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
10 rows processed

SQL> SELECT * 
2 FROM 
3 (
4 SELECT ROWNUM RN, A.* 
5 FROM 
6 (
7 SELECT DISTINCT CREATED 
8 FROM T 
9 ) A
10 WHERE ROWNUM < 20
11 ) 
12 WHERE RN >= 10;

RN CREATED
---------- -------------------
10 2005-12-19 17:07:57
11 2005-12-19 17:07:58
12 2005-12-19 17:08:24
13 2005-12-19 17:08:25
14 2005-12-19 17:08:26
15 2005-12-19 17:08:27
16 2005-12-19 17:08:28
17 2005-12-19 17:08:29
18 2005-12-19 17:08:33
19 2005-12-19 17:08:35

已选择10行。

已用时间: 00: 00: 00.03

执行计划
----------------------------------------------------------
Plan hash value: 1650124153

-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19 | 418 | 14 (36)|
|* 1 | VIEW | | 19 | 418 | 14 (36)|
|* 2 | COUNT STOPKEY | | | | |
| 3 | VIEW | | 987 | 8883 | 14 (36)|
|* 4 | SORT GROUP BY STOPKEY| | 987 | 7896 | 14 (36)|
| 5 | INDEX FAST FULL SCAN| IND_T_CREATED | 50333 | 393K| 10 (10)|
-------------------------------------------------------------------------------

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

1 - filter("RN">=10)
2 - filter(ROWNUM<20)
4 - filter(ROWNUM<20)

统计信息
----------------------------------------------------------
1 recursive calls
0 db block gets
73 consistent gets
0 physical reads
0 redo size
642 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
10 rows processed

出乎意料的是,GROUP BY操作的COST更低,而且逻辑读也小,这似乎与二者的工作量成反比。仔细观察执行计划发现,问题的根源来自于GROUP BY使用INDEX FULL SCAN,而DISTINCT使用了INDEX FAST FULL SCAN。也许有人会感到奇怪,索引的快速全扫描不是要比索引全扫描效率更高吗?对于读取所有数据的情况下,确实是索引快速全扫效率更高。但是由于这里采用了分页,只取前20条数据,而且Oracle的10g增加了GROUP BY STOPKEY这种新的执行路径,因此在这里GROUP BY操作的效率更高。

观察执行计划中的处理行数可以发现,索引全扫描由于是按照索引的顺序扫描,因此利用了STOPKEY,仅仅处理了969条记录就停了下来。而对于DISTINCT操作的快速索引全速而言,显然没有使用STOPKEY,读取了所有的50333条记录。这就是GROUP BY和DISTINCT的性能差异原因。

sql 单个字段去重查询 distinc 和 group by的效率问题的更多相关文章

  1. oracle 多字段去重查询

      oracle 多字段去重查询 CreationTime--2018年6月29日15点11分 Author:Marydon 1.情景展示 需要对表BASE_MRI_DEVICE的COMPNAME.F ...

  2. Oracle对字段去重查询所有字段数据

    单个字段: select distinct(a) from tableA; 多个字段,利用max()去重  SELECT  * FROM  GM_PPU_RESIDENT_NORBASE g  WHE ...

  3. 记一次SQL xml字段关联查询

    需求: 一张表是APP表,结构如下: app_category为该游戏所属的类别ID,xml字段类型 另一张表是类别表,就ID对应名称,这就不上图了. 还有一张表是每个游戏的下载记录,结构如下: Do ...

  4. MySQL数据库时间字段按年月日显示并多字段去重查询

    应用服务长久运行,难免要导出一些统计报表. 现在有一个日志表,记录了各种日志,需要导出十月份的登录日志,要求时间按日期显示,且每天用户登陆要去重. 先看日志表的字段构成: logType等于2的是登陆 ...

  5. sql 同一个字段在查询结果中出现两次

    SELECT GET .daytime,    GET.data AS GET,    xh.data AS xh FROM    ( SELECT daytime, SUM ( get_sum ) ...

  6. SQL 多字段去重

    select articleID from (select aeUID, max(articleID) articleID from [article] group by aeUID) a conca ...

  7. SQL去重之distinct和group by的应用

    遇到一个需求,要去重查出某张表的字段一和字段二,但是查出来的结果要按照表中记录的创建时间排序. 于是,第一时间就想到了使用distinct这个去重专用语法了: select distinct col1 ...

  8. Oracle 去重查询

      Oracle 去重查询 CreateTime--2018年2月28日15:38:45 Author:Marydon (一)使用distinct --查询指定区间内表停诊字段的值 SELECT DI ...

  9. mongodb多字段去重

    单字段去重 db.student.distinct("name"); 多字段去重 db.student.aggregate([{      $group:{            ...

随机推荐

  1. 以ZeroMQ谈消息中间件的设计【译文】

    本文主要是探究学习比较流行的一款消息层是如何设计与实现的 ØMQ是一种消息传递系统,或者乐意的话可以称它为"面向消息的中间件".它在金融服务,游戏开发,嵌入式系统,学术研究和航空航 ...

  2. 深入理解DOM节点类型第五篇——元素节点Element

    × 目录 [1]特征 [2]子节点 [3]特性操作[4]attributes 前面的话 元素节点Element非常常用,是DOM文档树的主要节点:元素节点是html标签元素的DOM化结果.元素节点主要 ...

  3. 一步步实现ABAP后台导入EXCEL到数据库【3】

    在一步步实现ABAP后台导入EXCEL到数据库[2]里,我们已经实现计划后台作业将数据导入数据库的功能.但是,这只是针对一个简单的自定义结构的导入程序.在实践应用中,面对不同的表.不同的导入文件,我们 ...

  4. ArcGIS Engine开发之地图基本操作(4)

    ArcGIS Engine开发中数据库的加载 1.加载个人地理数据库数据 个人地理数据库(Personal Geodatabase)使用Miscrosoft Access文件(*.mdb)进行空间数据 ...

  5. CoordinatorLayout, AppBarLayout, CollapsingToolbarLayout使用

    本文介绍Design Support Library中CoordinatorLayout, AppBarLayout, CollapsingToolbarLayout的使用. 先列出了Design S ...

  6. [转]ASP.NET应用程序生命周期趣谈(一)

    这几天一直在看ASP.NET应用程序生命周期,真是太难了,我理解起来费了劲了,但偏偏它又是那么重要,所以我希望能给大家带来一篇容易理解又好用的文章来帮助学习ASP.NET应用程序生命周期.这篇就是了. ...

  7. Android中的自定义控件(二)

    案例四: 自定义开关       功能介绍:本案例实现的功能是创建一个自定义的开关,可以自行决定开关的背景.当滑动开关时,开关的滑块可跟随手指移动.当手指松开后,滑块根据开关的状态,滑到最右边或者滑到 ...

  8. (十二)Maven生命周期和插件

    除了坐标.依赖以及仓库之外,Maven的另外两个核心概念是生命周期和插件.在有关Maven的日常使用中,命令行的输入往往就对应了生命周期,如mvn package就表示执行默认生命周期阶段packag ...

  9. 2、项目标准的制定 - PMO项目管理办公室

    PMO项目管理办公室应该是针对公司级别的项目内容制定项目标准.这个由公司的项目经理们进行集中讨论,然后将项目标准进行文档化,标准化,从而为公司的项目进行指导性的功能,为公司的项目进行服务.下面对项目标 ...

  10. 查看Oracle执行计划

    1.PL/SQL解释计划窗口 优点:方面 缺点:看到信息有限 2.explain_plan for 针对某个句子优化较方便 3.sqlplus Sqlplus里输入命令: set autotrace ...