这样只需要扫描索引区间的端点值即可,实现快速索引扫描(这里指的是min/max查找,非INDEX FAST FULL SCAN)。
本文研究ORACLE快速索引扫描的方法以及如何实现类似MySQL的index loose scan让分组能够走索引快速扫描。
建表语句如下:
点击(此处)折叠或打开
-
drop table group_tab;
-
create table group_tab as select * from dba_objects;
-
-
--多执行几次
-
insert into group_tab select * from group_tab;
-
commit;
-
-
--建立索引:
-
create index idx_group_tab on group_tab(owner,object_id);
-
-
--为了演示,将owner改为not null
-
alter table group_tab modify owner not null;
-
-
--收集统计信息:
-
exec dbms_stats.gather_table_stats(ownname=>user,tabname=>'group_tab',no_invalidate=>false);
-
-
环境:
-
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
- Version 19.3.0.0.0
点击(此处)折叠或打开
-
select count(distinct owner),count(*)
-
from group_tab;
-
-
COUNT(DISTINCTOWNER) COUNT(*)
-
-------------------- ----------
- 27 1164944
1.ORACLE里索引快速扫描(MIN/MAX)
1)在ORACLE里如果无条件,可以实现快速索引扫描。
SQL:点击(此处)折叠或打开
-
select min(owner)
- from group_tab;
点击(此处)折叠或打开
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 760541921
-
-
--------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | 5 | 3 (0)| 00:00:01 |
-
| 1 | SORT AGGREGATE | | 1 | 5 | | |
-
| 2 | INDEX FULL SCAN (MIN/MAX)| IDX_GROUP_TAB | 1 | 5 | 3 (0)| 00:00:01 |
-
--------------------------------------------------------------------------------------------
-
Statistics
-
----------------------------------------------------------
-
1 recursive calls
-
0 db block gets
-
3 consistent gets
-
0 physical reads
-
0 redo size
-
559 bytes sent via SQL*Net to client
-
394 bytes received via SQL*Net from client
-
2 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
- 1 rows processed
2)ORACLE里索引前导列等值,可以对次列min/max快速扫描
点击(此处)折叠或打开
-
select min(object_id)
-
from group_tab
-
where owner = 'SYS';
-
- Elapsed: 00:00:00.01
点击(此处)折叠或打开
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 3732729641
-
-
----------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
----------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | 10 | 3 (0)| 00:00:01 |
-
| 1 | SORT AGGREGATE | | 1 | 10 | | |
-
| 2 | FIRST ROW | | 1 | 10 | 3 (0)| 00:00:01 |
-
|* 3 | INDEX RANGE SCAN (MIN/MAX)| IDX_GROUP_TAB | 1 | 10 | 3 (0)| 00:00:01 |
-
----------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
3 - access("OWNER"='SYS')
-
-
Statistics
-
----------------------------------------------------------
-
1 recursive calls
-
0 db block gets
-
3 consistent gets
-
0 physical reads
-
0 redo size
-
556 bytes sent via SQL*Net to client
-
418 bytes received via SQL*Net from client
-
2 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
- 1 rows processed
Oracle数据库比较智能,比如三个列索引:
点击(此处)折叠或打开
- create index idx1_group_tab on group_tab(owner,object_id,object_name);
SQL如下:
点击(此处)折叠或打开
-
select min(object_id)
-
from group_tab
- where owner = 'SYS' and object_name like 'AB%';
看SQL条件:where owner = 'SYS' and object_name like 'AB%',索引中间列没有条件,前导列
owner有等值条件,也可以实现INDEX RANGE SCAN (MIN/MAX),
看谓词是access("OWNER"='SYS'),filter("OBJECT_NAME" LIKE 'AB%')。
点击(此处)折叠或打开
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 4109515879
-
-
-----------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | 45 | 3 (0)| 00:00:01 |
-
| 1 | SORT AGGREGATE | | 1 | 45 | | |
-
| 2 | FIRST ROW | | 1 | 45 | 3 (0)| 00:00:01 |
-
|* 3 | INDEX RANGE SCAN (MIN/MAX)| IDX1_GROUP_TAB | 1 | 45 | 3 (0)| 00:00:01 |
-
-----------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
3 - access("OWNER"='SYS')
-
filter("OBJECT_NAME" LIKE 'AB%')
-
-
Statistics
-
----------------------------------------------------------
-
1 recursive calls
-
0 db block gets
-
6653 consistent gets
-
6694 physical reads
-
0 redo size
-
554 bytes sent via SQL*Net to client
-
445 bytes received via SQL*Net from client
-
2 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
- 1 rows processed
点击(此处)折叠或打开
- drop index idx1_group_tab;
3)在ORACLE里注意,不能同时查找min、max,在MySQL里可以同时查找
走不了索引快速扫描:
点击(此处)折叠或打开
-
select min(object_id),max(object_id)
-
from group_tab
-
where owner = 'SYS';
- Elapsed: 00:00:00.18
这样不能实现min/max index scan:
点击(此处)折叠或打开
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2957380139
-
-
-----------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | 10 | 141 (1)| 00:00:01 |
-
| 1 | SORT AGGREGATE | | 1 | 10 | | |
-
|* 2 | INDEX RANGE SCAN| IDX_GROUP_TAB | 43146 | 421K| 141 (1)| 00:00:01 |
-
-----------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
2 - access("OWNER"='SYS')
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
2356 consistent gets
-
0 physical reads
-
0 redo size
-
645 bytes sent via SQL*Net to client
-
433 bytes received via SQL*Net from client
-
2 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
- 1 rows processed
如果要同时查找,实现快速扫描端点值,可以用dual表+标量子查询:
点击(此处)折叠或打开
-
select
-
(select min(object_id)
-
from group_tab
-
where owner = 'SYS') min_object_id,
-
(select max(object_id)
-
from group_tab
-
where owner = 'SYS') max_object_id
- from dual;
执行计划如下:
点击(此处)折叠或打开
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 3276013224
-
-
----------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
----------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 8 (0)| 00:00:01 |
-
| 1 | SORT AGGREGATE | | 1 | 10 | | |
-
| 2 | FIRST ROW | | 1 | 10 | 3 (0)| 00:00:01 |
-
|* 3 | INDEX RANGE SCAN (MIN/MAX)| IDX_GROUP_TAB | 1 | 10 | 3 (0)| 00:00:01 |
-
| 4 | SORT AGGREGATE | | 1 | 10 | | |
-
| 5 | FIRST ROW | | 1 | 10 | 3 (0)| 00:00:01 |
-
|* 6 | INDEX RANGE SCAN (MIN/MAX)| IDX_GROUP_TAB | 1 | 10 | 3 (0)| 00:00:01 |
-
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
-
----------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
3 - access("OWNER"='SYS')
-
6 - access("OWNER"='SYS')
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
6 consistent gets
-
0 physical reads
-
0 redo size
-
643 bytes sent via SQL*Net to client
-
525 bytes received via SQL*Net from client
-
2 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
- 1 rows processed
在MySQL里没有问题:
语句如下:
点击(此处)折叠或打开
-
select gender,min(emp_no),max(emp_no)
-
from emp1
- group by gender;
Extra:Using index for group-by ,执行计划如下:
点击(此处)折叠或打开
-
+----+-------------+-------+------------+-------+-----------------------------------------------------------------------+-----------+---------+------+------+----------+--------------------------+
-
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-
+----+-------------+-------+------------+-------+-----------------------------------------------------------------------+-----------+---------+------+------+----------+--------------------------+
-
| 1 | SIMPLE | emp1 | NULL | range | idx2_emp1,idx_emp1_skip,idx4_emp1_loose,idx_emp1_date2,idx_emp1_date1 | idx2_emp1 | 1 | NULL | 3 | 100.00 | Using index for group-by |
-
+----+-------------+-------+------------+-------+-----------------------------------------------------------------------+-----------+---------+------+------+----------+--------------------------+
- 1 row in set, 1 warning (0.01 sec)
树形执行计划:Covering index skip scan for grouping
点击(此处)折叠或打开
-
*************************** 1. row ***************************
- EXPLAIN: -> Covering index skip scan for grouping on emp1 using idx2_emp1 (cost=4.90 rows=3) (actual time=3.152..3.167 rows=2 loops=1)
2.ORACLE里分组查找min/max,不能实现MySQL loose scan
点击(此处)折叠或打开
-
select owner,max(object_id)
-
from group_tab
- group by owner;
在ORACLE里,直接分组查询,走不了快速索引扫描INDEX RANGE SCAN (MIN/MAX),执行计划
走INDEX FAST FULL SCAN,逻辑读3785,索引扫描行数1164K行。
点击(此处)折叠或打开
-
PLAN_TABLE_OUTPUT
-
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 0a47gjns4huux, child number 2
-
-------------------------------------
-
select owner,max(object_id) from group_tab group by owner
-
-
Plan hash value: 1341240122
-
-
----------------------------------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-
----------------------------------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 27 |00:00:00.21 | 3785 | | | |
-
| 1 | HASH GROUP BY | | 1 | 27 | 27 |00:00:00.21 | 3785 | 1010K| 1010K| 2561K (0)|
-
| 2 | INDEX FAST FULL SCAN| IDX_GROUP_TAB | 1 | 1164K| 1164K|00:00:00.09 | 3785 | | | |
- ----------------------------------------------------------------------------------------------------------------------------
点击(此处)折叠或打开
-
select/*+index_ss(a)*/ owner,max(object_id)
-
from group_tab a
-
group by owner;
-
27 rows selected.
-
-
PLAN_TABLE_OUTPUT
-
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 7n24nwtgzssdb, child number 2
-
-------------------------------------
-
select/*+index_ss(a)*/ owner,max(object_id) from group_tab a group by
-
owner
-
-
Plan hash value: 202550610
-
-
------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-
------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 27 |00:00:00.50 | 3733 |
-
| 1 | SORT GROUP BY NOSORT| | 1 | 27 | 27 |00:00:00.50 | 3733 |
-
| 2 | INDEX SKIP SCAN | IDX_GROUP_TAB | 1 | 1164K| 1164K|00:00:00.32 | 3733 |
- ------------------------------------------------------------------------------------------------
MySQL对分组可以实现index loose scan,如下所示:
可以看到执行计划Extra:Using index for group-by,树形计划显示Covering index skip scan for
grouping (ORACLE没有实现这种)
而且MySQL可以将min.max写在一层(ORACLE不可以)。
都是覆盖索引扫描才可以:
点击(此处)折叠或打开
-
explain select gender,max(first_name),min(first_name)
-
-> from emp1
-
-> group by gender
-
-> ;
-
+----+-------------+-------+------------+-------+-----------------------------------------------------------------------+-----------------+---------+------+------+----------+--------------------------+
-
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-
+----+-------------+-------+------------+-------+-----------------------------------------------------------------------+-----------------+---------+------+------+----------+--------------------------+
-
| 1 | SIMPLE | emp1 | NULL | range | idx2_emp1,idx_emp1_skip,idx4_emp1_loose,idx_emp1_date2,idx_emp1_date1 | idx4_emp1_loose | 1 | NULL | 3 | 100.00 | Using index for group-by |
-
+----+-------------+-------+------------+-------+-----------------------------------------------------------------------+-----------------+---------+------+------+----------+--------------------------+
- 1 row in set, 1 warning (0.01 sec)
点击(此处)折叠或打开
-
explain analyze
-
-> select gender,max(first_name),min(first_name)
-
-> from emp1
-
-> group by gender
-
-> \G
-
*************************** 1. row ***************************
- EXPLAIN: -> Covering index skip scan for grouping on emp1 using idx4_emp1_loose (cost=5.20 rows=3) (actual time=3.376..3.399 rows=2 loops=1)
3.ORACLE使用递归with实现MySQL loose scan快速分组
查找min/max
点击(此处)折叠或打开
-
select count(distinct owner),count(*) from group_tab;
-
-
COUNT(DISTINCTOWNER) COUNT(*)
-
-------------------- ----------
- 27 1164944
思考:如果能将27个owner,逐条取出来并且按照owner = :owner赋值,然后查找min/max,
这样就可以实现min/max索引快速查找。
1)首先要找出这个27个owner,不能直接distinct,那样也不能快速查找,很显然owner列是索引
前导列,可以用递归的方法,锚点选择min(owner),然后递归查找比前面所有owner大的min(
owner),这样类似loose scan,快速找到27个owner。
2)临时表现在是27个owner行,可以逐行传递给表group_tab,查找min/max,可以利用索引
的min/max扫描, 一般情况下,这种用标量子查询效率较高,因为owner的基数小,
标量子查询循环次数少。
递归with快速找到27个不同的owner值,注意owner IS NOT NULL
使用递归with如下:
点击(此处)折叠或打开
-
WITH owner_cte(owner) AS (
-
SELECT MIN(owner) FROM group_tab
-
UNION ALL
-
SELECT (SELECT MIN(owner) FROM group_tab WHERE owner > c.owner)
-
FROM owner_cte c
-
WHERE c.owner IS NOT NULL
-
)
-
--然后使用标量子查询快速查找max值
-
SELECT c.owner,
-
(SELECT MAX(g.object_id) FROM group_tab g WHERE g.owner = c.owner) max_object_id
-
from owner_cte c
- where c.owner IS NOT NULL;
执行计划貌似不符合预期,标量子查询竟然与递归with的结果做了HASH JOIN,标量子查询没有
走类似FILTER的计划,这样和直接分组没有啥区别,逻辑读3847:
点击(此处)折叠或打开
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1639639195
-
-
------------------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
------------------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 2 | 290 | 1127 (9)| 00:00:01 |
-
|* 1 | HASH JOIN OUTER | | 2 | 290 | 1127 (9)| 00:00:01 |
-
|* 2 | VIEW | | 2 | 132 | 27 (0)| 00:00:01 |
-
| 3 | UNION ALL (RECURSIVE WITH) BREADTH FIRST| | | | | |
-
| 4 | SORT AGGREGATE | | 1 | 5 | | |
-
| 5 | INDEX FULL SCAN (MIN/MAX) | IDX_GROUP_TAB | 1 | 5 | 3 (0)| 00:00:01 |
-
| 6 | SORT AGGREGATE | | 1 | 5 | | |
-
| 7 | FIRST ROW | | 1 | 5 | 3 (0)| 00:00:01 |
-
|* 8 | INDEX RANGE SCAN (MIN/MAX) | IDX_GROUP_TAB | 1 | 5 | 3 (0)| 00:00:01 |
-
|* 9 | RECURSIVE WITH PUMP | | | | | |
-
| 10 | VIEW | VW_SSQ_1 | 27 | 2133 | 1100 (9)| 00:00:01 |
-
| 11 | HASH GROUP BY | | 27 | 270 | 1100 (9)| 00:00:01 |
-
| 12 | INDEX FAST FULL SCAN | IDX_GROUP_TAB | 1164K| 11M| 1019 (2)| 00:00:01 |
-
------------------------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - access("ITEM_0"(+)="C"."OWNER")
-
2 - filter("C"."OWNER" IS NOT NULL)
-
8 - access("OWNER">:B1)
-
9 - filter("C"."OWNER" IS NOT NULL)
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
3847 consistent gets
-
0 physical reads
-
0 redo size
-
1319 bytes sent via SQL*Net to client
-
713 bytes received via SQL*Net from client
-
3 SQL*Net roundtrips to/from client
-
29 sorts (memory)
-
0 sorts (disk)
- 27 rows processed
COST,这里COST=1127,下面不让unnest cost其实更小,只有30。
知道了是查询转换有问题,那么不查询转换即可,对标量子查询使用no_unnest hints:
点击(此处)折叠或打开
-
WITH owner_cte(owner) AS (
-
SELECT MIN(owner) FROM group_tab
-
UNION ALL
-
SELECT (SELECT MIN(owner) FROM group_tab WHERE owner > c.owner)
-
FROM owner_cte c
-
WHERE c.owner IS NOT NULL
-
)
-
--标量子查询使用no_unnest hints
-
SELECT c.owner,
-
(SELECT/*+no_unnest*/ MAX(g.object_id) FROM group_tab g WHERE g.owner = c.owner) max_object_id
-
from owner_cte c
-
where c.owner IS NOT NULL;
-
- Elapsed: 00:00:00.00
显示顺序不一样,标量子查询执行计划比较特殊,在上面,同等级,但是是被下面的结果驱动),
这样标量子查询类似循环,外面行每传递一个owner,执行标量子查询,这样可以转为27个等值
条件分组,实现快速min/max查找
逻辑读从3847减少到120,Cost也只有30,执行计划里都是利用索引MIN/MAX快速查找:
点击(此处)折叠或打开
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 135935541
-
-
-----------------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 2 | 132 | 30 (0)| 00:00:01 |
-
| 1 | SORT AGGREGATE | | 1 | 10 | | |
-
| 2 | FIRST ROW | | 1 | 10 | 3 (0)| 00:00:01 |
-
|* 3 | INDEX RANGE SCAN (MIN/MAX) | IDX_GROUP_TAB | 1 | 10 | 3 (0)| 00:00:01 |
-
|* 4 | VIEW | | 2 | 132 | 27 (0)| 00:00:01 |
-
| 5 | UNION ALL (RECURSIVE WITH) BREADTH FIRST| | | | | |
-
| 6 | SORT AGGREGATE | | 1 | 5 | | |
-
| 7 | INDEX FULL SCAN (MIN/MAX) | IDX_GROUP_TAB | 1 | 5 | 3 (0)| 00:00:01 |
-
| 8 | SORT AGGREGATE | | 1 | 5 | | |
-
| 9 | FIRST ROW | | 1 | 5 | 3 (0)| 00:00:01 |
-
|* 10 | INDEX RANGE SCAN (MIN/MAX) | IDX_GROUP_TAB | 1 | 5 | 3 (0)| 00:00:01 |
-
|* 11 | RECURSIVE WITH PUMP | | | | | |
-
-----------------------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
3 - access("G"."OWNER"=:B1)
-
4 - filter("C"."OWNER" IS NOT NULL)
-
10 - access("OWNER">:B1)
-
11 - filter("C"."OWNER" IS NOT NULL)
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
120 consistent gets
-
0 physical reads
-
0 redo size
-
1319 bytes sent via SQL*Net to client
-
727 bytes received via SQL*Net from client
-
3 SQL*Net roundtrips to/from client
-
29 sorts (memory)
-
0 sorts (disk)
- 27 rows processed
如果写成JOIN也实现不了快速查找:
点击(此处)折叠或打开
-
WITH owner_cte(owner) AS (
-
SELECT MIN(owner) FROM group_tab
-
UNION ALL
-
SELECT (SELECT MIN(owner) FROM group_tab WHERE owner > c.owner)
-
FROM owner_cte c
-
WHERE c.owner IS NOT NULL
-
)
-
SELECT g.owner, MAX(g.object_id) AS max_object_id
-
FROM owner_cte c
-
JOIN group_tab g ON c.owner = g.owner
-
where g.owner is not null
-
GROUP BY g.owner;
- Elapsed: 00:00:00.37
INDEX RANGE SCAN,不符合预期:
点击(此处)折叠或打开
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2043639768
-
-
-------------------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 27 | 2052 | 312 (3)| 00:00:01 |
-
| 1 | HASH GROUP BY | | 27 | 2052 | 312 (3)| 00:00:01 |
-
| 2 | NESTED LOOPS | | 86292 | 6404K| 306 (1)| 00:00:01 |
-
| 3 | VIEW | | 2 | 132 | 27 (0)| 00:00:01 |
-
| 4 | UNION ALL (RECURSIVE WITH) BREADTH FIRST| | | | | |
-
| 5 | SORT AGGREGATE | | 1 | 5 | | |
-
| 6 | INDEX FULL SCAN (MIN/MAX) | IDX_GROUP_TAB | 1 | 5 | 3 (0)| 00:00:01 |
-
| 7 | SORT AGGREGATE | | 1 | 5 | | |
-
| 8 | FIRST ROW | | 1 | 5 | 3 (0)| 00:00:01 |
-
|* 9 | INDEX RANGE SCAN (MIN/MAX) | IDX_GROUP_TAB | 1 | 5 | 3 (0)| 00:00:01 |
-
|* 10 | RECURSIVE WITH PUMP | | | | | |
-
|* 11 | INDEX RANGE SCAN | IDX_GROUP_TAB | 43146 | 421K| 140 (1)| 00:00:01 |
-
-------------------------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
9 - access("OWNER">:B1)
-
10 - filter("C"."OWNER" IS NOT NULL)
-
11 - access("C"."OWNER"="G"."OWNER")
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
3829 consistent gets
-
0 physical reads
-
0 redo size
-
1319 bytes sent via SQL*Net to client
-
718 bytes received via SQL*Net from client
-
3 SQL*Net roundtrips to/from client
-
29 sorts (memory)
-
0 sorts (disk)
- 27 rows processed
总结:
本文主要研究ORACLE里的索引快速扫描min/max的方法,以及使用递归with查找前导列
distinct值+标量子查询(12以上要有no_unnest hints)实现MySQL group by 走
index loose scan的效果。