13 December, 2015

Trace Files -- 10 : Tracing DML

So far, all the examples of SQL Tracing that I have provided in this series are for SELECT statements.  (By "DML" I mean INSERT / UPDATE / DELETE statements other than SELECT statements)

Examples of the tkprof outputs are :

select count(*)
from
 all_objects_many_list where owner = 'HEMANT'


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      1.05      44.33      97793      97798          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      1.06      44.34      97793      97798          0           1


select /* second_run */ count(*)
from
 all_objects_many_list where owner = 'HEMANT'


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.96      42.35      97792      97797          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.96      42.35      97792      97797          0           1



select count(*)
from
 all_objects_short_list where owner = 'HEMANT'


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.10          2          2          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.09        140        580          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.01       0.20        142        582          0           1


select /* second_run */ count(*)
from
 all_objects_short_list where owner = 'HEMANT'


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          1          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          0        580          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.00          0        581          0           1


select min(created)
from
 all_objects_many_list


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.01          3          3          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.02          3          3          0           1


In all these cases the statistics (disk reads and query {i.e. consistent} gets) are reported against the FETCH call, not the EXECUTE call.  ("rows" is 1 in all the above cases because we are always retrieving a single value, a count or a min value).



Let's see what happens when we execute an UPDATE (which does not update an index entries).

update all_objects_short_list
set owner = 'HKC'
where owner = 'HEMANT'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.01          0          0          0           0
Execute      1      0.01       0.13        577        579          4           3
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.03       0.15        577        579          4           3

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 87
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         0          0          0  UPDATE  ALL_OBJECTS_SHORT_LIST (cr=579 pr=577 pw=0 time=136182 us)
         3          3          3   TABLE ACCESS FULL ALL_OBJECTS_SHORT_LIST (cr=579 pr=577 pw=0 time=135859 us cost=158 size=24 card=3)


Here, the UPDATE required a Full Table Scan as we can see in the Row Source Operations.  This required 579 consistent gets ("cr" in Row Source Operations and "query" in the Statistics).  However, the difference we now see is that the Statistics are reported against the *EXECUTE* Phase, not the FETCH phase.
The actual retrieval of blocks for the UPDATE (all the blocks since no index was used) is not reported as FETCH call statistics but EXECUTE call statistics.  Thereafter, we see 4 blocks retrieved in "current" mode for the 3 rows that were updated.   Does the trace file show which 4 blocks were retrieved in "current" mode ?  Unfortunately, no it does not.  The SQL Trace file only shows wait events if blocks were retrieved from disk but it doesn't identify and differentiate "consistent" and "current" gets.  That is why even the Row Source Operations reports "cr" being "consistent reads".

Another thing to note is that Oracle doesn't report any physical writes ("pw=0") because disk writes for normal DML (other than Direct Path INSERTs) is deferred to be executed by DBWR later.  So, the process doing the UPDATE does not necessarily have to do writes.

(What are "current" gets ?  "Current" gets are when Oracle needs to retrieve the most current version of a block because it needs to update the block).

Let my try another UPDATE.  This time of a single row and where the UPDATE has to update an Indexed column.

update all_objects_many_list
set created=sysdate
where rowid =
(select rowid from all_objects_many_list where rownum < 2)

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.02          0          0          0           0
Execute      1      0.00       0.01          0          4          8           1
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.00       0.03          0          4          8           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 87
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         0          0          0  UPDATE  ALL_OBJECTS_MANY_LIST (cr=4 pr=0 pw=0 time=434 us)
         1          1          1   TABLE ACCESS BY USER ROWID ALL_OBJECTS_MANY_LIST (cr=4 pr=0 pw=0 time=100 us cost=1 size=20 card=1)
         1          1          1    COUNT STOPKEY (cr=3 pr=0 pw=0 time=77 us)
         1          1          1     TABLE ACCESS FULL ALL_OBJECTS_MANY_LIST (cr=3 pr=0 pw=0 time=60 us cost=26777 size=86375424 card=7197952)


(Note : Let's put aside the high COST of 26777 and expected Row Cardinality of 7197952 for the Full Table Scan in the Row Source Operations --- that is the subject of another discussion, which you could see here).
Here we see that the query does not have to do Full Table Scan.  There are 4 blocks fetched in "consistent get" mode (reported in "query") and 8 blocks in "current get" mode.
So, here we have a single row being updated, so we'd have a table block updated and two updates to index leaf block(s) (an UPDATE to an index is actual executed as a "soft-delete" of the previous entry and an insert of the new entry).  Yet, there are 8 blocks in "current" mode.  When updating an Index, Oracle fetches the Root, Branch and Leaf Blocks in "current" mode because it doesn't know in advance if the UPDATE will result in Leaf Block Splits (search my blog for posts on 90-10 and 50-50 splits for an explanation).

Note also that the Row Source Operations or the Trace File will *not* show which Index(es) needed to be updated.

Remember also that when we execute DML, Oracle also has to update one or more Undo Blocks besides the Table and Index blocks.


Let me try a DELETE.

delete all_objects_short_list
where owner = 'HKC'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0        579          4           3
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.00       0.00          0        579          4           3

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 87
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         0          0          0  DELETE  ALL_OBJECTS_SHORT_LIST (cr=579 pr=0 pw=0 time=2448 us)
         3          3          3   TABLE ACCESS FULL ALL_OBJECTS_SHORT_LIST (cr=579 pr=0 pw=0 time=2195 us cost=158 size=24 card=3)



Here, again we see the statistics being reported against the EXECUTE call.


Takeaway :  Simple SELECT reports statistics under FETCH calls.  INSERT / UPDATE / DELETE operations report statistics under EXECUTE calls.
.
.
.



No comments: