March 16, 2011 (Updated August 11, 2011)
(Back to the Previous Post in the Series) (Forward to the Next Post in the Series)
In the previous articles in this series we looked at ways to analyze the Oracle time model data at the system-wide level with drill-down into the session level detail, with cross references to a handful of statistics found in V$OSSTAT and V$SYSSTAT, and the system-wide wait events with drill-down into the session level wait event detail. There is a chance that some of the statistics might appear to be inconsistent between the various performance views, or possibly even between the start of the retrieval of the rows from a single performance view and the retrieval of the final row from the query of that performance view. You might even find in some cases, with some combinations of operating system platform and Oracle release version that the statistics in V$OSSTAT might not be in the unit of measure described in the Oracle Database documentation – a recent thread on the OTN forums contains an example of such a case (I completely overlooked the inconsistency that was pointed out in that thread). Is there a reason for the inconsistency? A quick browse through Metalink (MOS) finds the following articles:
Bug 7430365: INCORRECT VALUES FOR USER_TIME IN V$OSSTAT (3.79 hours per CPU per elapsed hour)
Bug 3574504: INCORRECT STATISTICS IN V$OSSTAT IN HP-UX
Bug 5933195: NUM_CPUS VALUE IN V$OSSTAT IS WRONG
Bug 5639749: CPU_COUNT NOT SHOWN PROPERLY FROM THE DATABASE
Bug 10427553: HOW DOES V$OSSTAT GET IT’S INFORMATION ON AIX
Bug 9228541: CPU TIME REPORTED INCORRECTLY IN V$SYSMETRIC_HISTORY (3.75 hours per CPU per elapsed hour)
Doc ID 889396.1: Very large value for OS_CPU_WAIT_TIME FROM V$OSSTAT / AWR Report
Bug 7447648: OS_CPU_WAIT_TIME VALUE FROM V$OSSTAT IS INCORRECT
At the end of the previous article, we had produced a demonstration project that generated screen output similar to the following (note that the project code as of the end of part four in the series may be downloaded by using the link at the end of the fourth article):

Let’s continue adding features to the project. We will start by adding three new CommandButtons to the UserForm with the (Name) property set to the following (one name per CommandButton): cmdTraceSession, cmdStopTrace, and cmdShowExecutionPlan. Assign useful titles to the CommandButtons by setting appropriate values for the Caption property – due to limited available space on the UserForm I selected the Captions: Trace, Stop Trace, and XPLAN. The UserForm in my sample project currently appears as shown in the following screen capture:

