In spite of the SORT in the name, this operation does not necessarily sort its input, rather it only uses the buffering functionality of a «traditional» sort to store data in the PGA or UGA to reduce full table scans. Compare with SORT (…). See also Sadique's blog
Column object_type in plan_table often set to PROCEDURE and object_owner and object_type set to, for example, dbms_xplan and display.
CONCATENATION
3 - 3 (?)
The CONCATENATION operator is functionally equivalent to the UNION-ALL operator and was used in versions prior to 12c for or expansions. See also the use_concat hint.
Similar to TABLE ACCESS (FULL) but get requested values (attributes/columns) from an index rather than the table. Thus, the index is not used to locate rows. Compare with INDEX FULL SCAN.
Statement execution was started parallel, but Oracle then detected during optimization phase that the statement cannot be executed in parallel (for example because of involving a PL/SQL function that is not parallel enabled).
BATCHED indicates that the step retrieves a few rowids from an index and then tries to access the corresponding rows in order of the blocks so that the number of times that a block is read is minimized.
A passive operation: it passes its resultset to the parent operation. Jonathan Lewis says the VIEW operator identifies a separately optimized query block (in a possibly transformed SQL statement) whose result set might be completely built in memory before passing it to the parent operators. Compare with the plan operation FILTER.
Related to the SQL function XMLTable? Compare with JSONTABLE EVALUATION.
XPATH EVALUATION
This table was produced with raw data that was selected with the following select statement:
with sp as (
select
p.operation p_operation,
p.options p_options,
c.operation c_operation,
c.options c_options,
p.id p_id,
p.sql_id,
p.child_number,
c.sql_id c_sql_id, c.child_number c_child_number, c.parent_id c_parent_id,
rank() over (partition by c.sql_id, c.child_number, c.parent_id order by c.position) r_child
from
v$sql_plan p left join
v$sql_plan c on p.sql_id = c.sql_id and
p.child_number = c.child_number and
p.id = c.parent_id
/*
where
not (
( p.operation = 'FILTER' and p.options is null) or
( p.operation = 'UNION-ALL' and p.options is null) or
( p.operation = 'UNION ALL PUSHED PREDICATE' and p.options is null) or
( p.operation = 'SELECT STATEMENT' and p.options is null) or
( p.operation = 'LOAD AS SELECT' and p.options = '(CURSOR DURATION MEMORY)') or
( p.operation = 'LOAD TABLE CONVENTIONAL' and p.options is null) or
( p.operation = 'CREATE TABLE STATEMENT' and p.options is null)
)
*/
),
gr as (
select
p_operation,
p_options,
-- p_id,
-- sql_id,
child_number,
max(r_child) max_r_child
from
sp
group by
p_operation,
p_options,
p_id,
sql_id,
child_number
)
select
count(*) over (partition by p_operation, p_options) cnt,
p_operation,
p_options,
max_r_child
from
gr
group by
p_operation,
p_options,
max_r_child
order by
p_operation,
p_options;
Root operators
There are seven root operators for execution plans
The following matrix show which object types are accessed by which plan operations.
TABLE
CLUSTER
TABLE (TEMP)
INDEX (CLUSTER)
INDEX
INDEX (UNIQUE)
INDEX (BITMAP)
VIEW
VIEW (FIXED)
PROCEDURE
TABLE (FIXED)
SEQUENCE
REMOTE
TABLE ACCESS
FULL
X
X
X
TABLE ACCESS
BY GLOBAL INDEX ROWID
X
TABLE ACCESS
BY GLOBAL INDEX ROWID BATCHED
X
TABLE ACCESS
BY INDEX ROWID
X
X
X
TABLE ACCESS
BY INDEX ROWID BATCHED
X
X
X
TABLE ACCESS
BY LOCAL INDEX ROWID
X
TABLE ACCESS
BY LOCAL INDEX ROWID BATCHED
X
TABLE ACCESS
BY USER ROWID
X
X
TABLE ACCESS
SAMPLE
X
TABLE ACCESS
SAMPLE BY ROWID RANGE
X
TABLE ACCESS
CLUSTER
X
INDEX
FAST FULL SCAN
X
X
INDEX
FULL SCAN
X
X
X
INDEX
FULL SCAN (MIN/MAX)
X
X
INDEX
FULL SCAN DESCENDING
X
INDEX
RANGE SCAN
X
X
X
INDEX
RANGE SCAN (MIN/MAX)
X
X
INDEX
RANGE SCAN DESCENDING
X
X
INDEX
SAMPLE FAST FULL SCAN
X
X
INDEX
SKIP SCAN
X
X
X
INDEX
UNIQUE SCAN
X
X
BITMAP INDEX
FAST FULL SCAN
X
BITMAP INDEX
FULL SCAN
X
BITMAP INDEX
RANGE SCAN
X
BITMAP INDEX
SAMPLE FAST FULL SCAN
X
BITMAP INDEX
SINGLE VALUE
X
VIEW PUSHED PREDICATE
X
VIEW
X
X
COLLECTION ITERATOR
PICKLER FETCH
X
FIXED TABLE
FIXED INDEX
X
FIXED TABLE
FULL
X
REMOTE
X
SEQUENCE
X
This matrix was created on a Oracle version 19.22 database with the following statement:
select * from (
select
case when count(*) > 0 then 'X' end flg,
p.operation,
p.options,
p.object_type
from
( select operation, options, object_type from dba_hist_sql_plan union all
select operation, options, object_type from v$sql_plan
) p
where
p.object_type is not null
group by
p.operation,
p.options,
p.object_type
)
pivot (
any_value(flg) flg for
object_type in (
'TABLE' as "TABLE",
'CLUSTER' as "CLUSTER",
'TABLE (TEMP)' as "TABLE (TEMP)",
'INDEX (CLUSTER)' as "INDEX (CLUSTER)",
'INDEX' as "INDEX",
'INDEX (UNIQUE)' as "INDEX (UNIQUE)",
'INDEX (BITMAP)' as "INDEX (BITMAP)",
'VIEW' as "VIEW",
'VIEW (FIXED)' as "VIEW (FIXED)",
'PROCEDURE' as "PROCEDURE",
'TABLE (FIXED)' as "TABLE (FIXED)",
'SEQUENCE' as "SEQUENCE",
'REMOTE' as "REMOTE"
)
)
order by
case when operation = 'TABLE ACCESS' then
case when options = 'FULL' then '01'
when options = 'CLUSTER' then '03'
else '02'
end
when operation = 'INDEX' then '10'
when operation = 'BITMAP INDEX' then '20'
when operation like 'VIEW%' then '30'
else operation end,
options;
Find parent child operation relations
From v$sql_plan:
select
count(*) cnt,
p .operation, p .options,
c1.operation, c1.options,
c2.operation, c2.options,
c3.operation, c3.options
from
v$sql_plan p left join
v$sql_plan c1 on p.sql_id = c1.sql_id and p.child_number = c1.child_number and p.id = c1.parent_id and c1.position = 1 left join
v$sql_plan c2 on p.sql_id = c2.sql_id and p.child_number = c2.child_number and p.id = c2.parent_id and c2.position = 2 left join
v$sql_plan c3 on p.sql_id = c3.sql_id and p.child_number = c3.child_number and p.id = c3.parent_id and c3.position = 3
where
p.operation = 'PX BLOCK'
group by
p .operation, p .options,
c1.operation, c1.options,
c2.operation, c2.options,
c3.operation, c3.options
order by
p .operation, p .options,
c1.operation, c1.options,
c2.operation, c2.options,
c3.operation, c3.options
;
Same idea, but from dba_hist_sql_plan:
select
count(*) cnt,
p .operation, p .options,
c1.operation, c1.options,
c2.operation, c2.options,
c3.operation, c3.options
from
dba_hist_sql_plan p left join
dba_hist_sql_plan c1 on p.dbid = c1.dbid and p.plan_hash_value = c1.plan_hash_value and p.sql_id = c1.sql_id and p.id = c1.parent_id and c1.position = 1 left join
dba_hist_sql_plan c2 on p.dbid = c2.dbid and p.plan_hash_value = c2.plan_hash_value and p.sql_id = c2.sql_id and p.id = c2.parent_id and c2.position = 2 left join
dba_hist_sql_plan c3 on p.dbid = c3.dbid and p.plan_hash_value = c3.plan_hash_value and p.sql_id = c3.sql_id and p.id = c3.parent_id and c3.position = 3
where
p.operation = 'SELECT STATEMENT' -- and p.options = 'BY INDEX ROWID BATCHED'
group by
p .operation, p .options,
c1.operation, c1.options,
c2.operation, c2.options,
c3.operation, c3.options
order by
p .operation, p .options,
c1.operation, c1.options,
c2.operation, c2.options,
c3.operation, c3.options
;
Find entire plans where a given operation is present
with s as (
select distinct
sql_id,
child_number
from
v$sql_plan
where
operation = 'VIEW'
)
select
lpad(' ', 2*p.depth) || p.operation || ' ' || p.options,
p.object_name,
p.object_alias,
p.object_type,
t.sql_text,
count(*) over (partition by s.sql_id, s.child_number) cnt
from
s join
v$sql_plan p on s.sql_id = p.sql_id and
s.child_number = p.child_number join
v$sqlarea t on t.sql_id = p.sql_id
order by
cnt,
s.sql_id,
s.child_number,
p.id;
An execution plan operator may send multiple rows at once to its parent. In order to force the plan operators to send one row at at a time to their parents, the init parameter _rowsets_enabled can be set to false.