For qbregistry_graph, jump to Footnote 2.
If you look at the “Outline Data” from an execution plan it shows you a list of hints that will (in theory, at least) recreate the execution plan. It’s this information that gets stored as the “injection” part of an SQL Plan Baseline. Unfortunately the hints won’t necessarily allow you to infer what transformations the optimizer has used to get to the final execution plan.
If you’re prepared to generate the CBO trace file you could examine the Query Block Registry that appears near the end of the trace file to get some clues – here’s an example from 19.11.0.0 for a simple query involving a single table plus an IN subquery:
Query Block Registry:
SEL$2 0x6d47cde8 (PARSER)
SEL$5DA710D3 0x6d480e60 (SUBQUERY UNNEST SEL$1; SEL$2;)
SEL$2F1334C4 0x6d480e60 (SPLIT/MERGE QUERY BLOCKS SEL$5DA710D3) [FINAL]
SEL$683B0107 0x6d47cde8 (SUBQ INTO VIEW FOR COMPLEX UNNEST SEL$2)
SEL$B186933D 0x6d48e3a8 (VIEW MERGE SEL$88A77D12; SEL$683B0107; SEL$5DA710D3) [FINAL]
SEL$88A77D12 0x6d48e3a8 (PROJECTION VIEW FOR CVM SEL$683B0107)
SEL$B186933D 0x6d48e3a8 (VIEW MERGE SEL$88A77D12; SEL$683B0107; SEL$5DA710D3) [FINAL]
SEL$1 0x6d480e60 (PARSER)
SEL$5DA710D3 0x6d480e60 (SUBQUERY UNNEST SEL$1; SEL$2;)
I’m not going to say anything about interpreting this extract because I want to highlight a recent feature of the dbms_xplan package (brought to my attention by Franck Pachot some time ago). One of the format options for displaying execution plans will report the query block registry. Here’s the output from display_cursor(format=>’qbregistry’)) in 21.3.0.0 for the query that produced the CBO trace extract above:
Query Block Registry:
---------------------
SEL$1 (PARSER)
SEL$5DA710D3 (SUBQUERY UNNEST SEL$1 ; SEL$2)
SEL$2F1334C4 (SPLIT/MERGE QUERY BLOCKS SEL$5DA710D3) [FINAL]
SEL$2 (PARSER)
SEL$683B0107 (SUBQ INTO VIEW FOR COMPLEX UNNEST SEL$2)
SEL$88A77D12 (PROJECTION VIEW FOR CVM SEL$683B0107)
SEL$B186933D (VIEW MERGE SEL$88A77D12 ; SEL$683B0107) [FINAL]
Two things to notice here – first that the output has reduced the 9 lines to 7 lines (which can only be helpful). secondly that the redundant memory addresses which appear in the trace file don’t get copied into the report.
I’m still not going to say anything about interpreting the output because I want to show you the display_cursor() output for the same query when executed in 19.11.0..0. It looks like this:
Query Block Registry:
---------------------
<q o="13"><n><![CDATA[SEL$88A77D12]]></n><p><![CDATA[SEL$683B0107]]></p><
f><h><t><![CDATA[T1]]></t><s><![CDATA[SEL$1]]></s></h><h><t><![CDATA[VW_N
SO_1]]></t><s><![CDATA[SEL$5DA710D3]]></s></h></f></q>
<q o="12"><n><![CDATA[SEL$683B0107]]></n><p><![CDATA[SEL$2]]></p><f><h><t
><![CDATA[T2]]></t><s><![CDATA[SEL$2]]></s></h></f></q>
<q o="2"><n><![CDATA[SEL$1]]></n><f><h><t><![CDATA[T1]]></t><s><![CDATA[S
EL$1]]></s></h></f></q>
<q o="2"><n><![CDATA[SEL$2]]></n><f><h><t><![CDATA[T2]]></t><s><![CDATA[S
EL$2]]></s></h></f></q>
<q o="18" f="y" h="y"><n><![CDATA[SEL$B186933D]]></n><p><![CDATA[SEL$88A7
7D12]]></p><i><o><t>VW</t><v><![CDATA[SEL$683B0107]]></v></o></i><f><h><t
><![CDATA[T1]]></t><s><![CDATA[SEL$1]]></s></h><h><t><![CDATA[T2]]></t><s
><![CDATA[SEL$2]]></s></h></f></q>
<q o="19" h="y"><n><![CDATA[SEL$5DA710D3]]></n><p><![CDATA[SEL$1]]></p><i
><o><t>SQ</t><v><![CDATA[SEL$2]]></v></o></i><f><h><t><![CDATA[T1]]></t><
s><![CDATA[SEL$1]]></s></h><h><t><![CDATA[VW_NSO_1]]></t><s><![CDATA[SEL$
5DA710D3]]></s></h></f></q>
<q o="15" f="y"><n><![CDATA[SEL$2F1334C4]]></n><p><![CDATA[SEL$5DA710D3]]
></p><f><h><t><![CDATA[VM_NWVW_2]]></t><s><![CDATA[SEL$2F1334C4]]></s></h
></f></q>
Yes, it’s naked XML (extracted from the v$sql_plan.other_xml column for operation 1).
I had been living in hope that someone else would write a messy bit of SQL to translate this into something readable – but the last time I searched the Internet for “other_xml qbregistry” I got the magical result of a Googlewhack (i.e. only one hit), which was in Russian and largely a short description of all the legal inputs for the format option.
Since I’ve just installed 21.3 on a VM I decided to bite the bullet and write the code myself – but I’ve taken a short-cut. I’ve run a trace on a call to dbms_xplan.display_cursor() and extracted the critical query from the resulting trace file. So it only took about 30 minutes making it readable, hacking it to make it almost workable on 19c, then finding out why it can’t work without a little extra effort. Here’s the resulting hack:
rem
rem Script: qbregistry_query.sql
rem Author: Oracle Corp / Jonathan Lewis
rem Dated: Aug 2021
rem
rem Last tested
rem 19.11.0.0
rem
define m_sql_id='232sya6twg7sq'
define m_origin = 2
with
xml as (
select other_xml
from V$sql_plan
where sql_id = '&m_sql_id'
and id = 1
and other_xml is not null
),
allqbs as (
select
extractvalue(d.column_value, '/q/n') qbname,
extractvalue(d.column_value, '/q/@f') final,
extractvalue(d.column_value, '/q/p') prev,
extractvalue(d.column_value, '/q/@o') origin
from
table(xmlsequence(extract(xmltype ((select other_xml from xml)), '/other_xml/qb_registry/q'))) d
),
inpqbs as (
select
xml.qbname qbname,
listagg(xml.depqbs, ',') within group (
order by xml.depqbs) depqbs
from
xmltable('/other_xml/qb_registry/q/i/o' passing xmltype((select other_xml from xml))
columns depqbs varchar2(256) path 'v',
qbname varchar2(256) path './../../n'
) xml
where xml.depqbs in ( select qbname from allqbs)
group by xml.qbname
),
recqb (src, origin, dest, final, lvl, inpobjs) as (
select
qbname src, origin origin, null dest, final final, 1 lvl, null inpobjs
from
allqbs
where
-- origin = &m_origin
origin in (2,3)
union all
select
a.qbname src, a.origin origin, a.prev dest, a.final final, lvl+1,
(select depqbs from inpqbs i where i.qbname = a.qbname) inpobjs
from
allqbs a,
recqb r
where a.prev = r.src
)
search depth first by src asc set ordseq,
finalans as (
select
src,
/*
(
select
name
from v$query_block_origin
where
origin_id=origin
) origin,
*/
origin,
dest, final, lvl, inpobjs
from recqb order by ordseq
)
select
/*+ opt_param('parallel_execution_enabled', 'false') */
g.qbreg
from (
select
rpad(' ', 2*(lvl-1)) ||
src || ' (' || origin ||
case when length(dest)>0
then ' ' || dest
else ''
end ||
case when length(inpobjs)>0
then ' ; ' || inpobjs
else ' '
end ||
')' ||
case when final='y'
then ' [final]'
else ''
end
qbreg
from
finalans
) g
/
In lines 10 and 11 I’ve defined a couple of substitution variables that appear further on in the script. One is the SQL_ID for the query you’re interested in, the other is a fixed (probably) symbolic constant used by Oracle.
Lines 14-20 are a “with” subquery that I’ve prepended to Oracle’s internal code to create a single row, single column table holding the other_xml value of the query of interest. You’ll notice that I’ve been fairly casual about this bit since I haven’t catered for the fact that a single sql_id may have several child cursors and might even be obsolete.
Lines 28 and 36 are where I’ve used my “with” subquery to supply the other_xml value that would have appeared as a bind variable (:B1) in the trace file.
Line 49 (commented out for the reason described in footnote 1) uses the m_origin variable to identify a row in the dynamic performance view v$query_block_origin (highlighted at line 68) and that’s where we have a problem with Oracle 19c: the view doesn’t exist, nor does the x$qbname structure that the view is based on (although it’s easy to find a table of the values in the oracle executable – albeit that several items in the 21c list don’t appear in the 19.11 executable).
In the code above I’ve actually commented out the whole of the inline scalar subquery that translates an origin number into an origin name and reported the actual value of origin. Originally I did this to check whether it was worth spending any more working on the code – and this is the result I got from the initial test:
SEL$1 (2 )
SEL$5DA710D3 (19 SEL$1 ; SEL$2)
SEL$2F1334C4 (15 SEL$5DA710D3 ) [final]
SEL$2 (2 )
SEL$683B0107 (12 SEL$2 )
SEL$88A77D12 (13 SEL$683B0107 )
SEL$B186933D (18 SEL$88A77D12 ; SEL$683B0107) [final]
A quick check by eye shows that it’s got the same pattern and set of query block names that the 21c output produced so it’s clearly a step in the right direction. Now all I need is a way to translate the origin numbers into names.
I could have tried searching x$ksmfsv to see if I could spot a pointer to the relevant structure and fake my way through the whole process of creating a “nearly dynamic” performance view, but I decided the quick and dirty workaround was to dump a CSV file listing the view contents in 21c, then read the file back as an external table to copy the data into a local IOT (index organized table) called my_query_block_origin. With the inline view back in play – and the name suitably changed – the 19c and 21c queries produced the same result (which is slightly surprising as the “SUBQUERY UNNEST” and “VIEW MERGE” options don’t seem to exist in the 19.11 list I found in the oracle executable.)
Footnote 1
Here’s a query to show the content of that 21c view (which is fairly interesting in its own right):
set linesize 144
set pagesize 100
set trimspool on
set tabout off
column name format a60
column hint_token format a32
spool query_block_origin.lst
select
origin_id,
name,
hint_token
from
v$query_block_origin
/
ORIGIN_ID NAME HINT_TOKEN
---------- ------------------------------------------------------------ --------------------------------
0 NOT NAMED
1 ALLOCATE
2 PARSER
3 HINT
4 COPY
5 SAVE
6 MV REWRITE REWRITE
7 PUSHED PREDICATE PUSH_PRED
8 STAR TRANSFORM SUBQUERY
9 COMPLEX VIEW MERGE
10 COMPLEX SUBQUERY UNNEST
11 OR EXPANSION USE_CONCAT
12 SUBQ INTO VIEW FOR COMPLEX UNNEST
13 PROJECTION VIEW FOR CVM
14 GROUPING SET TO UNION
15 SPLIT/MERGE QUERY BLOCKS
16 COPY PARTITION VIEW
17 RESTORE
18 VIEW MERGE MERGE
19 SUBQUERY UNNEST UNNEST
20 STAR TRANSFORM STAR_TRANSFORMATION
21 INDEX JOIN
22 STAR TRANSFORM TEMP TABLE
23 MAP QUERY BLOCK
24 VIEW ADDED
25 SET QUERY BLOCK
26 QUERY BLOCK TABLES CHANGED
27 QUERY BLOCK SIGNATURE CHANGED
28 MV UNION QUERY BLOCK
29 SPLIT QUERY BLOCK FOR GSET-TO-UNION EXPAND_GSET_TO_UNION
30 PREDICATES REMOVED FROM QUERY BLOCK PULL_PRED
31 PREDICATES ADDED TO QUERY BLOCK
32 OLD PUSHED PREDICATE OLD_PUSH_PRED
33 ORDER BY REMOVED FROM QUERY BLOCK ELIMINATE_OBY
34 JOIN REMOVED FROM QUERY BLOCK ELIMINATE_JOIN
35 OUTER-JOIN REMOVED FROM QUERY BLOCK OUTER_JOIN_TO_INNER
36 STAR TRANSFORMATION JOINBACK ELIMINATION ELIMINATE_JOIN
37 BITMAP JOIN INDEX JOINBACK ELIMINATION ELIMINATE_JOIN
38 CONNECT BY COST BASED TRANSFORMATION CONNECT_BY_COST_BASED
39 CONNECT BY WITH FILTERING CONNECT_BY_FILTERING
40 CONNECT BY WITH NO FILTERING NO_CONNECT_BY_FILTERING
41 CONNECT BY START WITH QUERY BLOCK
42 CONNECT BY FULL SCAN QUERY BLOCK
43 PLACE GROUP BY PLACE_GROUP_BY
44 CONNECT BY NO FILTERING COMBINE NO_CONNECT_BY_FILTERING
45 VIEW ON SELECT DISTINCT
46 COALESCED SUBQUERY COALESCE_SQ
47 QUERY HAS COALESCED SUBQUERIES COALESCE_SQ
48 SPLIT QUERY BLOCK FOR DISTINCT AGG OPTIM TRANSFORM_DISTINCT_AGG
49 CONNECT BY ELIMINATE DUPLICATES FROM INPUT CONNECT_BY_ELIM_DUPS
50 CONNECT BY COST BASED TRANSFORMATION FOR WHR ONLY CONNECT_BY_CB_WHR_ONLY
51 TABLE EXPANSION EXPAND_TABLE
52 TABLE EXPANSION BRANCH
53 JOIN FACTORIZATION SET QUERY BLOCK FACTORIZE_JOIN
54 DISTINCT PLACEMENT PLACE_DISTINCT
55 JOIN FACTORIZATION BRANCH QUERY BLOCK
56 TABLE LOOKUP BY NESTED LOOP QUERY BLOCK TABLE_LOOKUP_BY_NL
57 FULL OUTER JOIN TRANSFORMED TO OUTER FULL_OUTER_JOIN_TO_OUTER
58 LEFT OUTER JOIN TRANSFORMED TO ANTI OUTER_JOIN_TO_ANTI
59 VIEW DECORRELATED DECORRELATE
60 QUERY VIEW DECORRELATED DECORRELATE
61 NOT EXISTS SQ ADDED
62 BRANCH WITH OUTER JOIN
63 BRANCH WITH ANTI JOIN
64 UNION ALL FOR FULL OUTER JOIN
65 VECTOR TRANSFORMATION VECTOR_TRANSFORM
66 VECTOR TRANSFORMATION TEMP TABLE
67 QUERY ANSI REARCHiTECTURE ANSI_REARCH
68 VIEW ANSI REARCHiTECTURE ANSI_REARCH
69 ELIMINATION OF GROUP BY ELIM_GROUPBY
70 UAL BRANCH OF UNNESTED SUBQUERY
71 QUERY BLOCK HAS BUSHY JOIN BUSHY_JOIN
72 SUBQUERY ELIMINATE ELIMINATE_SQ
73 OR EXPANSION UNION ALL BRANCH
74 OR EXPANSION UNION ALL VIEW OR_EXPAND
75 DIST AGG GROUPING SETS UNION ALL TRANSFORMATION USE_DAGG_UNION_ALL_GSETS
76 MATERIALIZED WITH CLAUSE
77 STATISTCS BASED TRANSFORMED QB
78 PQ TABLE EXPANSION
79 LEFT OUTER JOIN TRANSFORMED TO BOTH INNER AND ANTI
80 SHARD TEMP TABLE
81 BRANCH OF COMPLEX UNNESTED SET QUERY BLOCK
82 DIST AGG GROUPING SETS OPTIMIZATION DAGG_OPTIM_GSETS
You’ll notice the highlight for origin_id 2 which has the name PARSER – that’s the (first) significant value when reporting the query block registry but take note, also, of origin_id 3 which has the name hint. This is where the code built into 21c goes wrong. If you use the qb_name hint to name all your query blocks then their origin_id will be 3, and Oracle’s code won’t find them.
When I added the hint /*+ qb_name(main) */ to the query this is what I got from my registry query:
Query Block Registry:
---------------------
SEL$1 (PARSER)
SEL$7D4DB4AA (SUBQ INTO VIEW FOR COMPLEX UNNEST SEL$1)
SEL$EFD91A2C (PROJECTION VIEW FOR CVM SEL$7D4DB4AA)
SEL$7086F02E (VIEW MERGE SEL$EFD91A2C ; SEL$7D4DB4AA) [FINAL]
And when I also added the hint /*+ qb_name(subq) */ to the subquery the result was this:
Query Block Registry:
---------------------
An uncaught error happened in display_cursor : ORA-06502: PL/SQL: numeric or value error
I’ve said for a long time: “always name all your query blocks”. I think 21c (temporarily) demonstrates why you have two options: name ALL of them or name NONE of them. If you name just some of them you might not notice that parts of your plan don’t appear in the qbregistry report, and I’d say it’s better to see an error than to be fooled into thinking you’ve got complete information.
Footnote 2
There’s another new option for the format parameter in 21c which is qbregistry_graph. I haven’t considered playing about with the trace file to see if I can extract and hack the SQL that generates the appropriate output (but that might change if I pick up a tool to turn the textual description into a graphic). For the registry listing above this is what the “graph” output looks like:
Query Block Registry Graph (dot format):
---------------------
digraph g{
rankdir = TB
"SEL$88A77D12"
"SEL$683B0107"
"SEL$1"
"SEL$2"
"SEL$B186933D" [peripheries=2]
"SEL$5DA710D3"
"SEL$2F1334C4" [peripheries=2]
"SEL$683B0107" -> "SEL$88A77D12" [label="PROJECTION VIEW FOR CVM"]
"SEL$2" -> "SEL$683B0107" [label="SUBQ INTO VIEW FOR COMPLEX UNNEST"]
"SEL$88A77D12" -> "SEL$B186933D" [label="VIEW MERGE"]
"SEL$1" -> "SEL$5DA710D3" [label="SUBQUERY UNNEST"]
"SEL$5DA710D3" -> "SEL$2F1334C4" [label="SPLIT/MERGE QUERY BLOCKS"]
"SEL$683B0107" -> "SEL$B186933D" [style=dotted]
"SEL$2" -> "SEL$5DA710D3" [style=dotted]
{ rank = same }
{
rank="sink";
rankdir = LR;
item1 [style=invis];
item2 [shape="plaintext" label="Participating query blocks"];
item3 [label=" " peripheries=2];
item4 [shape="plaintext" label="Final query blocks"];
item1 -> item2 [style=dotted];
{ rank=same item3 item4; }
}
}
Footnote 2.1 (a few days later)
A couple of days after publishing this note I received an email pointing out that the qbregistry_graph output is in the Graphviz DOT language (see http://www.graphviz.org/) and there are even websites where it can easily be rendered into graphic form. This is the result I got by pasting the output into the website:
I haven’t tried to think through a generalised pattern for drawing these pictures, but I think I’d prefer to see a diagram which showed that the “final” query block sel$2f1334c4 was used by query block sel$b186933d. After all, the word “final” in this context means only that the query block was one for which the optimizer produced an independent (sub-)plan (i.e. an outline_leaf).
Footnote 3
For completeness – here’s the original SQL and plan for the statement that produced this qbregistry example:
select
*
from t1
where owner = 'OUTLN'
and object_name in (
select /*+
unnest
no_semijoin
*/
distinct object_name
from t2
where object_type = 'TABLE'
)
;
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 5 (100)| |
| 1 | VIEW | VM_NWVW_2 | 1 | 483 | 5 (20)| 00:00:01 |
| 2 | HASH UNIQUE | | 1 | 155 | 5 (20)| 00:00:01 |
| 3 | NESTED LOOPS SEMI | | 1 | 155 | 4 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 1 | 128 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | T1_I1 | 1 | | 1 (0)| 00:00:01 |
|* 6 | TABLE ACCESS BY INDEX ROWID BATCHED| T2 | 1 | 27 | 2 (0)| 00:00:01 |
|* 7 | INDEX RANGE SCAN | T2_I2 | 48 | | 0 (0)| |
----------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$B186933D / "VM_NWVW_2"@"SEL$2F1334C4"
2 - SEL$B186933D
4 - SEL$B186933D / "T1"@"SEL$1"
5 - SEL$B186933D / "T1"@"SEL$1"
6 - SEL$B186933D / "T2"@"SEL$2"
7 - SEL$B186933D / "T2"@"SEL$2"
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('21.1.0')
DB_VERSION('21.1.0')
ALL_ROWS
OUTLINE_LEAF(@"SEL$B186933D")
MERGE(@"SEL$683B0107" >"SEL$5DA710D3")
OUTLINE_LEAF(@"SEL$2F1334C4")
OUTLINE(@"SEL$88A77D12")
OUTLINE(@"SEL$683B0107")
OUTLINE(@"SEL$5DA710D3")
UNNEST(@"SEL$2" UNNEST_INNERJ_DISTINCT_VIEW)
OUTLINE(@"SEL$2")
OUTLINE(@"SEL$1")
NO_ACCESS(@"SEL$2F1334C4" "VM_NWVW_2"@"SEL$2F1334C4")
INDEX_RS_ASC(@"SEL$B186933D" "T1"@"SEL$1" ("T1"."OWNER"))
BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$B186933D" "T1"@"SEL$1")
INDEX_RS_ASC(@"SEL$B186933D" "T2"@"SEL$2" ("T2"."OBJECT_TYPE"))
BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$B186933D" "T2"@"SEL$2")
LEADING(@"SEL$B186933D" "T1"@"SEL$1" "T2"@"SEL$2")
USE_NL(@"SEL$B186933D" "T2"@"SEL$2")
USE_HASH_AGGREGATION(@"SEL$B186933D" UNIQUE)
PARTIAL_JOIN(@"SEL$B186933D" "T2"@"SEL$2")
END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("OWNER"='OUTLN')
6 - filter("OBJECT_NAME"="OBJECT_NAME")
7 - access("OBJECT_TYPE"='TABLE')
Query Block Registry:
---------------------
SEL$1 (PARSER)
SEL$5DA710D3 (SUBQUERY UNNEST SEL$1 ; SEL$2)
SEL$2F1334C4 (SPLIT/MERGE QUERY BLOCKS SEL$5DA710D3) [FINAL]
SEL$2 (PARSER)
SEL$683B0107 (SUBQ INTO VIEW FOR COMPLEX UNNEST SEL$2)
SEL$88A77D12 (PROJECTION VIEW FOR CVM SEL$683B0107)
SEL$B186933D (VIEW MERGE SEL$88A77D12 ; SEL$683B0107) [FINAL]
Tables t1 and t2 are copies of the same data set, which is a subset of 100 rows from all_objects. You won’t necessarily see this plan on your systems because (even with the hints) the plan can vary depending on the number of rows with owner = ‘OUTLN’ (which is likely to be zero) or with object_type = ‘TABLE’ (which might be all of them). The script I started with was one I had used in a note I wrote about “distinct” appearing in the select list of subqueries, but the data it produced in the newer versions of Oracle was sufficiently different that I had to be a little more careful in constructing a data set that produced stable plans.
If you cross-check the Query Block Registry with the Outline Data you’ll see that the lines labelled FINAL start with the query block names that are shown as “outline_leaf” entries, and the other 5 query block names appear as “outline” entries.
Reading down the tree I then find myself struggling to interpret the QBR. I think I know what has happened, but I can’t quite manage to see exactly how the QBR is telling me that it happened.
QBR – tentative interpretation
Part of the difficulty is that the QBR seems to have a section for every initial query block in the query, so there’s (a) likely to be some overlap between sections and (b) some sequencing that means you can’t get the full picture just by reading straight from top to bottom. In this case we have two initial query blocks (the main query, implicitly named sel$1, and the subquery implicitly named sel$2), and I think the interpretation is as follows:
Starting with sel$2 section we can see that its second line tells us that the subquery was unnested and the resulting aggregate inline view is the sole content of a query block called sel$683B0107.
Jumping backwards to the sel$1 section, its second line tells us that sel$5DA710D3 is a query block consisting of a join between t1 and the inline aggregate view.
Sticking with the sel$1 tree, we then see a query block that tells us that the optimizer has transformed an “aggregate then join” into a “join then aggregate”. sel$2F1334C4 is the query block holding nothing but a select from the view VM_VWNW_2.
Returning to the sel$2 tree, sel$88A77D12 is the resulting query block when the inline aggregate view is merged using complex view merging. This is where I get a bit stuck, because this seems to be repeating a step that we’ve handled in the sel$1 section by a different route.
The final step of the sel$2 tree is sel$B186933D the query block where we select from the non-mergeable inline view VM_VWNW_2 that seems to have come from one of two different places.
Bottom line on this one: even though it’s an extremely simple query and I believe I understand what the execution plan is telling us about the transformations that took place, the query block registry is still something of a mystery to me.
[…] question (very similar to one I had been asking myself) that appeared on twitter in response to my original posting on the new qbregistry format option in calls to dbms_xplan, I’ve drafted a note of how I […]
Pingback by qbregistry 2 | Oracle Scratchpad — August 25, 2021 @ 1:45 pm BST Aug 25,2021 |
[…] just discovered the qbregistry and qbregisrty_graph options for the format command in dbms_xplan.display_cursor() of 21c I couldn’t resist […]
Pingback by ANSI hinting | Oracle Scratchpad — September 18, 2021 @ 3:22 pm BST Sep 18,2021 |
[…] And here’s the plan, pulled from memory with a call to dbms_xplan.display_cursor() with /*+ ordered hint */ in place. I’ve included the outline information, hint report and (since this is from 21c) the query block registry: […]
Pingback by Ordered hint | Oracle Scratchpad — January 29, 2022 @ 5:27 pm GMT Jan 29,2022 |
[…] Part 1 of a pair of articles examining the qb_registry option of dbms_xplan.display_xxx() (Aug 2021) […]
Pingback by Execution Plans Catalogue | Oracle Scratchpad — January 29, 2022 @ 5:58 pm GMT Jan 29,2022 |
[…] this case it’s just a couple of headline details about the optimizer environment and the query block registry. When I populated my table this value ended up in the row with id = 1383, and I’ll be using […]
Pingback by xmltype | Oracle Scratchpad — August 8, 2023 @ 8:13 am BST Aug 8,2023 |
[…] the two subqueries (and the main query block) but then turned those hints into comments because the qbregistry_graph format I’d used in my call to dbms_xplan can’t cope with user-supplied query block […]
Pingback by Push Group by | Oracle Scratchpad — November 1, 2023 @ 10:40 am GMT Nov 1,2023 |
[…] проблемный Query Block SEL$D59007A2 (*), что показывает отличный скрипт by Jonathan Lewis (адаптированный для версии Oracle […]
Pingback by Анализ выполнений стандартного concurrent request | Oracle mechanics — December 17, 2023 @ 7:02 pm GMT Dec 17,2023 |
[…] Transformations, основанный на сравнении секций Query Block Registry (qbregistry_query.sql by Jonathan Lewis), и получить в наблюдаемом случае простой […]
Pingback by Oracle 19: Сравнение Query Block Registry | Oracle mechanics — February 13, 2024 @ 9:37 pm GMT Feb 13,2024 |