Next, we need to add a little more code to the Initialize event of the UserForm to add the SQL statements that will allow the CommandButtons to function. Double-click in the background area of the UserForm to display the Initialize event code for the UserForm.
Scroll down through the code until you find the following comment section:
'More code will be copied here
'
'
'
Add a couple of blank lines above that comment section and paste in the following code:
Set snpSQLStats = New ADODB.Recordset
Set comSQLStats = New ADODB.Command
With comTrace
strSQL = "DBMS_MONITOR.SESSION_TRACE_ENABLE(?,?,TRUE,TRUE)"
.CommandText = strSQL
.CommandType = adCmdStoredProc
.ActiveConnection = dbDatabase
End With
With comXPLAN
strSQL = "SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR( ?, ?, 'TYPICAL +PEEKED_BINDS'))"
'Add the bind variables
.Parameters.Append .CreateParameter("sqlid", adVarChar, adParamInput, 40, "")
.Parameters.Append .CreateParameter("childnumber", adNumeric, adParamInput, 8, 0)
.CommandText = strSQL
.CommandType = adCmdText
.CommandTimeout = 30
.ActiveConnection = dbDatabase
End With
With comSQLChildReason
strSQL = "SELECT" & vbCrLf
strSQL = strSQL & " SSC.*" & vbCrLf
strSQL = strSQL & "FROM" & vbCrLf
strSQL = strSQL & " V$SQL_SHARED_CURSOR SSC" & vbCrLf
strSQL = strSQL & "WHERE" & vbCrLf
strSQL = strSQL & " SSC.SQL_ID= ?" & vbCrLf
strSQL = strSQL & " AND SSC.CHILD_NUMBER= ?" & vbCrLf
'Set up the command properties
.CommandText = strSQL
.CommandType = adCmdText
.CommandTimeout = 30
.ActiveConnection = dbDatabase
'Add the bind variables
.Parameters.Append .CreateParameter("sqlid", adVarChar, adParamInput, 40, "")
.Parameters.Append .CreateParameter("childnumber", adNumeric, adParamInput, 8, 0)
End With
With comSQLChildBind
strSQL = "SELECT" & vbCrLf
strSQL = strSQL & " SBM.POSITION," & vbCrLf
strSQL = strSQL & " SBM.DATATYPE," & vbCrLf
strSQL = strSQL & " SBM.MAX_LENGTH," & vbCrLf
strSQL = strSQL & " SBM.BIND_NAME" & vbCrLf
strSQL = strSQL & "FROM" & vbCrLf
strSQL = strSQL & " V$SQL S," & vbCrLf
strSQL = strSQL & " V$SQL_BIND_METADATA SBM" & vbCrLf
strSQL = strSQL & "WHERE" & vbCrLf
strSQL = strSQL & " S.SQL_ID= ?" & vbCrLf
strSQL = strSQL & " AND S.CHILD_NUMBER= ?" & vbCrLf
strSQL = strSQL & " AND S.CHILD_ADDRESS=SBM.ADDRESS" & vbCrLf
strSQL = strSQL & "ORDER BY" & vbCrLf
strSQL = strSQL & " SBM.POSITION" & vbCrLf
'Set up the command properties
.CommandText = strSQL
.CommandType = adCmdText
.CommandTimeout = 30
.ActiveConnection = dbDatabase
'Add the bind variables
.Parameters.Append .CreateParameter("sqlid", adVarChar, adParamInput, 40, "")
.Parameters.Append .CreateParameter("childnumber", adNumeric, adParamInput, 8, 0)
End With
Set snpSQLStats = New ADODB.Recordset
Set comSQLStats = New ADODB.Command
With comSQLStats
strSQL = "SELECT" & vbCrLf
strSQL = strSQL & " LAST_ACTIVE_TIME," & vbCrLf
strSQL = strSQL & " PARSE_CALLS," & vbCrLf
strSQL = strSQL & " EXECUTIONS," & vbCrLf
strSQL = strSQL & " CPU_TIME," & vbCrLf
strSQL = strSQL & " ELAPSED_TIME," & vbCrLf
strSQL = strSQL & " APPLICATION_WAIT_TIME," & vbCrLf
strSQL = strSQL & " CONCURRENCY_WAIT_TIME," & vbCrLf
strSQL = strSQL & " CLUSTER_WAIT_TIME," & vbCrLf
strSQL = strSQL & " USER_IO_WAIT_TIME," & vbCrLf
strSQL = strSQL & " PLSQL_EXEC_TIME," & vbCrLf
strSQL = strSQL & " JAVA_EXEC_TIME," & vbCrLf
strSQL = strSQL & " BUFFER_GETS," & vbCrLf
strSQL = strSQL & " DISK_READS," & vbCrLf
strSQL = strSQL & " DIRECT_WRITES," & vbCrLf
strSQL = strSQL & " ROWS_PROCESSED," & vbCrLf
strSQL = strSQL & " FETCHES," & vbCrLf
strSQL = strSQL & " LOADS," & vbCrLf
strSQL = strSQL & " INVALIDATIONS" & vbCrLf
strSQL = strSQL & "FROM" & vbCrLf
strSQL = strSQL & " V$SQLSTATS" & vbCrLf
strSQL = strSQL & "WHERE" & vbCrLf
strSQL = strSQL & " SQL_ID= ?" & vbCrLf
strSQL = strSQL & " AND PLAN_HASH_VALUE= ?"
'Set up the command properties
.CommandText = strSQL
.CommandType = adCmdText
.CommandTimeout = 30
.ActiveConnection = dbDatabase
'Add the bind variables
.Parameters.Append .CreateParameter("sqlid", adVarChar, adParamInput, 40, "")
.Parameters.Append .CreateParameter("plan_hash_value", adNumeric, adParamInput, 8, 0)
End With
In the above, you might have noticed that I neglected to set up the two bind variables for the comTrace object (an object of type ADODB.Command), while I did set up the two bind variables for the comXPLAN object. The reason for this omission is that in previous testing I simply could not make the SQL code execute with bind variables, so I cheated a bit, and will simply replace the CommandText property value for the comTrace object with hardcoded values for the session’s SID and SERIAL# when the cmdTraceSession CommandButton is clicked.
View the UserForm again (find frmTimeModel under the Forms heading, right-click the UserForm’s name, and select View Object). Then double-click the cmdTraceSession CommandButton to show the Click event for that object. Change the Click event so that it includes the following code:
Private Sub cmdTraceSession_Click()
Dim i As Integer
Dim intSessionTrace As Integer
Dim strSQL As String
If intCurrentSessionIndex > -1 Then
strSQL = "DBMS_MONITOR.SESSION_TRACE_ENABLE(" & Format(lngSID(intCurrentSessionIndex)) & "," & Format(lngSerial(intCurrentSessionIndex)) & " ,TRUE,TRUE)"
comTrace.CommandText = strSQL
comTrace.Execute
End If
'Remember that we enabled trace for this session
intSessionTrace = 0
For i = 1 To 1000
If SessionTrace(i).lngSID = lngSID(intCurrentSessionIndex) Then
intSessionTrace = i
Exit For
End If
Next i
If intSessionTrace = 0 Then
'Find an unused entry
For i = 1 To 1000
If SessionTrace(i).lngSID = 0 Then
intSessionTrace = i
SessionTrace(i).lngSID = lngSID(intCurrentSessionIndex)
SessionTrace(i).lngSerial = lngSerial(intCurrentSessionIndex)
Exit For
End If
Next i
End If
SessionTrace(intSessionTrace).int10046Level = 12
cmdStopTrace.Enabled = True
cmdTraceSession.Enabled = False
tvTimeModel.SetFocus
End Sub
If you examine the code, you will see that we use the intCurrentSessionIndex variable’s value to know which session is the session of interest, and then set the SessionTrace object’s int10046Level property to 12 to indicate that we have enabled a level 12 trace for the session (this allows us to remember which sessions are being traced). But, we have a couple of problems: the intCurrentSessionIndex variable has a default value of 0 and so far we have not added code to assign a value to that variable; secondly, the SessionTrace object simply does not exist. Adding the SessionTrace object is easy, so we will start there. Scroll all the way up to the top of the project’s code and add the following code above everything else:
Private Type TraceDefinition
lngSID As Long
lngSerial As Long
int10046Level As Integer
int10053Level As Integer
int10032Level As Integer
int10033Level As Integer
int10104Level As Integer
End Type
Dim SessionTrace(1000) As TraceDefinition
The first of the above sections creates the definition of the object type TraceDefinition which is simply composed of seven variables.
While in that section of the project code, add a couple of more variable declarations that will be used later (first scroll down to where you see similar variable declarations):
Dim snpSQLStats As ADODB.Recordset 'ADO recordset object used to retrieve the statistics for the execution plan
Dim comSQLStats As ADODB.Command 'ADO command object used to retrieve the statistics for the execution plan
The second section (beginning with the word Dim) creates an array of 1,000 (actually 1,001) objects of type TraceDefinition.
Now for the hard part – how do we know which session is the current session – the one that is currently highlighted in the TreeView control? The key to this is in how we named the TreeView rows (by setting the Key property) as the rows were added in the UpdateDisplay subroutine. Each row that contains session-level detail has a Key property that begins with the value SESSION, so we are able to easily determine when a session-level detail row is clicked. Also part of the Key property is an underscore ( _ ) character that acts as a field delimiter between the rest of the name and a sequential number that points to additional information about that session. View the UserForm object again and double-click the TreeView control. Switch to the NodeClick event for the TreeView control (in the drop-down list at the right of the code window) and change the NodeClick event’s code to show the following:
Private Sub tvTimeModel_NodeClick(ByVal Node As MSComctlLib.Node)
Dim i As Integer
Dim intSessionTrace As Integer
If Left(Node.Key, 7) = "SESSION" Then
intCurrentSessionIndex = Right(Node.Key, Len(Node.Key) - InStr(Node.Key, "_"))
intSessionTrace = 0
For i = 1 To 1000
If (SessionTrace(i).lngSID = lngSID(intCurrentSessionIndex)) And (SessionTrace(i).lngSerial = lngSerial(intCurrentSessionIndex)) Then
intSessionTrace = i
Exit For
End If
Next i
If intSessionTrace > 0 Then
If SessionTrace(intSessionTrace).int10046Level > 0 Then
cmdTraceSession.Enabled = False
cmdStopTrace.Enabled = True
Else
cmdTraceSession.Enabled = True
cmdStopTrace.Enabled = False
End If
Else
cmdTraceSession.Enabled = True
cmdStopTrace.Enabled = False
End If
If strSQLID(intCurrentSessionIndex) <> "" Then
cmdShowExecutionPlan.Enabled = True
Else
cmdShowExecutionPlan.Enabled = False
End If
Else
intCurrentSessionIndex = -1
cmdTraceSession.Enabled = False
cmdStopTrace.Enabled = False
cmdShowExecutionPlan.Enabled = False
End If
End Sub
Easy to understand so far? Let’s add the code to the cmdStopTrace CommandButton. View the UserForm and then double-click the cmdStopTrace CommandButton. Change the Click event’s code to show the following:
Private Sub cmdStopTrace_Click()
Dim i As Integer
Dim intSessionTrace As Integer
Dim strSQL As String
If intCurrentSessionIndex > -1 Then
strSQL = "DBMS_MONITOR.SESSION_TRACE_DISABLE(" & Format(lngSID(intCurrentSessionIndex)) & "," & Format(lngSerial(intCurrentSessionIndex)) & ")"
comTrace.CommandText = strSQL
comTrace.Execute
'Remember that we disabled trace for this session
intSessionTrace = 0
For i = 1 To 1000
If SessionTrace(i).lngSID = lngSID(intCurrentSessionIndex) Then
intSessionTrace = i
Exit For
End If
Next i
If intSessionTrace <> 0 Then
SessionTrace(intSessionTrace).int10046Level = 0
If (SessionTrace(intSessionTrace).int10032Level = 0) _
And (SessionTrace(intSessionTrace).int10033Level = 0) _
And (SessionTrace(intSessionTrace).int10046Level = 0) _
And (SessionTrace(intSessionTrace).int10053Level = 0) _
And (SessionTrace(intSessionTrace).int10104Level = 0) Then
'Forget this trace entry
SessionTrace(intSessionTrace).lngSID = 0
SessionTrace(intSessionTrace).lngSerial = 0
End If
End If
cmdStopTrace.Enabled = False
cmdTraceSession.Enabled = True
End If
tvTimeModel.SetFocus
End Sub
You might notice that the code to stop the trace for a session is very similar to the code to start the trace.
The code for the cmdShowExecutionPlan CommandButton could be quite tame, as it was in the original Time Model Viewer project, but that would be a bit boring. Let’s do something a little special. First, view the UserForm object and then double-click the cmdShowExecutionPlan CommandButton. Change the Click event for the CommandButton to show the following:
Private Sub cmdShowExecutionPlan_Click()
Dim strOut As String
Dim strSQLSQLID As String
Dim intSQLChildNumber As Integer
Dim intFileNum As Integer
Dim intFlag As Integer
Dim j As Integer
On Error Resume Next
tvTimeModel.SetFocus
If intCurrentSessionIndex > -1 Then
strSQLSQLID = Left(strSQLID(intCurrentSessionIndex), InStr(strSQLID(intCurrentSessionIndex), "/") - 1)
intSQLChildNumber = Val(Right(strSQLID(intCurrentSessionIndex), Len(strSQLID(intCurrentSessionIndex)) - InStr(strSQLID(intCurrentSessionIndex), "/")))
comXPLAN("sqlid") = strSQLSQLID
comXPLAN("childnumber") = Null
'If you only want the plan for the current SQL statement, uncomment the following
'comXPLAN("childnumber") = intSQLChildNumber
Set snpXPLAN = comXPLAN.Execute
If Not (snpXPLAN Is Nothing) Then
If snpXPLAN.State = 1 Then
'Create the directory if it does not already exist
If Len(Dir("C:\ExcelTimeModelViewer", vbDirectory)) < 5 Then
MkDir "C:\ExcelTimeModelViewer"
End If
intFileNum = FreeFile
Open "C:\ExcelTimeModelViewer\DBMS_XPLAN.txt" For Output As #intFileNum
If snpXPLAN.EOF = True Then
strOut = "No Execution Plans for SQL ID " & strSQLSQLID
Else
strOut = ""
End If
Do While Not snpXPLAN.EOF
If (Left(snpXPLAN(0), 7) = "SQL_ID ") And (InStr(LCase(snpXPLAN(0)), "child number ") > 1) And (InStr(LCase(snpXPLAN(0)), "cannot be found") = 0) Then
If intFlag = True Then
strOut = strOut & String(100, "~") & vbCrLf
strOut = strOut & "" & vbCrLf
intFlag = False
End If
strOut = strOut & String(100, "*") & vbCrLf
comSQLChildReason("sqlid") = strSQLSQLID
comSQLChildReason("childnumber") = CInt(Right(snpXPLAN(0), Len(snpXPLAN(0)) - (InStr(snpXPLAN(0), "child number ") + 12)))
Set snpSQLChildReason = comSQLChildReason.Execute
If Not (snpSQLChildReason Is Nothing) Then
If snpSQLChildReason.State = 1 Then
If Not (snpSQLChildReason.EOF) Then
strOut = strOut & "Reason for Child Cursor Creation" & vbCrLf
For j = 4 To snpSQLChildReason.Fields.Count - 1
If snpSQLChildReason(j) = "Y" Then
strOut = strOut & snpSQLChildReason.Fields(j).Name & "" & vbCrLf
End If
Next j
strOut = strOut & "**********" & vbCrLf
End If
snpSQLChildReason.Close
End If
End If
comSQLChildBind("sqlid") = strSQLSQLID
comSQLChildBind("childnumber") = CInt(Right(snpXPLAN(0), Len(snpXPLAN(0)) - (InStr(snpXPLAN(0), "child number ") + 12)))
Set snpSQLChildBind = comSQLChildBind.Execute
If Not (snpSQLChildBind Is Nothing) Then
If snpSQLChildBind.State = 1 Then
If Not (snpSQLChildBind.EOF) Then
strOut = strOut & "Bind Variable Definitions" & vbCrLf
Do While Not (snpSQLChildBind.EOF)
strOut = strOut & " Position:" & CStr(snpSQLChildBind("position"))
strOut = strOut & " Max Length:" & CStr(snpSQLChildBind("max_length"))
Select Case snpSQLChildBind("datatype")
Case 1
strOut = strOut & " VARCHAR2"
Case 2
strOut = strOut & " NUMBER"
Case 8
strOut = strOut & " LONG"
Case 11
strOut = strOut & " ROWID"
Case 12
strOut = strOut & " DATE"
Case 23
strOut = strOut & " RAW"
Case 24
strOut = strOut & " LONG RAW"
Case 96
strOut = strOut & " CHAR"
Case 112
strOut = strOut & " CLOB"
Case 113
strOut = strOut & " BLOB"
Case 114
strOut = strOut & " BFILE"
Case Else
strOut = strOut & " TYPE " & CStr(snpSQLChildBind("datatype"))
End Select
strOut = strOut & " Name:" & snpSQLChildBind("bind_name")
strOut = strOut & "" & vbCrLf
snpSQLChildBind.MoveNext
Loop
strOut = strOut & "**********" & vbCrLf
End If
snpSQLChildBind.Close
End If
End If
End If
If InStr(UCase(snpXPLAN(0)), "PLAN HASH VALUE:") = 1 Then
'Found the PLAN_HASH_VALUE
comSQLStats("sqlid") = strSQLSQLID
comSQLStats("plan_hash_value") = Val(Right(snpXPLAN(0), Len(snpXPLAN(0)) - InStr(snpXPLAN(0), ":")))
Set snpSQLStats = comSQLStats.Execute
If Not (snpSQLStats Is Nothing) Then
If snpSQLStats.State = 1 Then
If Not (snpSQLStats.EOF) Then
If snpSQLStats("executions") > 0 Then
strOut = strOut & "" & vbCrLf
strOut = strOut & "Statistics for Execution Plan:" & vbCrLf
strOut = strOut & " Last Active: " & snpSQLStats("last_active_time") & vbCrLf
strOut = strOut & " Parses: " & Format(Format(snpSQLStats("parse_calls"), "#,##0 "), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Executions: " & Format(Format(snpSQLStats("executions"), "#,##0 "), "@@@@@@@@@@@@@@") _
& " Exec Per Parse: " & Format(Format(snpSQLStats("executions") / snpSQLStats("parse_calls"), "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " CPU Time: " & Format(Format(snpSQLStats("cpu_time") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("cpu_time") / snpSQLStats("executions") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Elapsed Time: " & Format(Format(snpSQLStats("elapsed_time") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("elapsed_time") / snpSQLStats("executions") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Application Wait Time: " & Format(Format(snpSQLStats("application_wait_time") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("application_wait_time") / snpSQLStats("executions") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Concurrency Wait Time: " & Format(Format(snpSQLStats("concurrency_wait_time") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("concurrency_wait_time") / snpSQLStats("executions") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " User IO Wait Time: " & Format(Format(snpSQLStats("user_io_wait_time") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("user_io_wait_time") / snpSQLStats("executions") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Application Wait Time: " & Format(Format(snpSQLStats("application_wait_time") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("application_wait_time") / snpSQLStats("executions"), "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Cluster Wait Time: " & Format(Format(snpSQLStats("cluster_wait_time") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("cluster_wait_time") / snpSQLStats("executions"), "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " PL/SQL Execute Time: " & Format(Format(snpSQLStats("plsql_exec_time") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("plsql_exec_time") / snpSQLStats("executions") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Java Execution Time: " & Format(Format(snpSQLStats("java_exec_time") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("java_exec_time") / snpSQLStats("executions") / 1000000, "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Buffer (Cons.) Gets: " & Format(Format(snpSQLStats("buffer_gets"), "#,##0 "), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("buffer_gets") / snpSQLStats("executions"), "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Disk (Block) Reads: " & Format(Format(snpSQLStats("disk_reads"), "#,##0 "), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("disk_reads") / snpSQLStats("executions"), "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Direct Writes: " & Format(Format(snpSQLStats("direct_writes"), "#,##0 "), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("direct_writes") / snpSQLStats("executions"), "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Rows Processed: " & Format(Format(snpSQLStats("rows_processed"), "#,##0 "), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("rows_processed") / snpSQLStats("executions"), "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Fetches: " & Format(Format(snpSQLStats("fetches"), "#,##0 "), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("fetches") / snpSQLStats("executions"), "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Loads: " & Format(Format(snpSQLStats("loads"), "#,##0 "), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("loads") / snpSQLStats("executions"), "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & " Invalidations: " & Format(Format(snpSQLStats("invalidations"), "#,##0 "), "@@@@@@@@@@@@@@") _
& " Per Exec: " & Format(Format(snpSQLStats("invalidations") / snpSQLStats("executions"), "#,##0.00"), "@@@@@@@@@@@@@@") & vbCrLf
strOut = strOut & "" & vbCrLf
End If
End If
snpSQLStats.Close
End If
End If
End If
If (InStr(snpXPLAN(0), "SQL_ID") > 0) And (InStr(snpXPLAN(0), "child number " & Format(intSQLChildNumber)) > 0) Then
intFlag = True
strOut = strOut & "Plan Used by the Session" & vbCrLf
strOut = strOut & String(100, "~") & vbCrLf
strOut = strOut & snpXPLAN(0) & vbCrLf
strOut = strOut & String(100, "~") & vbCrLf
Else
strOut = strOut & snpXPLAN(0) & vbCrLf
End If
snpXPLAN.MoveNext
Loop
snpXPLAN.Close
If intFlag = True Then
strOut = strOut & String(100, "~") & vbCrLf
End If
strOut = strOut & vbCrLf
End If
End If
Print #intFileNum, strOut
Close #intFileNum
Shell "notepad.exe C:\ExcelTimeModelViewer\DBMS_XPLAN.txt", vbNormalFocus
End If
End Sub
Note that in the above code, if you are running Windows Vista or Window 7 with User Access Control enabled, you will need to replace all instances of C:\ExcelTimeModelViewer in the code with a writeable file location.
Press the F5 key to start up the execution of the UserForm. You might need to toggle the chkPauseFresh CheckBox to allow the UserForm data to begin updating. Just as an experiment, I decided to take a look at the execution plan for the SQL_ID that was identified for my Excel session. The following is the output that was displayed on screen (in the Notepad application window):
****************************************************************************************************
Reason for Child Cursor Creation
**********
Plan Used by the Session
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID 6uw0vzxdsd8f8, child number 0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-------------------------------------
SELECT S.SID, S.SERIAL#, S.STATUS, NVL(S.USERNAME,' ')
USERNAME, NVL(S.MACHINE,' ') MACHINE, NVL(S.PROGRAM,' ') PROGRAM,
NVL(S.SQL_ID,NVL(S.PREV_SQL_ID,' ')) SQL_ID,
NVL(S.SQL_CHILD_NUMBER,NVL(S.PREV_CHILD_NUMBER,0)) SQL_CHILD_NUMBER,
STM.VALUE, STM.STAT_NAME FROM V$SESS_TIME_MODEL STM,
V$SESSION S WHERE S.SID=STM.SID ORDER BY S.USERNAME,
S.PROGRAM, S.SID
Statistics for Execution Plan:
Last Active: 3/15/2011 11:12:31 PM
Parses: 14
Executions: 18 Exec Per Parse: 1.29
CPU Time: 0.06 Per Exec: 0.00
Elapsed Time: 0.08 Per Exec: 0.00
Application Wait Time: 0.00 Per Exec: 0.00
Concurrency Wait Time: 0.00 Per Exec: 0.00
User IO Wait Time: 0.01 Per Exec: 0.00
Application Wait Time: 0.00 Per Exec: 0.00
Cluster Wait Time: 0.00 Per Exec: 0.00
PL/SQL Execute Time: 0.00 Per Exec: 0.00
Java Execution Time: 0.00 Per Exec: 0.00
Buffer (Cons.) Gets: 201 Per Exec: 11.17
Disk (Block) Reads: 4 Per Exec: 0.22
Direct Writes: 0 Per Exec: 0.00
Rows Processed: 8,170 Per Exec: 453.89
Fetches: 90 Per Exec: 5.00
Loads: 2 Per Exec: 0.11
Invalidations: 0 Per Exec: 0.00
Plan hash value: 186343697
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Inst |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| | |
| 1 | SORT ORDER BY | | 273 | 27846 | 2 (100)| 00:00:01 | |
|* 2 | HASH JOIN | | 273 | 27846 | 1 (100)| 00:00:01 | |
|* 3 | FIXED TABLE FULL | X$KEWSSMAP | 13 | 455 | 0 (0)| | OR112 |
| 4 | NESTED LOOPS | | 1604 | 104K| 0 (0)| | |
| 5 | NESTED LOOPS | | 24 | 1224 | 0 (0)| | |
| 6 | NESTED LOOPS | | 24 | 288 | 0 (0)| | |
| 7 | FIXED TABLE FULL | X$KSLWT | 24 | 192 | 0 (0)| | OR112 |
|* 8 | FIXED TABLE FIXED INDEX| X$KSLED (ind:2) | 1 | 4 | 0 (0)| | OR112 |
|* 9 | FIXED TABLE FIXED INDEX | X$KSUSE (ind:1) | 1 | 39 | 0 (0)| | OR112 |
|* 10 | FIXED TABLE FIXED INDEX | X$KEWSSESV (ind:1) | 67 | 1072 | 0 (0)| | OR112 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("MAP"."SOFFST"="SESV"."KEWSNUM")
3 - filter(("MAP"."AGGID"=1 AND INTERNAL_FUNCTION("MAP"."STYPE") AND
"MAP"."INST_ID"=USERENV('INSTANCE')))
8 - filter("W"."KSLWTEVT"="E"."INDX")
9 - filter((BITAND("S"."KSUSEFLG",1)<>0 AND BITAND("S"."KSSPAFLG",1)<>0 AND
"S"."INST_ID"=USERENV('INSTANCE') AND "S"."INDX"="W"."KSLWTSID"))
10 - filter((BITAND("SESV"."KSUSEFLG",1)<>0 AND BITAND("SESV"."KSSPAFLG",1)<>0 AND
"S"."INDX"="SESV"."KSUSENUM"))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
****************************************************************************************************
Reason for Child Cursor Creation
STATS_ROW_MISMATCH
**********
SQL_ID 6uw0vzxdsd8f8, child number 1
-------------------------------------
SELECT S.SID, S.SERIAL#, S.STATUS, NVL(S.USERNAME,' ')
USERNAME, NVL(S.MACHINE,' ') MACHINE, NVL(S.PROGRAM,' ') PROGRAM,
NVL(S.SQL_ID,NVL(S.PREV_SQL_ID,' ')) SQL_ID,
NVL(S.SQL_CHILD_NUMBER,NVL(S.PREV_CHILD_NUMBER,0)) SQL_CHILD_NUMBER,
STM.VALUE, STM.STAT_NAME FROM V$SESS_TIME_MODEL STM,
V$SESSION S WHERE S.SID=STM.SID ORDER BY S.USERNAME,
S.PROGRAM, S.SID
Statistics for Execution Plan:
Last Active: 3/15/2011 11:12:31 PM
Parses: 14
Executions: 18 Exec Per Parse: 1.29
CPU Time: 0.06 Per Exec: 0.00
Elapsed Time: 0.08 Per Exec: 0.00
Application Wait Time: 0.00 Per Exec: 0.00
Concurrency Wait Time: 0.00 Per Exec: 0.00
User IO Wait Time: 0.01 Per Exec: 0.00
Application Wait Time: 0.00 Per Exec: 0.00
Cluster Wait Time: 0.00 Per Exec: 0.00
PL/SQL Execute Time: 0.00 Per Exec: 0.00
Java Execution Time: 0.00 Per Exec: 0.00
Buffer (Cons.) Gets: 201 Per Exec: 11.17
Disk (Block) Reads: 4 Per Exec: 0.22
Direct Writes: 0 Per Exec: 0.00
Rows Processed: 8,170 Per Exec: 453.89
Fetches: 90 Per Exec: 5.00
Loads: 2 Per Exec: 0.11
Invalidations: 0 Per Exec: 0.00
Plan hash value: 186343697
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Inst |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| | |
| 1 | SORT ORDER BY | | 273 | 27846 | 2 (100)| 00:00:01 | |
|* 2 | HASH JOIN | | 273 | 27846 | 1 (100)| 00:00:01 | |
|* 3 | FIXED TABLE FULL | X$KEWSSMAP | 13 | 455 | 0 (0)| | OR112 |
| 4 | NESTED LOOPS | | 1604 | 104K| 0 (0)| | |
| 5 | NESTED LOOPS | | 24 | 1224 | 0 (0)| | |
| 6 | NESTED LOOPS | | 24 | 288 | 0 (0)| | |
| 7 | FIXED TABLE FULL | X$KSLWT | 24 | 192 | 0 (0)| | OR112 |
|* 8 | FIXED TABLE FIXED INDEX| X$KSLED (ind:2) | 1 | 4 | 0 (0)| | OR112 |
|* 9 | FIXED TABLE FIXED INDEX | X$KSUSE (ind:1) | 1 | 39 | 0 (0)| | OR112 |
|* 10 | FIXED TABLE FIXED INDEX | X$KEWSSESV (ind:1) | 67 | 1072 | 0 (0)| | OR112 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("MAP"."SOFFST"="SESV"."KEWSNUM")
3 - filter(("MAP"."AGGID"=1 AND INTERNAL_FUNCTION("MAP"."STYPE") AND
"MAP"."INST_ID"=USERENV('INSTANCE')))
8 - filter("W"."KSLWTEVT"="E"."INDX")
9 - filter((BITAND("S"."KSUSEFLG",1)<>0 AND BITAND("S"."KSSPAFLG",1)<>0 AND
"S"."INST_ID"=USERENV('INSTANCE') AND "S"."INDX"="W"."KSLWTSID"))
10 - filter((BITAND("SESV"."KSUSEFLG",1)<>0 AND BITAND("SESV"."KSSPAFLG",1)<>0 AND
"S"."INDX"="SESV"."KSUSENUM"))
If you examine the above, you will notice that the output from V$SQLSTATS is identical for both of the child cursors – that is because the PLAN_HASH_VALUE is identical for both of the child cursors.
Where do we head next with the project? I am a person who prefers looking at raw numbers, but maybe someone reading this blog prefers pictures?
Added August 11, 2011:
The Excel project code to this point, save with a .XLS extension (currently has a .DOC extension, please change):
TimeModelViewerExcelArticle5.xls
Recent Comments