INDEX FULL SCAN and Sorting Avoidance

Cases when sorting is avoided are quite known. Let’s take a look at yet another one when the optimizer ends up with a Sort Merge Join join method to execute a query. The query is a simple one and serves demonstrations purposes only:

select * from student s, takes t where s.id = t.id

with the following final plan:

The plan tells us that Sort  Merge Join is used to join the two tables.
The table at the bottom of the page is an exert from from the optimizer trace file to highlight some points (line numbers have been added).

Line 193: only 2 permutations are possible: STUDENT is outer table and TAKES is inner and visa versa.
To remind, number of permutations can be controlled by the _optimizer_max_permutations undocumented parameter which defaults to 2000.
Line 1: the optimizer starts estimating plan costs for the case when STUDENT is outer table and TAKES is inner.
Line 6: Nested Loop join is estimated and cost is 45 (line 20) when inner table TAKES is accessed by Full Scan (line 18) and is 30 (line 32) when TAKES is accessed via Range Scan by the index SYS_C0014106. Out of these two ways, the latter is a better one with less cost (line 35).
Line 63: The optimizer estimates cost for Sort Merge Join which is 12.002240. This is a case when both tables get sorted: STUDENT at line 45 and TAKES at line 56. For example, STUDENT table has only one block, so just one block needs to be sorted, line 49. There is just one run to sort and this is one-pass sort – no subsequent passes, hence # of merge passes is 0, line 50.
The optimizer picks up the plan with SMJ with cost 8.001695, lines 98 and 99 and this is where things get interesting (on line 104 the tables are swapped and other plans are considered and etc.).
Cost estimation for this plan kicks in on line 65SM Join (with index on outer).
The outer table STUDENT is accessed via index SYS_C0015206 and by INDEX FULL SCAN access path, line 68. Cost of this access is: 2.000510, line 72 (resc_cpu is CPU cost of access and ix_sel: 1.000000 is index selectivity: fraction of rows that qualify). What we observe is there is no sorting associated with this table here. Only inner table TAKES gets sorted, on line 81.
In case of INDEX FULL SCAN the database first lands on the leftmost leaf node of the index tree and then scans the index in the other direction i.e. scans the data in the order it is held in the index. Data in the index is sorted already. SYS_C0015206 is a PK on the ID column of the STUDENT table. So, it works as follows: get every entry from the index, get the rowid and fetch the table row by the rowid and the resultant data will be sorted by the ID column because from the index we get the data in a sorted manner. Hence, no need to invoke sorting on the table itself to prepare it for the Sort Merge Join: is actually what we observe from the plan. Here are the steps the database takes to execute it according to that plan (numbers are steps of the optimizer).
5 and 4:    SORT JOIN
                    TABLE ACCESS FULL
Here, the table TAKES is scanned fully and then sorted.

3 and 2:    TABLE ACCESS BY INDEX ROWID (note that no sorting is here)
                    INDEX FULL SCAN

                Here, the table STUDENT is accessed via index.

1:             MERGE JOIN
                Finally, invoking Sort Merge Join to join the two tables.

So, we have seen yet another case when the optimizer may skip sorting due an index on the join column.

1Join order[1]: STUDENT[S]#0 TAKES[T]#1
2
3***************
4Now joining: TAKES[T]#1
5***************
6NL Join
7Outer table: Card: 13.000000 Cost: 5.000998 Resp: 5.000998 Degree: 1 Bytes:
8Access path analysis for TAKES
9Scan IO Cost (Disk) = 3.076923
10Scan CPU Cost (Disk) = 41107.200000
11Total Scan IO Cost = 3.076923 (scan (Disk))
12+ 0.000000 (io filter eval) (= 0.000000 (per row) * 22.000000 (#rows))
133.076923
14Total Scan CPU Cost = 41107.200000 (scan (Disk))
15+ 1100.000000 (cpu filter eval) (= 50.000000 (per row) * 22.000000 (#rows))
1642207.2
17Inner table: TAKES Alias: T
18Access Path: TableScan
19NL Join: Cost: 45.015282 Resp: 45.015282 Degree: 1
20Cost_io: 45.000000 Cost_cpu: 587031
21Resp_io: 45.000000 Resp_cpu: 587031
22****** Costing Index SYS_C0014106
23SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_SCAN
24
25SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_FILTER
26
27Access Path: index (RangeScan)
28Index: SYS_C0014106
29resc_io: 2.000000 resc_cpu: 15143
30ix_sel: 0.083333 ix_sel_with_filters: 0.083333
31NL Join : Cost: 30.005924 Resp: 30.005924 Degree: 1
32Cost_io: 30.000000 Cost_cpu: 227573
33Resp_io: 30.000000 Resp_cpu: 227573
34
35Best NL cost: 30.005924
36resc: 30.005924 resc_io: 30.000000 resc_cpu: 227573
37resp: 30.005924 resp_io: 30.000000 resc_cpu: 227573
38SPD: Return code in qosdDSDirSetup: NOCTX, estType = JOIN
39Join Card: 22.000000 = outer (13.000000) * inner (22.000000) * sel (0.076923)
40Join Card – Rounded: 22 Computed: 22.000000
41Outer table: STUDENT Alias: S
42resc: 5.000998 card 13.000000 bytes: deg: 1 resp: 5.000998
43Inner table: TAKES Alias: T
44resc: 5.001070 card: 22.000000 bytes: deg: 1 resp: 5.001070
45using dmeth: 2 #groups: 1
46SORT ressource Sort statistics
47Sort width: 118 Area size: 131072 Max Area size: 20971520
48Degree: 1
49Blocks to Sort: 1 Row size: 38 Total Rows: 13
50Initial runs: 1 Merge passes: 0 IO Cost / pass: 0
51Total IO sort cost: 0.000000 Total CPU sort cost: 38415391
52Total Temp space used: 0
53SORT resource Sort statistics
54Sort width: 118 Area size: 131072 Max Area size: 20971520
55Degree: 1
56Blocks to Sort: 1 Row size: 40 Total Rows: 22
57Initial runs: 1 Merge passes: 0 IO Cost / pass: 0
58Total IO sort cost: 0.000000 Total CPU sort cost: 38417643
59Total Temp space used: 0
60SM join: Resc: 12.002240 Resp: 12.002240 [multiMatchCost=0.000000]
61SM Join
62SM cost: 12.002240
63resc: 12.002240 resc_io: 10.000000 resc_cpu: 76912478
64resp: 12.002240 resp_io: 10.000000 resp_cpu: 76912478
65SM Join (with index on outer)
66****** Costing Index SYS_C0015206
67SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_FILTER
68Access Path: index (FullScan)
69Index: SYS_C0015206
70resc_io: 2.000000 resc_cpu: 19573
71ix_sel: 1.000000 ix_sel_with_filters: 1.000000
72Cost: 2.000510 Resp: 2.000510 Degree: 1
73Outer table: STUDENT Alias: S
74resc: 2.000510 card 13.000000 bytes: deg: 1 resp: 2.000510
75Inner table: TAKES Alias: T
76resc: 5.001070 card: 22.000000 bytes: deg: 1 resp: 5.001070
77using dmeth: 2 #groups: 1
78SORT ressource Sort statistics
79Sort width: 118 Area size: 131072 Max Area size: 20971520
80Degree: 1
81Blocks to Sort: 1 Row size: 40 Total Rows: 22
82Initial runs: 1 Merge passes: 0 IO Cost / pass: 0
83Total IO sort cost: 0.000000 Total CPU sort cost: 38417643
84Total Temp space used: 0
85SM join: Resc: 8.001695 Resp: 8.001695 [multiMatchCost=0.000000]
86Outer table: STUDENT Alias: S
87resc: 5.000998 card 13.000000 bytes: deg: 1 resp: 5.000998
88Inner table: TAKES Alias: T
89resc: 5.001070 card: 22.000000 bytes: deg: 1 resp: 5.001070
90using dmeth: 2 #groups: 1
91Cost per ptn: 0.015728 #ptns: 1
92hash_area: 124 (max=5120) buildfrag: 1 probefrag: 1 ppasses: 1
93Hash join: Resc: 10.017796 Resp: 10.017796 [multiMatchCost=0.000000]
94HA Join
95HA cost: 10.017796
96resc: 10.017796 resc_io: 10.000000 resc_cpu: 683594
97resp: 10.017796 resp_io: 10.000000 resp_cpu: 683594
98Best:: JoinMethod: SortMerge
99Cost: 8.001695 Degree: 1 Resp: 8.001695 Card: 22.000000 Bytes:
100***********************
101Best so far: Table#: 0 cost: 2.000510 card: 13.000000 bytes: 325.000000
102Table#: 1 cost: 8.001695 card: 22.000000 bytes: 1144.000000
103***********************
104Join order[2]: TAKES[T]#1 STUDENT[S]#0
105
106***************
107Now joining: STUDENT[S]#0
108***************
109NL Join
110Outer table: Card: 22.000000 Cost: 5.001070 Resp: 5.001070 Degree: 1 Bytes:
111Access path analysis for STUDENT
112Scan IO Cost (Disk) = 3.000000
113Scan CPU Cost (Disk) = 38337.200000
114Total Scan IO Cost = 3.000000 (scan (Disk))
115+ 0.000000 (io filter eval) (= 0.000000 (per row) * 13.000000 (#rows))
1163
117Total Scan CPU Cost = 38337.200000 (scan (Disk))
118+ 650.000000 (cpu filter eval) (= 50.000000 (per row) * 13.000000 (#rows))
11938987.2
120Inner table: STUDENT Alias: S
121Access Path: TableScan
122NL Join: Cost: 71.023399 Resp: 71.023399 Degree: 1
123Cost_io: 71.000000 Cost_cpu: 898826
124Resp_io: 71.000000 Resp_cpu: 898826
125****** Costing Index SYS_C0015206
126SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_SCAN
127SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_FILTER
128Access Path: index (UniqueScan)
129Index: SYS_C0015206
130resc_io: 1.000000 resc_cpu: 8381
131ix_sel: 0.076923 ix_sel_with_filters: 0.076923
132NL Join : Cost: 27.005870 Resp: 27.005870 Degree: 1
133Cost_io: 27.000000 Cost_cpu: 225499
134Resp_io: 27.000000 Resp_cpu: 225499
135****** Costing Index SYS_C0015206
136SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_SCAN
137SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_FILTER
138Access Path: index (AllEqUnique)
139Index: SYS_C0015206
140resc_io: 1.000000 resc_cpu: 8381
141ix_sel: 0.076923 ix_sel_with_filters: 0.076923
142NL Join : Cost: 27.005870 Resp: 27.005870 Degree: 1
143Cost_io: 27.000000 Cost_cpu: 225499
144Resp_io: 27.000000 Resp_cpu: 225499
145
146Best NL cost: 27.005870
147resc: 27.005870 resc_io: 27.000000 resc_cpu: 225499
148resp: 27.005870 resp_io: 27.000000 resc_cpu: 225499
149SPD: Return code in qosdDSDirSetup: NOCTX, estType = JOIN
150Join Card: 22.000000 = outer (22.000000) * inner (13.000000) * sel (0.076923)
151Join Card – Rounded: 22 Computed: 22.000000
152Outer table: TAKES Alias: T
153resc: 5.001070 card 22.000000 bytes: deg: 1 resp: 5.001070
154Inner table: STUDENT Alias: S
155resc: 5.000998 card: 13.000000 bytes: deg: 1 resp: 5.000998
156using dmeth: 2 #groups: 1
157SORT ressource Sort statistics
158Sort width: 118 Area size: 131072 Max Area size: 20971520
159Degree: 1
160Blocks to Sort: 1 Row size: 40 Total Rows: 22
161Initial runs: 1 Merge passes: 0 IO Cost / pass: 0
162Total IO sort cost: 0.000000 Total CPU sort cost: 38417643
163Total Temp space used: 0
164SORT ressource Sort statistics
165Sort width: 118 Area size: 131072 Max Area size: 20971520
166Degree: 1
167Blocks to Sort: 1 Row size: 38 Total Rows: 13
168Initial runs: 1 Merge passes: 0 IO Cost / pass: 0
169Total IO sort cost: 0.000000 Total CPU sort cost: 38415391
170Total Temp space used: 0
171SM join: Resc: 12.002240 Resp: 12.002240 [multiMatchCost=0.000000]
172SM Join
173SM cost: 12.002240
174resc: 12.002240 resc_io: 10.000000 resc_cpu: 76912478
175resp: 12.002240 resp_io: 10.000000 resp_cpu: 76912478
176Outer table: TAKES Alias: T
177resc: 5.001070 card 22.000000 bytes: deg: 1 resp: 5.001070
178Inner table: STUDENT Alias: S
179resc: 5.000998 card: 13.000000 bytes: deg: 1 resp: 5.000998
180using dmeth: 2 #groups: 1
181Cost per ptn: 0.015739 #ptns: 1
182hash_area: 124 (max=5120) buildfrag: 1 probefrag: 1 ppasses: 1
183Hash join: Resc: 10.017831 Resp: 10.017831 [multiMatchCost=0.000023]
184HA Join
185HA cost: 10.017831
186resc: 10.017831 resc_io: 10.000000 resc_cpu: 684944
187resp: 10.017831 resp_io: 10.000000 resp_cpu: 684944
188Join order aborted: cost > best plan cost
189***********************
190(newjo-stop-1) k:0, spcnt:0, perm:2, maxperm:2000
191
192*********************************
193Number of join permutations tried: 2
194*********************************
Posted in Oracle.

Leave a Reply