<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>OraNA :: Database Management and Performance</title><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/orana_dba" /><language>en</language><managingEditor>noemail@noemail.org (OraNA.info)</managingEditor><lastBuildDate>Fri, 12 Mar 2010 01:28:53 PST</lastBuildDate><generator>Google Reader http://www.google.com/reader</generator><gr:continuation xmlns:gr="http://www.google.com/schemas/reader/atom/">CMXVu8zXr6AC</gr:continuation><feedburner:info uri="orana_dba" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><description>Read and monitor Oracle Database management and performance related blogs and news sources, all in one place.</description><item><title>Grid Control &amp; OID: a deadly combo</title><link>http://feedproxy.google.com/~r/orana_dba/~3/EfyDGKgFQQs/grid-control-oid-deadly-combo.html</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Frank</dc:creator><pubDate>Fri, 12 Mar 2010 01:28:29 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/561c97537c8294dc</guid><description>If you ever experience these symptoms, ask your self whether you have Grid Control (or OEM) running.Single Sign On failsOID Processes suddenly have stopped (which explains the above)ODS database account is locked, which seems to cause the problemUnlocking the ODS account resolves the problem, but it gets locked after a short period.The cause is the fact you changed the ODS password. Now, you may&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/EfyDGKgFQQs" height="1" width="1"/&gt;</description><feedburner:origLink>http://vanbortel.blogspot.com/2010/03/grid-control-oid-deadly-combo.html</feedburner:origLink></item><item><title>From a VBS Script to a 10046 Trace and Back into a VBS Script</title><link>http://feedproxy.google.com/~r/orana_dba/~3/wigna_c4prA/from-a-vbs-script-to-a-10046-trace-and-back-into-a-vbs-script</link><category>Bind Variable</category><category>Performance</category><category>VBS</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Charles Hooper</dc:creator><pubDate>Thu, 11 Mar 2010 22:00:25 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/da340f14b766177c</guid><description>&lt;div&gt;&lt;p&gt;March 12, 2010&lt;/p&gt;
&lt;p&gt;I thought that we would try something a bit more difficult today.  In previous articles I showed how to generate and read 10046 trace files using various methods, and I also showed a couple of different VBS scripts that could interact with an Oracle database.  With some effort we could even read through a 10046 to pull out bits of information, much like TKPROF, but it probably does not make much sense to reproduce what TKPROF already accomplishes.  I thought instead what I would do is to create a VBS script that generates a 10046 trace file at level 4, while executing a couple of SQL statements.  A second VBS script will read the raw 10046 trace file and convert that trace file back into a VBS script, complete with bind variables.  The code for the second VBS script is based on some of the code in my Toy project for performance tuning – something that I originally created just to see if it could be done.&lt;/p&gt;
&lt;p&gt;First, we need a table to use as the data source for the first VBS script – this is the test table used in &lt;a href="http://hoopercharles.wordpress.com/2009/12/16/output-employee-attendance-calendar-to-web-with-vbs/"&gt;this blog article&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;CREATE TABLE EMPLOYEE_RECORD_TEST AS
SELECT
  DECODE(TRUNC(DBMS_RANDOM.VALUE(0,5)),
          0,&amp;#39;MIKE&amp;#39;,
          1,&amp;#39;ROB&amp;#39;,
          2,&amp;#39;SAM&amp;#39;,
          3,&amp;#39;JOE&amp;#39;,
          4,&amp;#39;ERIC&amp;#39;) EMPLOYEE_ID,
  TRUNC(SYSDATE)-ROUND(DBMS_RANDOM.VALUE(0,1000)) SHIFT_DATE,
  DECODE(TRUNC(DBMS_RANDOM.VALUE(0,10)),
          0,&amp;#39;VAC&amp;#39;,
          1,&amp;#39;HOL&amp;#39;,
          2,&amp;#39;BEREAVE&amp;#39;,
          3,&amp;#39;JURY&amp;#39;,
          4,&amp;#39;ABS&amp;#39;,
          5,&amp;#39;EXCUSE&amp;#39;,
          6,&amp;#39;MIL&amp;#39;,
          &amp;#39;OTHER&amp;#39;) INDIRECT_ID
FROM
  DUAL
CONNECT BY
  LEVEL&amp;lt;=1000;&lt;/pre&gt;
&lt;p&gt;With the test table built, we execute the following simple VBS script (using either CSCRIPT or WSCRIPT on a Windows client):&lt;/p&gt;
&lt;pre&gt;Const adCmdText = 1
Const adNumeric = 131
Const adDate = 7
Const adDBDate = 133
Const adDBTimeStamp = 135
Const adDBTime = 134
Const adVarChar = 200
Const adParamInput = 1

Dim strSQL
Dim strUsername
Dim strPassword
Dim strDatabase

Dim dbDatabase
Dim snpDataEmployees
Dim comDataEmployees
Dim snpDataAttend
Dim comDataAttend
Dim snpDataEmpRecord
Dim comDataEmpRecord

Set dbDatabase = CreateObject(&amp;quot;ADODB.Connection&amp;quot;)
Set snpDataEmployees = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
Set comDataEmployees = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
Set snpDataAttend = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
Set comDataAttend = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
Set snpDataEmpRecord = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
Set comDataEmpRecord = CreateObject(&amp;quot;ADODB.Command&amp;quot;)

strUsername = &amp;quot;MyUsername&amp;quot;
strPassword = &amp;quot;MyPassword&amp;quot;
strDatabase = &amp;quot;MyDB&amp;quot;

dbDatabase.ConnectionString = &amp;quot;Provider=OraOLEDB.Oracle;Data Source=&amp;quot; &amp;amp; strDatabase &amp;amp; &amp;quot;;User ID=&amp;quot; &amp;amp; strUsername &amp;amp; &amp;quot;;Password=&amp;quot; &amp;amp; strPassword &amp;amp; &amp;quot;;&amp;quot;
dbDatabase.Open
&amp;#39;Should verify that the connection attempt was successful, but I will leave that for someone else to code

dbDatabase.Execute &amp;quot;ALTER SESSION SET TRACEFILE_IDENTIFIER = &amp;#39;VBS2TRACE2VBS&amp;#39;&amp;quot;
dbDatabase.Execute &amp;quot;ALTER SESSION SET EVENTS &amp;#39;10046 TRACE NAME CONTEXT FOREVER, LEVEL 4&amp;#39;&amp;quot;

strSQL = &amp;quot;INSERT INTO EMPLOYEE_RECORD_TEST(&amp;quot; &amp;amp; VBCrLf
strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_ID,&amp;quot; &amp;amp; VBCrLf
strSQL = strSQL &amp;amp; &amp;quot;  SHIFT_DATE,&amp;quot; &amp;amp; VBCrLf
strSQL = strSQL &amp;amp; &amp;quot;  INDIRECT_ID)&amp;quot; &amp;amp; VBCrLf
strSQL = strSQL &amp;amp; &amp;quot;VALUES(&amp;quot; &amp;amp; VBCrLf
strSQL = strSQL &amp;amp; &amp;quot;  ?,&amp;quot; &amp;amp; VBCrLf
strSQL = strSQL &amp;amp; &amp;quot;  ?,&amp;quot; &amp;amp; VBCrLf
strSQL = strSQL &amp;amp; &amp;quot;  ?)&amp;quot;

With comDataEmpRecord
    &amp;#39;Set up the command properties
    .CommandText = strSQL
    .CommandType = adCmdText
    .CommandTimeout = 30
    .ActiveConnection = dbDatabase

    .Parameters.Append .CreateParameter(&amp;quot;employee_id&amp;quot;, adVarChar, adParamInput, 15, &amp;quot;TEST&amp;quot;)
    .Parameters.Append .CreateParameter(&amp;quot;shift_date&amp;quot;, adDate, adParamInput, 8, Date)
    .Parameters.Append .CreateParameter(&amp;quot;indirect_id&amp;quot;, adVarchar, adParamInput, 15, &amp;quot;HOL&amp;quot;)
End With

&amp;#39;Rollback Test
dbDatabase.BeginTrans

comDataEmpRecord.Execute

dbDatabase.RollbackTrans

strSQL = &amp;quot;SELECT DISTINCT&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_ID&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;FROM&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_RECORD_TEST&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;WHERE&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  SHIFT_DATE&amp;gt;= ?&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  AND INDIRECT_ID= ?&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;ORDER BY&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_ID&amp;quot;

With comDataEmployees
    &amp;#39;Set up the command properties
    .CommandText = strSQL
    .CommandType = adCmdText
    .CommandTimeout = 30
    .ActiveConnection = dbDatabase

    .Parameters.Append .CreateParameter(&amp;quot;shift_date&amp;quot;, adDate, adParamInput, 8, DateAdd(&amp;quot;d&amp;quot;, -90, Date))
    .Parameters.Append .CreateParameter(&amp;quot;indirect_id&amp;quot;, adVarChar, adParamInput, 15, &amp;quot;VAC&amp;quot;)
End With

strSQL = &amp;quot;SELECT&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  SUM(DECODE(TO_CHAR(SHIFT_DATE,&amp;#39;D&amp;#39;),&amp;#39;2&amp;#39;,1,0)) MON_COUNT,&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  SUM(DECODE(TO_CHAR(SHIFT_DATE,&amp;#39;D&amp;#39;),&amp;#39;3&amp;#39;,1,0)) TUE_COUNT,&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  SUM(DECODE(TO_CHAR(SHIFT_DATE,&amp;#39;D&amp;#39;),&amp;#39;4&amp;#39;,1,0)) WED_COUNT,&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  SUM(DECODE(TO_CHAR(SHIFT_DATE,&amp;#39;D&amp;#39;),&amp;#39;5&amp;#39;,1,0)) THU_COUNT,&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  SUM(DECODE(TO_CHAR(SHIFT_DATE,&amp;#39;D&amp;#39;),&amp;#39;6&amp;#39;,1,0)) FRI_COUNT&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;FROM&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_RECORD_TEST&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;WHERE&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_ID= ?&amp;quot; &amp;amp; vbCrLf
strSQL = strSQL &amp;amp; &amp;quot;  AND INDIRECT_ID= ?&amp;quot;

With comDataAttend
    &amp;#39;Set up the command properties
    .CommandText = strSQL
    .CommandType = adCmdText
    .CommandTimeout = 30
    .ActiveConnection = dbDatabase

    .Parameters.Append .CreateParameter(&amp;quot;employee_id&amp;quot;, adVarChar, adParamInput, 15, &amp;quot;HOOPER&amp;quot;)
    .Parameters.Append .CreateParameter(&amp;quot;indirect_id&amp;quot;, adVarChar, adParamInput, 15, &amp;quot;EXCUSE&amp;quot;)
End With

Set snpDataEmployees = comDataEmployees.Execute

If Not (snpDataEmployees Is Nothing) Then
    Do While Not snpDataEmployees.EOF
        comDataAttend(&amp;quot;employee_id&amp;quot;) = snpDataEmployees(&amp;quot;employee_id&amp;quot;)
        comDataAttend(&amp;quot;indirect_id&amp;quot;) = &amp;quot;EXCUSE&amp;quot;
        Set snpDataAttend = comDataAttend.Execute
        If Not snpDataAttend.EOF Then
            &amp;#39;Do Something with the data
        End If
        snpDataAttend.Close

        comDataAttend(&amp;quot;indirect_id&amp;quot;) = &amp;quot;ABS&amp;quot;
        Set snpDataAttend = comDataAttend.Execute
        If Not snpDataAttend.EOF Then
            &amp;#39;Do Something with the data
        End If
        snpDataAttend.Close

        snpDataEmployees.MoveNext
    Loop

    snpDataEmployees.Close
End If

dbDatabase.Close
Set snpDataEmployees = Nothing
Set snpDataAttend = Nothing
Set comDataEmployees = Nothing
Set comDataAttend = Nothing
Set snpDataEmpRecord = Nothing
Set comDataEmpRecord = Nothing
Set dbDatabase = Nothing&lt;/pre&gt;
&lt;p&gt;(&lt;a href="http://hoopercharles.files.wordpress.com/2010/03/testscript-vbs1.doc"&gt;TestScript.vbs&lt;/a&gt; - save as TestScript.vbs)&lt;/p&gt;
&lt;p&gt;In the above, replace MyDB with a valid database name from the tnsnames.ora file, MyUsername with a valid username, and MyPassword with the password for the user.  The script starts by starting a transaction (the default behavior is an implicit commit), a row is inserted into the test table, and then a ROLLBACK is performed.  The script then submits a SQL statement that retrieves a list of 5 employees from the test table.  For each of the (up to) 5 employees a second SQL statement is executed with two different bind variable sets to determine the number of each week day the employee has been out of work on an excused (EXCUSE) or unexcused (ABS) absence.  If I were writing a real program to accomplish this task I would combine the three SELECT statements into a single SELECT statement, but I want to demonstrate how the second VBS script handles multiple SQL statements that are open at the same time.&lt;/p&gt;
&lt;p&gt;Running the above script generated a trace file when executed against Oracle Database 11.2.0.1: &lt;a href="http://hoopercharles.files.wordpress.com/2010/03/or112_ora_5482_vbs2trace2vbs-trc2.doc"&gt;or112_ora_5482_VBS2TRACE2VBS.trc&lt;/a&gt;  (save as C:\or112_ora_5482_VBS2TRACE2VBS.trc – Windows users can view the file with Wordpad and convert the file into a plain text file that can be opened with Notepad).  The goal is to take the trace file and transform it back into a VBS script, ignoring SQL statements that appear in the trace file at a depth greater than 0.&lt;/p&gt;
&lt;p&gt;The output of the VBS script that reads the 10046 trace file and generates a VBS file should look something like this:&lt;/p&gt;
&lt;pre&gt;&amp;#39;Source File:C:\or112_ora_5482_VBS2TRACE2VBS.trc

HyperactiveTrace

Sub HyperactiveTrace()
    Const adCmdText = 1
    Const adCmdStoredProc = 4
    Const adParamInput = 1
    Const adVarNumeric = 139
    Const adBigInt = 20
    Const adDecimal = 14
    Const adDouble = 5
    Const adInteger = 3
    Const adLongVarBinary = 205
    Const adNumeric = 131
    Const adSingle = 4
    Const adSmallInt = 2
    Const adTinyInt = 16
    Const adUnsignedBigInt = 21
    Const adUnsignedInt = 19
    Const adUnsignedSmallInt = 18
    Const adUnsignedTinyInt = 17
    Const adDate = 7
    Const adDBDate = 133
    Const adDBTimeStamp = 135
    Const adDBTime = 134
    Const adVarChar = 200
    Const adChar = 129
    Const adUseClient = 3

    Dim i
    Dim strSQL
    Dim strUsername
    Dim strPassword
    Dim strDatabase

    Dim dbDatabase
    Set dbDatabase = CreateObject(&amp;quot;ADODB.Connection&amp;quot;)
    Dim snpData1
    Dim comData1
    Set snpData1 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData1 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData2
    Dim comData2
    Set snpData2 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData2 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData3
    Dim comData3
    Set snpData3 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData3 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData4
    Dim comData4
    Set snpData4 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData4 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData5
    Dim comData5
    Set snpData5 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData5 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData6
    Dim comData6
    Set snpData6 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData6 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData7
    Dim comData7
    Set snpData7 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData7 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData8
    Dim comData8
    Set snpData8 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData8 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData9
    Dim comData9
    Set snpData9 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData9 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData10
    Dim comData10
    Set snpData10 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData10 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData11
    Dim comData11
    Set snpData11 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData11 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData12
    Dim comData12
    Set snpData12 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData12 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData13
    Dim comData13
    Set snpData13 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData13 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData14
    Dim comData14
    Set snpData14 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData14 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData15
    Dim comData15
    Set snpData15 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData15 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData16
    Dim comData16
    Set snpData16 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData16 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData17
    Dim comData17
    Set snpData17 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData17 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData18
    Dim comData18
    Set snpData18 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData18 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData19
    Dim comData19
    Set snpData19 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData19 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)
    Dim snpData20
    Dim comData20
    Set snpData20 = CreateObject(&amp;quot;ADODB.Recordset&amp;quot;)
    Set comData20 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)

    On Error Resume Next

    strUsername = &amp;quot;MyUsername&amp;quot;
    strPassword = &amp;quot;MyPassword&amp;quot;
    strDatabase = &amp;quot;MyDB&amp;quot;
    dbDatabase.ConnectionString = &amp;quot;Provider=OraOLEDB.Oracle;Data Source=&amp;quot; &amp;amp; strDatabase &amp;amp; &amp;quot;;User ID=&amp;quot; &amp;amp; strUsername &amp;amp; &amp;quot;;Password=&amp;quot; &amp;amp; strPassword &amp;amp; &amp;quot;;&amp;quot;
    dbDatabase.Open
    &amp;#39;Should verify that the connection attempt was successful, but I will leave that for someone else to code

    &amp;#39;dbDatabase.BeginTrans

    &amp;#39;Transaction Committed and NO Records were Affected, Need to determine transaction start
    dbDatabase.CommitTrans

    &amp;#39;dbDatabase.BeginTrans

    Set comData3 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)

    strSQL = &amp;quot;INSERT INTO EMPLOYEE_RECORD_TEST(&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_ID,&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  SHIFT_DATE,&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  INDIRECT_ID)&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;VALUES(&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  ?,&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  ?,&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  ?)&amp;quot;

    With comData3
        &amp;#39;Set up the command properties
        .CommandText = strSQL
        .CommandType = adCmdText
        .CommandTimeout = 30
        .ActiveConnection = dbDatabase
        &amp;#39;Bind variables will be defined below, if there are no bind variable, uncomment the next line and add the looping construct
    End With
    &amp;#39;comData3.Execute

    With comData3
        .Parameters.Append .CreateParameter(&amp;quot;B1&amp;quot;, adChar, adParamInput, 4, &amp;quot;TEST&amp;quot;)
        .Parameters.Append .CreateParameter(&amp;quot;B2&amp;quot;, adDate, adParamInput, 7, &amp;quot;3/11/2010 0:0:0&amp;quot;)
        .Parameters.Append .CreateParameter(&amp;quot;B3&amp;quot;, adChar, adParamInput, 3, &amp;quot;HOL&amp;quot;)
    End With

    comData3(&amp;quot;B1&amp;quot;) = &amp;quot;TEST&amp;quot;
    comData3(&amp;quot;B2&amp;quot;) = cDate(&amp;quot;3/11/2010 0:0:0&amp;quot;)
    comData3(&amp;quot;B3&amp;quot;) = &amp;quot;HOL&amp;quot;

    comData3.Execute

    &amp;#39;Transaction Rolled Back and Records Should have been Affected, Need to determine transaction start
    dbDatabase.RollbackTrans

    &amp;#39;dbDatabase.BeginTrans

    &amp;#39;Cursor 2 Closing
    If snpData2.State = 1 Then
        snpData2.Close
    End If
    Set comData2 = Nothing

    Set comData2 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)

    strSQL = &amp;quot;SELECT DISTINCT&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_ID&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;FROM&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_RECORD_TEST&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;WHERE&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  SHIFT_DATE&amp;gt;= ?&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  AND INDIRECT_ID= ?&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;ORDER BY&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_ID&amp;quot;

    With comData2
        &amp;#39;Set up the command properties
        .CommandText = strSQL
        .CommandType = adCmdText
        .CommandTimeout = 30
        .ActiveConnection = dbDatabase
        &amp;#39;Bind variables will be defined below, if there are no bind variable, uncomment the next line and add the looping construct
    End With
    &amp;#39;Set snpData2 = comData2.Execute

    &amp;#39;Cursor 4 Closing
    If snpData4.State = 1 Then
        snpData4.Close
    End If
    Set comData4 = Nothing

    &amp;#39;Cursor 4 Closing
    If snpData4.State = 1 Then
        snpData4.Close
    End If
    Set comData4 = Nothing

    &amp;#39;Cursor 4 Closing
    If snpData4.State = 1 Then
        snpData4.Close
    End If
    Set comData4 = Nothing

    With comData2
        .Parameters.Append .CreateParameter(&amp;quot;B1&amp;quot;, adDate, adParamInput, 7, &amp;quot;12/11/2009 0:0:0&amp;quot;)
        .Parameters.Append .CreateParameter(&amp;quot;B2&amp;quot;, adChar, adParamInput, 3, &amp;quot;VAC&amp;quot;)
    End With

    comData2(&amp;quot;B1&amp;quot;) = cDate(&amp;quot;12/11/2009 0:0:0&amp;quot;)
    comData2(&amp;quot;B2&amp;quot;) = &amp;quot;VAC&amp;quot;

    Set snpData2 = comData2.Execute

    If Not (snpData2 Is Nothing) Then
        Do While Not snpData2.EOF

            snpData2.MoveNext
        Loop
    End If

    &amp;#39;Cursor 4 Closing
    If snpData4.State = 1 Then
        snpData4.Close
    End If
    Set comData4 = Nothing

    Set comData4 = CreateObject(&amp;quot;ADODB.Command&amp;quot;)

    strSQL = &amp;quot;SELECT&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  SUM(DECODE(TO_CHAR(SHIFT_DATE,&amp;#39;D&amp;#39;),&amp;#39;2&amp;#39;,1,0)) MON_COUNT,&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  SUM(DECODE(TO_CHAR(SHIFT_DATE,&amp;#39;D&amp;#39;),&amp;#39;3&amp;#39;,1,0)) TUE_COUNT,&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  SUM(DECODE(TO_CHAR(SHIFT_DATE,&amp;#39;D&amp;#39;),&amp;#39;4&amp;#39;,1,0)) WED_COUNT,&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  SUM(DECODE(TO_CHAR(SHIFT_DATE,&amp;#39;D&amp;#39;),&amp;#39;5&amp;#39;,1,0)) THU_COUNT,&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  SUM(DECODE(TO_CHAR(SHIFT_DATE,&amp;#39;D&amp;#39;),&amp;#39;6&amp;#39;,1,0)) FRI_COUNT&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;FROM&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_RECORD_TEST&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;WHERE&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  EMPLOYEE_ID= ?&amp;quot; &amp;amp; vbCrLf
    strSQL = strSQL &amp;amp; &amp;quot;  AND INDIRECT_ID= ?&amp;quot;

    With comData4
        &amp;#39;Set up the command properties
        .CommandText = strSQL
        .CommandType = adCmdText
        .CommandTimeout = 30
        .ActiveConnection = dbDatabase
        &amp;#39;Bind variables will be defined below, if there are no bind variable, uncomment the next line and add the looping construct
    End With
    &amp;#39;Set snpData4 = comData4.Execute

    With comData4
        .Parameters.Append .CreateParameter(&amp;quot;B1&amp;quot;, adChar, adParamInput, 4, &amp;quot;ERIC&amp;quot;)
        .Parameters.Append .CreateParameter(&amp;quot;B2&amp;quot;, adChar, adParamInput, 6, &amp;quot;EXCUSE&amp;quot;)
    End With

    comData4(&amp;quot;B1&amp;quot;) = &amp;quot;ERIC&amp;quot;
    comData4(&amp;quot;B2&amp;quot;) = &amp;quot;EXCUSE&amp;quot;

    Set snpData4 = comData4.Execute

    If Not (snpData4 Is Nothing) Then
        Do While Not snpData4.EOF

            snpData4.MoveNext
        Loop
    End If

    &amp;#39;Cursor 5 Closing
    If snpData5.State = 1 Then
        snpData5.Close
    End If
    Set comData5 = Nothing

    comData4(&amp;quot;B1&amp;quot;) = &amp;quot;ERIC&amp;quot;
    comData4(&amp;quot;B2&amp;quot;) = &amp;quot;ABS&amp;quot;

    Set snpData4 = comData4.Execute

    If Not (snpData4 Is Nothing) Then
        Do While Not snpData4.EOF

            snpData4.MoveNext
        Loop
    End If

    &amp;#39;Cursor 5 Closing
    If snpData5.State = 1 Then
        snpData5.Close
    End If
    Set comData5 = Nothing

    comData4(&amp;quot;B1&amp;quot;) = &amp;quot;JOE&amp;quot;
    comData4(&amp;quot;B2&amp;quot;) = &amp;quot;EXCUSE&amp;quot;

    Set snpData4 = comData4.Execute

    If Not (snpData4 Is Nothing) Then
        Do While Not snpData4.EOF

            snpData4.MoveNext
        Loop
    End If

    comData4(&amp;quot;B1&amp;quot;) = &amp;quot;JOE&amp;quot;
    comData4(&amp;quot;B2&amp;quot;) = &amp;quot;ABS&amp;quot;

    Set snpData4 = comData4.Execute

    If Not (snpData4 Is Nothing) Then
        Do While Not snpData4.EOF

            snpData4.MoveNext
        Loop
    End If

    comData4(&amp;quot;B1&amp;quot;) = &amp;quot;MIKE&amp;quot;
    comData4(&amp;quot;B2&amp;quot;) = &amp;quot;EXCUSE&amp;quot;

    Set snpData4 = comData4.Execute

    If Not (snpData4 Is Nothing) Then
        Do While Not snpData4.EOF

            snpData4.MoveNext
        Loop
    End If

    comData4(&amp;quot;B1&amp;quot;) = &amp;quot;MIKE&amp;quot;
    comData4(&amp;quot;B2&amp;quot;) = &amp;quot;ABS&amp;quot;

    Set snpData4 = comData4.Execute

    If Not (snpData4 Is Nothing) Then
        Do While Not snpData4.EOF

            snpData4.MoveNext
        Loop
    End If

    comData4(&amp;quot;B1&amp;quot;) = &amp;quot;SAM&amp;quot;
    comData4(&amp;quot;B2&amp;quot;) = &amp;quot;EXCUSE&amp;quot;

    Set snpData4 = comData4.Execute

    If Not (snpData4 Is Nothing) Then
        Do While Not snpData4.EOF

            snpData4.MoveNext
        Loop
    End If

    comData4(&amp;quot;B1&amp;quot;) = &amp;quot;SAM&amp;quot;
    comData4(&amp;quot;B2&amp;quot;) = &amp;quot;ABS&amp;quot;

    Set snpData4 = comData4.Execute

    If Not (snpData4 Is Nothing) Then
        Do While Not snpData4.EOF

            snpData4.MoveNext
        Loop
    End If

    &amp;#39;Transaction Committed and NO Records were Affected, Need to determine transaction start
    dbDatabase.CommitTrans

    &amp;#39;dbDatabase.BeginTrans

    &amp;#39;Cursor 5 Closing
    If snpData5.State = 1 Then
        snpData5.Close
    End If
    Set comData5 = Nothing

    &amp;#39;*************************************************************
    &amp;#39;Maximum Recordset Number Used is 5 - Adjust the Code at the Start Accordingly
    &amp;#39;*************************************************************

    If snpData1.State = 1 Then
        snpData1.Close
    End If
    Set snpData1 = Nothing

    If snpData2.State = 1 Then
        snpData2.Close
    End If
    Set snpData2 = Nothing

    If snpData3.State = 1 Then
        snpData3.Close
    End If
    Set snpData3 = Nothing

    If snpData4.State = 1 Then
        snpData4.Close
    End If
    Set snpData4 = Nothing

    If snpData5.State = 1 Then
        snpData5.Close
    End If
    Set snpData5 = Nothing

    Set comData1 = Nothing
    Set comData2 = Nothing
    Set comData3 = Nothing
    Set comData4 = Nothing
    Set comData5 = Nothing

    dbDatabase.Close
    Set dbDatabase = Nothing
End Sub&lt;/pre&gt;
&lt;p&gt;(&lt;a href="http://hoopercharles.files.wordpress.com/2010/03/tracetovbsoutput-vbs.doc"&gt;TraceToVBSOutput.vbs&lt;/a&gt; – save as TraceToVBSOutput.vbs)&lt;/p&gt;
&lt;p&gt;If you compare the original TestScript.vbs with the above output, we see that the two scripts are similar, but with a couple of distinct differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is not necessarily easy to determine when a transaction starts, but it is possible to determine when a transaction ends.  The script that reads the trace file inserts ‘dbDatabase.BeginTrans where it believes that a transaction should start – remove the ‘ if that is the correct starting point for the transaction.&lt;/li&gt;
&lt;li&gt;Looping structures with nested SQL statements (the retrieval of the employee list from the EMPLOYEE_RECORD_TEST table and the probing of matching rows for each of those employees) cannot be reproduced automatically – you will have to recognize when one SQL statement is feeding the bind variable values of a second SQL statement.&lt;/li&gt;
&lt;li&gt;The VBS script assumes that up to 20 cursors will be open at any one time, but will automatically handle many more than 20 simultaneously open cursors.  The resulting VBS file should be cleaned up to remove the unneeded comData and snpData objects.&lt;/li&gt;
&lt;li&gt;There are spurious snpData&lt;em&gt;n&lt;/em&gt;.Close statements – see the suggestions for improvements.&lt;/li&gt;
&lt;li&gt;SQL statements submitted without bind variables will not have code written to execute those statements in the generated VBS file - see the suggestions for improvements.&lt;/li&gt;
&lt;li&gt;Bind variables that are submitted as VARCHAR (adVarchar) are written to the trace file as if the bind variables were declared as CHAR (adChar) – while this does not appear to cause a problem, it might appear to be an unexpected change when comparing the test script with the automatically generated script.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Suggestions for improvement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recognize the EXEC line in the 10046 trace and use that to actually indicate that a SQL statement should execute in the generated script, rather than executing the SQL statement in response to the submission of bind variables.&lt;/li&gt;
&lt;li&gt;Allow submitting the source trace file name and the destination (generated) VBS filename on the command line.&lt;/li&gt;
&lt;li&gt;Allow submitting the username, password, and database name on the command line or in a web-based user interface.&lt;/li&gt;
&lt;li&gt;Correct the script so that it does not attempt to close recordsets when those recordsets were never opened at dep=0 – this is caused by the script seeing a recursive SQL statement that is preparing to open with that cursor number.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The VBS script that converts 10046 trace files to VBS script files may be downloaded here: &lt;a href="http://hoopercharles.files.wordpress.com/2010/03/tracetovbs-vbs.doc"&gt;TraceToVBS.vbs&lt;/a&gt; (save as TraceToVBS.vbs).  There may be bugs in the script, but it should be close enough to provide some degree of educational benefit.&lt;/p&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/wigna_c4prA" height="1" width="1"/&gt;</description><feedburner:origLink>http://hoopercharles.wordpress.com/2010/03/12/from-a-vbs-script-to-a-10046-trace-and-back-into-a-vbs-script</feedburner:origLink></item><item><title>Hotsos 2010 – A Training Day with Tanel Poder</title><link>http://feedproxy.google.com/~r/orana_dba/~3/OCXnZrX3J6I/</link><category>Events</category><category>Oracle</category><category>Performance</category><category>hotsos 2010</category><category>Tanel Põder</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Marco Gralike</dc:creator><pubDate>Thu, 11 Mar 2010 17:16:39 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/e812298d799f278c</guid><description>&lt;p&gt;My last day already, so I hereby leave you with some impressions from Tanel’s Training Day. Maybe until next year.&lt;/p&gt;
&lt;p&gt;&lt;embed src="http://www.youtube.com/v/NOg2FNiPW24&amp;amp;hl=nl_NL&amp;amp;fs=1&amp;amp;" allowScriptAccess="never" allowFullScreen="true" width="480" height="345" wmode="transparent" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/p&gt;
&lt;p&gt;If you also want to learn from him, then &lt;a href="http://www.miraclebenelux.nl/index.php?option=com_events&amp;amp;task=view_detail&amp;amp;agid=16&amp;amp;year=2010&amp;amp;month=4&amp;amp;day=6&amp;amp;Itemid=34&amp;amp;catids=54%7C55"&gt;here’s&lt;/a&gt; your chance in Holland. Scripts and tools used by Tanel can be found &lt;a href="http://tech.e2sn.com/oracle-scripts-and-tools"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;img src="http://www.liberidu.com/blog/wp-includes/images/smilies/icon_cool.gif" alt="8-)"&gt; &lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/Bloggralikecom/~4/8fuAHHYw0jA" height="1" width="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/OCXnZrX3J6I" height="1" width="1"/&gt;</description><feedburner:origLink>http://feedproxy.google.com/~r/Bloggralikecom/~3/8fuAHHYw0jA/</feedburner:origLink></item><item><title>Friday Philosophy – CABs {an expensive way to get nowhere?}</title><link>http://feedproxy.google.com/~r/orana_dba/~3/Df9AZahhEBY/</link><category>Friday Philosophy</category><category>Management</category><category>biology</category><category>development</category><category>perception</category><category>system development</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">mwidlake</dc:creator><pubDate>Thu, 11 Mar 2010 15:24:21 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/c4807ef88597b05a</guid><description>&lt;p&gt;A few years ago, my wife and I went to New York for a holiday. We got a cab from the airport into Manhattan. It was an expensive way to see, at great length, some of the more uninteresting automobile transit routes through New York. We arrived at our hotel a great deal later than we anticipated. And with most of our paper dollars no longer in our possession.&lt;/p&gt;
&lt;p&gt;I’ve also taken cabs through London, usually at the weekend to get back to Liverpool Street Station. The trip is generally quick, painless and not too expensive. Those black-cab drivers know their stuff.&lt;/p&gt;
&lt;p&gt;Of course, the CABs I refer to in the title of this Friday Philosophy are not private cars for hire. In this context CAB is &lt;strong&gt;Change Advisory Board&lt;/strong&gt;. A term that can make grown developers weep. If you do not know, the Change Advisory Board is a group of people who look at the changes that are planed for a computer system and decide if they are fit for release. My personal experience of them has been similar to my experience of the taxi variety, though sadly more of the New York than London variety.&lt;/p&gt;
&lt;p&gt;You might expect me to now sink into a diatribe {ie extended rant} about how I hate CABs. Well, I don’t. CABs can be a part of a valuable and highly worthwhile process control mechanism. Just as proper QA is core to any mature software development process, so CABs are important in getting informed, talented stakeholders to review proposed changes. They check for overall system impact, clashes with other proposed changes that individual development streams may be unaware of and to verify Due Diligence has been invoked {that last one is a bit of a minefield and where, I believe, most CABs fail}.&lt;/p&gt;
&lt;p&gt;Sadly, though this is often the aim. The end result is a bunch of uninformed and technically naive politicos trying to wield power, using the CAB meeting as an extended game of management chess.&lt;/p&gt;
&lt;p&gt;I’ve seen CABs trade changes. “I’ll let you have X if I can have Y and Z”. I’ve seen CABs turn down changes because the form had spelling mistakes in it. I’ve seen CABs object to a change that will save the company 5 million pounds a day because it lacked a signature. &lt;/p&gt;
&lt;p&gt;That last one just stopped me in my tracks {I’m not exaggerating either, if anything I am underplaying the cost impact of that decision. I saw the figures and I wasted a couple of days of my life checking, 5 million pounds a day was the least I felt I could prove.} We are talking about enough money &lt;em&gt;every day&lt;/em&gt; to pay the salary of everyone on the CAB for several years. And they blocked it because the DBA team had not signed off the change. &lt;/p&gt;
&lt;p&gt;The DBA Team had not signed off the change because the one and only DBA Team Leader who was allowed to sign off was on holiday for two weeks. They needed that holiday too, for other but I suspect linked reasons.&lt;/p&gt;
&lt;p&gt;Now I knew the DBA Team Lead and he was a good bloke, he knew his stuff and he was not paid 5 million pounds a day. His deputy was paid even less but was no less talented but she was not allowed to sign off the change as she was not the DBA Team Lead.&lt;/p&gt;
&lt;p&gt;That was a CAB gone very wrong. The process of the CAB had been allowed to over-rule good business sense. It was also overruling general and technical sense, but that really is secondary to what keeps the business making a profit.&lt;/p&gt;
&lt;p&gt;I’ve seen the opposite of course, technical teams that just apply whatever changes they feel are fit and, to be honest, they seem to mess up less often than a &lt;em&gt;poor CAB process&lt;/em&gt; as they know they are the ones who will spend the weekend fixing a mess if one occurs. But that mess will occur eventually, if control is lacking.&lt;/p&gt;
&lt;p&gt;So, I feel CABs are good, if you have the right people on them and you have a sensible cascade of authority so one person being away does not block the system. That is quite a bit harder to put in place than a simple “Dave A, John, Andrea, Alex, Raj, Dave P, Mal, Malcolm  and Sarah have final signoff” which most CABs effecively become.&lt;/p&gt;
&lt;p&gt;But there is one last fault of CABs I want to highlight. They tend to treat all changes in the same way and all changes are not the same. Upgrading the underlying OS is not the same as adding a cardinality hint to one Business Objects report.&lt;/p&gt;
&lt;p&gt;If your CAB or change process treat the two above examples the same, then your CAB or change process is broken. Now, in all IT “rules of thumb” there is an exception. In this case, I am truly struggling to think of one. If your change process treats an OS upgrade the same as adding a hint to a report, it is not fit for purpose. That is my main issue with CABs. They should be of significant business importance, but nearly always they are implemented with one process to deal with all situations and then get taken over by people with an “Office Politics” agenda as opposed to a “Getting the best job we can reasonably expect done” agenda.&lt;/p&gt;
&lt;p&gt;I’m very passionate about this and I have a way I hope can throw this issue into context, an analogy.&lt;/p&gt;
&lt;p&gt;Ask yourself one this senario.&lt;br&gt;
You go to your Doctor with a niggly cough you have had for a week OR you go to your doctor because you almost passed out each day you got out of bed for the last three days.&lt;br&gt;
If your doctor treated you the same for &lt;em&gt;both sets of symptoms&lt;/em&gt;, would you be happy with that doctor?&lt;/p&gt;
&lt;p&gt;Why are all IT changes handled by most CABs in exactly the same way?&lt;/p&gt;
&lt;p&gt;I could say no one dies when it comes to IT, but you work in healthcare computing for a while and that argument falls apart a little.&lt;/p&gt;
&lt;p&gt;Oh dear, got a bit “deep” there!&lt;/p&gt;
&lt;p&gt;(BTW if you ever almost collapse when you get out of the bed in the morning, do NOT go to work, go instead to your doctor and ask them for a full medical and if he/she does not take blood pressure tests and order a full blood chemisty test, go find a new doctor.)&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/mwidlake.wordpress.com/774/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/mwidlake.wordpress.com/774/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/mwidlake.wordpress.com/774/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/mwidlake.wordpress.com/774/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/mwidlake.wordpress.com/774/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/mwidlake.wordpress.com/774/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/mwidlake.wordpress.com/774/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/mwidlake.wordpress.com/774/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/mwidlake.wordpress.com/774/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/mwidlake.wordpress.com/774/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=mwidlake.wordpress.com&amp;amp;blog=7382739&amp;amp;post=774&amp;amp;subd=mwidlake&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/Df9AZahhEBY" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://1.gravatar.com/avatar/71518fe903d8f40e558541c20ae75281?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://mwidlake.wordpress.com/2010/03/11/friday-philosophy-cabs-an-expensive-way-to-get-nowhere/</feedburner:origLink></item><item><title>Upgrading to R12.1.2, Tip #1</title><link>http://feedproxy.google.com/~r/orana_dba/~3/P39QMYngOG4/upgrading-to-r1212-tip-1.html</link><category>Oracle Apps</category><category>DBA</category><category>R12.1</category><category>oracle</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Jeff Hunter</dc:creator><pubDate>Thu, 11 Mar 2010 15:11:00 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/1059670fbe2afa8c</guid><description>While applying patch 7303033 on top of a 12.1.0 R12 installation, I got the following error in one of my workers:&lt;br&gt;&lt;br&gt;FNDLOAD APPS/***** 0 Y UPLOAD @FND:patch/115/import/afscursp.lct @JTF:patch/115/import/US/jtfdiagresp.ldt - &lt;br&gt;&lt;br&gt;Connecting to APPS......Connected successfully.&lt;br&gt;&lt;br&gt;Calling FNDLOAD function.&lt;br&gt;&lt;br&gt;Returned from FNDLOAD function.&lt;br&gt;&lt;br&gt;Log file: /r12u/apps/apps_st/appl/admin/UPG/log/&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/P39QMYngOG4" height="1" width="1"/&gt;</description><feedburner:origLink>http://marist89.blogspot.com/2010/03/upgrading-to-r1212-tip-1.html</feedburner:origLink></item><item><title>Hotsos 2010 - Monique</title><link>http://feedproxy.google.com/~r/orana_dba/~3/KK6mzOWLoU8/index.php</link><category>conferences</category><category>cuddly toys</category><category>hotsos 2010</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dougburns@yahoo.com (Doug Burns)</dc:creator><pubDate>Thu, 11 Mar 2010 14:10:00 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/dbbab3cdfabcaf53</guid><description>&lt;img height="640" width="512" src="http://oracledoug.com/serendipity/uploads/monique.jpg" style="border:0px none;padding-left:5px;padding-right:5px" alt=""&gt;&lt;br&gt;&lt;br&gt;There she goes, squirreling into the corner of your carry-on.  Ready for another adventure.  Tucked between a fun floppy hat and the flourescent sun-block, of which she uses plenty, pale creature that she is, Monique is situated to prove to you that a seasoned traveller knows best.  Dressed in a curly mohair (so as not to show the wrinkles that betray one new to the jet set) and sporting a pale chiffon scarf - to double as a disquise in those cities where she is already known for her somewhat silly escapades - she seems to wink at you and beckon you along.  Forget your cares, but by no means your playing cards, and step lightly into whatever dreams your next voyage should hold for you:  let Monique be your guide.  &lt;br&gt; &lt;br&gt;Her very presence alleviates all the maladies brought on by the jostling of trains, the dipping of ships, the swerving of buses and even by the simple fact that you are far from home.  For she has discovered the secret to successful travel; with the exception of the luscious few minutes in the X-ray machine, where she delights in making funny faces at the operators searching for more dangerous contents.  Monique never looks back.&lt;br&gt; &lt;br&gt;&lt;a href="http://www.charleenkinserdesigns.com/"&gt;Charleen Kinser Designs&lt;/a&gt; &lt;br&gt;&lt;br&gt;(With thanks to Carol Dacko for bringing along an interesting dinner companion for my own cuddly friends. Somehow I doubt they'll ever be the same - she's terribly sophisticated for my lot!)&lt;br&gt;&lt;img src="http://feeds.feedburner.com/~r/DougsOracleBlog/~4/jBOmtgW5uwU" height="1" width="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/KK6mzOWLoU8" height="1" width="1"/&gt;</description><feedburner:origLink>http://feedproxy.google.com/~r/DougsOracleBlog/~3/jBOmtgW5uwU/index.php</feedburner:origLink></item><item><title>Hotsos 2010 - What's THAT?</title><link>http://feedproxy.google.com/~r/orana_dba/~3/HyQN3sjQHYw/index.php</link><category>conferences</category><category>cuddly toys</category><category>hotsos 2010</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dougburns@yahoo.com (Doug Burns)</dc:creator><pubDate>Thu, 11 Mar 2010 14:00:00 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/f5067482097f5ae3</guid><description>I heard someone bemoaning the lack of Swag at Hotsos - just an event program, couple of magazines and a small clockwork toy sponsored by Oracle. I don't think I ever come to the Symposium expecting Swag - it's just not that kind of event. But you do tend to get good speaker gifts, including &lt;a href="http://oracledoug.com/serendipity/index.php?/archives/843-Hotsos-Day-1.75.html"&gt;the best speaker gift I ever received&lt;/a&gt;. This year, it's a digital photo frame.&lt;br&gt;&lt;br&gt;As for that clockwork toy, the Cuddly Toys think it&amp;#39;s *brilliant*!!! At first, their reaction was &amp;quot;What&amp;#39;s THAT?!&amp;quot; then when I wound it up and let it dance in front of them, they fell in love.&lt;br&gt;&lt;br&gt;&lt;img height="414" width="336" src="http://oracledoug.com/serendipity/uploads/whatsthat1.jpg" style="border:0px none;padding-left:5px;padding-right:5px" alt=""&gt;&lt;br&gt;&lt;br&gt;The only thing is that it doesn&amp;#39;t run for long per wind-up, so my right wrist is aching from them continually beggind me to &amp;quot;Do it again, Douglas!&amp;quot;&lt;br&gt;&lt;br&gt;Then again, their reaction wasn't quite as relaxed as when they met Carol Dacko's friend while we were at the restaurant. Apologies for the quality of the photo, it was dark in there, but at least it proves that Little H and Chris were allowed out for dinner and that not all Cuddly Toys are made the same.&lt;br&gt;&lt;br&gt;&lt;img height="252" width="448" style="border:0px none;padding-left:5px;padding-right:5px" src="http://oracledoug.com/serendipity/uploads/whatsthat2.jpg" alt=""&gt;&lt;br&gt;&lt;br&gt;They were just stunned by their new friend. What's THAT?&lt;br&gt;&lt;br&gt;All will be revealed in the next post ....&lt;br&gt;&lt;img src="http://feeds.feedburner.com/~r/DougsOracleBlog/~4/bK2LsrEY7aU" height="1" width="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/HyQN3sjQHYw" height="1" width="1"/&gt;</description><feedburner:origLink>http://feedproxy.google.com/~r/DougsOracleBlog/~3/bK2LsrEY7aU/index.php</feedburner:origLink></item><item><title>Hotsos 2010 - Day 4</title><link>http://feedproxy.google.com/~r/orana_dba/~3/3i1OmntiJBk/index.php</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dougburns@yahoo.com (Doug Burns)</dc:creator><pubDate>Thu, 11 Mar 2010 10:25:35 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/2e4117d2b7518c98</guid><description>Hotsos 2010 - Day 4&lt;br&gt;&lt;br&gt;First up was Cary Millsap's - Lessons Learned, Version 2010.03 As Cary pointed out, they always try to put the best speakers in the toughest slots - 8:30 in the morning post-party. I think local guys are slightly more reliable too because they might have actually gone home the night before! He started with a quick Hangover Survey (me - check!) and then pressed on talking about how we test system performance.&lt;br&gt;&lt;br&gt;He showed a video of Boeing stress-testing the wing of the 787 and, as he pointed out, aircraft manufacturers really know how to stress-test! (Of course whether that reassures you as it does me, or makes you wish no-one would talk about wings disintegrating, as it probably would Mads, is personal.) They showed Boeing test equpiment which is complicated, expensive and non-revenue generating. Those tests are expensive but when people's lives are on the line, what choice is there? Boeing knows that it has to test the analytic models used in the design. He spent a lot of time talking about good test design. A few thoughts that stood out to me ...&lt;br&gt;&lt;br&gt;- Some stress tests are a waste of time. Will the Boeing 787 land on the moon? If this test fails, what has it proven? If it passes, then it's awesome but it would be a very expensive way to prove it can cope with commercial flights in Earth's atmosphere.&lt;br&gt;&lt;br&gt;- Why test for more than you will see in Production? Because you don't really know for sure what you'll see in Production.&lt;br&gt;&lt;br&gt;- At some point, but I can't remember the context, he used a Scottish phrase that he'd heard Billy Connolly shout (although the Big Yin was only fully credited later in the day) ...&lt;br&gt;&lt;br&gt;    &amp;quot;&lt;a href="http://en.wikiquote.org/wiki/Billy_Connolly"&gt;&lt;em&gt;There's no such thing as bad weather, just the wrong clothes&lt;/em&gt;&lt;/a&gt;&amp;quot;&lt;br&gt;&lt;br&gt;... looked over at me and said - &amp;quot;I&amp;#39;d love to hear you say that, with the proper accent&amp;quot;. I declined politely.&lt;br&gt;&lt;br&gt;- Most people try to prove only that their systems will work.&lt;br&gt;&lt;br&gt;- Most tests of systems that are destined to fail never proved it in advance.&lt;br&gt;&lt;br&gt;- Test to destruction&lt;br&gt;&lt;br&gt;    a) Test&lt;br&gt;    b) Until the system melts&lt;br&gt;    c) Decide whether your real requirements are likely to be lower or higher than melting point.&lt;br&gt;&lt;br&gt;There was a small amount of time for questions and once it looked like they were done, I granted Cary's wish (never thought I'd say that), stuck my hand up and repeated The Big Yin's words. It was only after the laughter had stopped that I realised I might have ruined his big closing, but I think he was ok about it &lt;img src="http://oracledoug.com/serendipity/templates/default/img/emoticons/wink.png" alt=";-)" style="display:inline;vertical-align:bottom"&gt;&lt;br&gt;&lt;br&gt;Next was Tanel Poder talking about LGWR, log file sync waits and COMMIT performance and shock, horror, I was actually going to say that this was one of the least rewarding sessions of the week for me. What?!? Tanel? But he's, like, an Oracle God! LOL But there were reasons&lt;br&gt;&lt;br&gt;- I realise that I know a *lot* about how log file sync and log file parallel write work, how they relate to each other and some of the problems they might help you identify. Because it's a subject I'm *so* familiar with, I didn't learn much.&lt;br&gt;&lt;br&gt;- His main demo didn't quite show what he wanted it to because it didn't run multiple sessions but, frankly, I'm in no position to talk about demos this week!&lt;br&gt;&lt;br&gt;By the end, the presentation turned out ok, not least because there was another unexpected appearance from Bob Sneed to talk about the I/O components involved in  redo log management including a suggestion that LGWR be put into a higher scheduling class (but not Real Time!) I&amp;#39;ll try to find a link to his slides and let you take a look yourself.&lt;br&gt;&lt;br&gt;I loved Tanel's Big Log File Sync Tuning Secret, though ...&lt;br&gt;&lt;br&gt;    COMMIT LESS!&lt;br&gt;&lt;br&gt;It was particularly relevant to me because I had a Big Log File Sync Tuning Secret as the closing moment of my own presentation. The problem was I couldn't use it after the demos went wrong!&lt;br&gt;&lt;br&gt;    USE ASYNCHRONOUS COMMITS&lt;br&gt;&lt;br&gt;But, in my case, that was supposed to be funny, too.&lt;br&gt;&lt;br&gt;I ran off to try and use the free breakfast voucher that Marco had given me but I was just too late. No food again, then &lt;img src="http://oracledoug.com/serendipity/templates/default/img/emoticons/sad.png" alt=":-(" style="display:inline;vertical-align:bottom"&gt; Well, I had a couple of slices of cold meat at lunchtime, but mainly to catch up with Alex G before he had to present and then head back to Ottawa. I managed to skip one session at this stage but, after a quick call home, I decided to go along to Alex's RAC Connection Management presentation after all (a little late). Although I have seen some of this stuff before, I always enjoy watching Alex's demos and was particularly impressed by the fact that he'd managed to write his own RAC connection load balancer! I was waiting for the applause in the room but either people didn't quite get it or there was just a lack of energy post-lunch on the last day. I suspect the latter.&lt;br&gt;&lt;br&gt;Of course, once I'd said goodbye to Alex properly (don't see him nearly enough), I was a little late for whichever session was going to be my final one of the conference and I was hopelessly torn between Kyle Hailey's modern SQL performance tools presentation (Kyle's done a lot of cool work in the area of Oracle Performance Visualisation) and Chris Antognini's Diagnosing Parallel Executions Performance. In the end I plumped for the latter because I thought it was going to be like something &lt;a href="http://oracledoug.com/px_profiles.pdf"&gt;I'd unsuccessfully attempted&lt;/a&gt; a couple of years ago and I wanted to see if Chris had a different angle on it and had been more successful. In the end, I probably made the wrong choice because although Chris' presentation was great, it was really all stuff I already knew. Definitely my bad call, though. Hopefully I'll get a chance to catch up with Kyle's presentation at some point in the future too!&lt;br&gt;&lt;br&gt;After that there was just the usual short farewell and thanks from Gary Goodman of Hotsos. Although the thanks were appreciated, I'm glad they were spread around everybody because the attendees are one of the things that make this conference great and Becky and Rhonda did their usual sterling job of organising everything.&lt;br&gt;&lt;br&gt;Then it was time for some Fajitas with a few friends (actually, a whopping great number of friends who practically filled the Mexican restaurant!) and a few very sedate beers. (We are old men (and women) now and the night before had a big one!) While we were waiting to go to the Mexican, I had one great surprise left - Alex's flights weren't going to get him home, so he came back from the airport and had to check in overnight! At least I got a chance to talk to him properly when I wasn't hopelessly drunk and didn't try to seduce him this time.&lt;br&gt;&lt;br&gt;Now I need to stop blogging and get back to listening to Tanel's Training Day (good stuff, too, but more about that later)&lt;img src="http://feeds.feedburner.com/~r/DougsOracleBlog/~4/YLuk8YKwGUU" height="1" width="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/3i1OmntiJBk" height="1" width="1"/&gt;</description><feedburner:origLink>http://feedproxy.google.com/~r/DougsOracleBlog/~3/YLuk8YKwGUU/index.php</feedburner:origLink></item><item><title>Dropping OUTLN</title><link>http://feedproxy.google.com/~r/orana_dba/~3/-CHyPvvkc4o/</link><category>Infrastructure</category><category>Troubleshooting</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Jonathan Lewis</dc:creator><pubDate>Thu, 11 Mar 2010 09:35:16 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/15210a9025aa1154</guid><description>I had a very pleasant day yesterday at a SIG meeting of the UK Oracle User Group where I did the presentation about “drawing your SQL” that is the basis of the article I wrote for Simple Talk a little while ago.
One of the other sessions had the entertaining title: “What happens if you drop [...]&lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathanlewis.wordpress.com&amp;amp;blog=491988&amp;amp;post=3351&amp;amp;subd=jonathanlewis&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/-CHyPvvkc4o" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/08b4eccce68cd521b54671abb0442ae1?s=96&amp;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&amp;r=G" /></media:group><feedburner:origLink>http://jonathanlewis.wordpress.com/2010/03/11/dropping-outln/</feedburner:origLink></item><item><title>20. - 21. May EMEA Harmony Conference in Tallinn</title><link>http://feedproxy.google.com/~r/orana_dba/~3/xFbAr31Ds9Q/20-21-may-emea-harmony-conference-in.html</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">ilmar.kerm@gmail.com (Ilmar Kerm)</dc:creator><pubDate>Wed, 10 Mar 2010 23:03:00 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/ab4f2a45cc30c025</guid><description>&lt;p&gt;An excellent joint conference for Estonian, Finnish, Latvian and Russian Oracle User Groups in Tallinn, 20. - 21. May 2010.&lt;br&gt;
Speakers also include Tom Kyte, Tanel Põder, Chris J. Date and Steven Feuerstein.&lt;/p&gt;

&lt;p&gt;Read the agenda &lt;a href="http://ougf.fi/index.php?option=com_docman&amp;amp;task=doc_download&amp;amp;gid=305&amp;amp;Itemid=42%E2%8C%A9%3Dfi&amp;amp;lang=en"&gt;here&lt;/a&gt; and register in &lt;a href="http://ougf.fi/index.php?lang=en"&gt;OUGF home page&lt;/a&gt; (250€+VAT registration fee).&lt;/p&gt;

&lt;p&gt;In addition, just before the conference, 17.-18. May, Chris Date will perform his "How to Write Correct SQL and Know It: A Relational Approach to SQL" seminar in Helsinki. &lt;a href="http://ougf.fi/index.php?option=com_content&amp;amp;view=article&amp;amp;id=85%3Acj-date-seminaari&amp;amp;catid=923&amp;amp;Itemid=2&amp;amp;lang=en"&gt;More info here&lt;/a&gt; and more detailed information &lt;a href="http://www.kantamestarit.fi/Files/Chris_Date_Seminar_Helsinki_2010.pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;div&gt;&lt;img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/8455543779867801145-1881008695708393911?l=ilmarkerm.blogspot.com" alt=""&gt;&lt;/div&gt;&lt;div&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ilmarkerm?a=xFbAr31Ds9Q:4E9TTGhnW7c:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ilmarkerm?d=yIl2AUoC8zA" border="0"&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ilmarkerm?a=xFbAr31Ds9Q:4E9TTGhnW7c:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ilmarkerm?i=xFbAr31Ds9Q:4E9TTGhnW7c:F7zBnMyn0Lo" border="0"&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ilmarkerm?a=xFbAr31Ds9Q:4E9TTGhnW7c:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ilmarkerm?i=xFbAr31Ds9Q:4E9TTGhnW7c:V_sGLiPBpWU" border="0"&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/xFbAr31Ds9Q" height="1" width="1"/&gt;</description><feedburner:origLink>http://ilmarkerm.blogspot.com/2010/03/20-21-may-emea-harmony-conference-in.html</feedburner:origLink></item><item><title>What does ‘extended’ mean when referring to RAC clusters</title><link>http://feedproxy.google.com/~r/orana_dba/~3/lXgvwN0Gct8/</link><category>Oracle</category><category>extended</category><category>extended RAC</category><category>RAC</category><category>rac distance between nodes</category><category>stretched cluster</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">John Hallas</dc:creator><pubDate>Thu, 11 Mar 2010 00:12:19 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/2aa76bb7cc39d134</guid><description>&lt;p&gt;Yesterday I attended and spoke at the Scotland DBA SIG. It was an enjoyable day with not one but two presentation from Julian Dyke. The second one was an overview of HA options and it in Julian mentioned extended RAC. Now I have worked at several sites which have used or wanted to use extended RAC and I have heard a number of views on what exactly the term extended means.&lt;/p&gt;
&lt;p&gt;From MoS Note 220970.1 we get the definitive statement on the maximum distance for extended RAC&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The high impact of latency create practical limitations as to where this architecture can be deployed. While there is not fixed distance limitation, the additional latency on round trip on I/O and a one way cache fusion will have an affect on performance as distance increases. For example tests at 100km showed a 3-4 ms impact on I/O and 1 ms impact on cache fusion, thus the farther distance is the greater the impact on performance. This architecture fits best where the 2 datacenters are relatively close (&amp;lt;~25km) and the impact is negligible. Most customers implement under this distance w/ only a handful above and the farthest known example is at 100km. Largest distances than the commonly implemented may want to estimate or measure the performance hit on their application before implementing. Do ensure a proper setup of SAN buffer credits to limit the impact of distance at the I/O layer.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So we now have figures that 100Km is technically possible but probably won’t work well and most sites use less than 25Km. Co-incidentally in the paragraph above that quotation it refers to SE RAC (10g) as being limited to having all nodes located in the same room.  So in two lines of text we have just gone from a 100ft to 100KMs.&lt;/p&gt;
&lt;p&gt;Yesterday Julian’s answer when I asked him the question was that he thought extended was defined as a couple of kilometres or above which is reasonable. However I am thinking that a better definition of extended is not to focus on distance of nodes apart but on what connects the nodes. My thoughts are that that if fibre channel or DWDM is involved and the nodes are not in the same building  then that would count as an extended RAC cluster despite potentially only being a 100 yards apart. Another view would be that if some form of array based mirroring is in place and RAC technology is in place then that again would define the system as ‘extended’ .&lt;/p&gt;
&lt;p&gt;Now that all fits with a view I have heard from a well-known RAC consultant from Oracle who defines any RAC system where nodes are in different rooms as extended.  I have never subscribed to that theory but I can see that the definition I provided in the previous paragraph would lead to that conclusion.&lt;/p&gt;
&lt;p&gt;Now we just need to consolidate on the word ‘extended’ rather than the use of  ’stretched’ which I see occasionally.&lt;/p&gt;
&lt;p&gt;I really do hope I get some comments on this piece as I am sure it is worthy of debate and comment and anything that helps to create a shared view on the matter can only be good.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jhdba.wordpress.com/570/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jhdba.wordpress.com/570/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jhdba.wordpress.com/570/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jhdba.wordpress.com/570/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jhdba.wordpress.com/570/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jhdba.wordpress.com/570/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jhdba.wordpress.com/570/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jhdba.wordpress.com/570/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jhdba.wordpress.com/570/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jhdba.wordpress.com/570/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jhdba.wordpress.com&amp;amp;blog=1223552&amp;amp;post=570&amp;amp;subd=jhdba&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/lXgvwN0Gct8" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://1.gravatar.com/avatar/bd215d7013d9c1179651d5524f40b981?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://jhdba.wordpress.com/2010/03/11/what-does-extended-mean-when-referring-to-rac-clusters/</feedburner:origLink></item><item><title>Select Statement Causing an ORA-00001?</title><link>http://feedproxy.google.com/~r/orana_dba/~3/ml-lLr4kQVQ/</link><category>General Administration</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Charles Hooper</dc:creator><pubDate>Wed, 10 Mar 2010 22:00:24 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/5b600d4bd5d6064a</guid><description>&lt;p&gt;March 11, 2010&lt;/p&gt;
&lt;p&gt;Sometimes I receive seemingly interesting emails showing Oracle errors – leaving me to ponder… certainly, that can’t cause an error, can it?  Here is one that I received a year ago (paraphrased):&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The commercially developed application that we are using displayed an error message identifying a SELECT statement as the source of an ORA-00001 error.  What is the source of the Oracle constraint error?  The error message displayed by the application is as follows:&lt;/p&gt;
&lt;pre&gt;select account_period
from PROJECT_SUMMARY
where project_id = :m_saProjSumProjectID[nProjSumIndex]
and id =:m_saProjSumSavedID[nProjSumIndex]
into :nACCOUNT_PERIOD

ORA-00001: unique constraint (TESTUSER.SYS_C006354) violated

This transaction has resulted in violating an Oracle defined constraint.
Constraints are enforced by the database manager.  This transaction has
been rolled back.&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;My first thought at the sight of this error was that the commercially developed application was actually displaying one of the SQL statements that was executed after the SQL statement which triggered the primary key violation.  It is easy to let a runtime error slide through for a period of time before the program notices that an error happened – maybe it is just a sign of sloppy programming (I hope not, because this has happened in some of my custom-developed programs too).&lt;/p&gt;
&lt;p&gt;How would we start to troubleshoot this error message?  The “SYS_C” portion of the constraint name indicates that the constraint is most likely a system generated constraint name, probably intended to enforce a uniqueness requirement for a primary key column.  Exporting the data from the database using Datapump export (or the legacy exp utility) and importing the data into a new database could cause the number following “SYS_C” to change, and it is likely that constraint SYS_C006354 in my database (that is used by the same application) is very different from that of the person who posed the question to me.&lt;/p&gt;
&lt;p&gt;Let’s see if we are able to find the answer by working the problem in a circular fashion.  For example, let’s find the name of the index that is used to enforce the primary key constraint on of one the application’s tables:&lt;/p&gt;
&lt;pre&gt;SELECT
  INDEX_NAME
FROM
  DBA_INDEXES
WHERE
  INDEX_NAME LIKE 'SYS%'
  AND TABLE_NAME='INVENTORY_TRANS';

INDEX_NAME
-----------
SYS_C005168&lt;/pre&gt;
&lt;p&gt;Now that we know that the index is named SYS_C005168, we could do something like this:&lt;/p&gt;
&lt;pre&gt;SELECT
  DC.OWNER,
  DC.CONSTRAINT_NAME,
  DC.CONSTRAINT_TYPE,
  DC.TABLE_NAME,
  DC.STATUS,
  SUBSTR(DCC.COLUMN_NAME,1,30) COLUMN_NAME
FROM
  DBA_CONSTRAINTS DC,
  DBA_CONS_COLUMNS DCC
WHERE
  DC.CONSTRAINT_NAME='SYS_C005168'
  AND DC.OWNER='TESTUSER'
  AND DC.OWNER=DCC.OWNER
  AND DC.CONSTRAINT_NAME=DCC.CONSTRAINT_NAME
ORDER BY
  DCC.POSITION;

OWNER   CONSTRAINT_NAME  CONSTRAINT_TYPE  TABLE_NAME       STATUS   COLUMN_NAME
------  ---------------  ---------------  ---------------  -------  --------------
SYSADM  SYS_C005168      P                INVENTORY_TRANS  ENABLED  TRANSACTION_ID&lt;/pre&gt;
&lt;p&gt;The above output shows that the primary key constraint SYS_C005168 enforces the uniqueness of the primary key (TRANSACTION_ID) column in the table INVENTORY_TRANS.  We just demonstrated that we now know what we already mostly knew.&lt;/p&gt;
&lt;p&gt;In the case of the person who sent the email to me, the table name was not known.  So, we could take the last of the above SQL statements and substitute SYS_C006354 in place of SYS_C005168 to find the table name and primary key column that was violated.  If the SQL statement failed to return usable information the next step might be to &lt;a href="http://hoopercharles.wordpress.com/2009/12/01/10046-extended-sql-trace-interpretation/"&gt;enable a 10046 trace&lt;/a&gt; at level 4 for one of the affected sessions, and try to reproduce the problem.  A 10046 trace will list the sequence of events that led up to the error message appearing in the client application.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/hoopercharles.wordpress.com/1665/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/hoopercharles.wordpress.com/1665/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/hoopercharles.wordpress.com/1665/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/hoopercharles.wordpress.com/1665/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/hoopercharles.wordpress.com/1665/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/hoopercharles.wordpress.com/1665/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/hoopercharles.wordpress.com/1665/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/hoopercharles.wordpress.com/1665/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/hoopercharles.wordpress.com/1665/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/hoopercharles.wordpress.com/1665/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hoopercharles.wordpress.com&amp;amp;blog=10738606&amp;amp;post=1665&amp;amp;subd=hoopercharles&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/ml-lLr4kQVQ" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://1.gravatar.com/avatar/feb0aae889ae283c65d94de211e7f8d9?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://hoopercharles.wordpress.com/2010/03/11/select-statement-causing-an-ora-00001/</feedburner:origLink></item><item><title>ODTUG Kaleidoscope Conference 2010</title><link>http://feedproxy.google.com/~r/orana_dba/~3/Xj544-qS7e4/odtug-kaleidoscope-conference-2010.html</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Optimizer Development Group</dc:creator><pubDate>Wed, 10 Mar 2010 19:57:40 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/c384ec7fde0b9367</guid><description>&lt;strong style="font-weight:normal"&gt;&lt;a style="font-weight:bold" href="http://www.odtugkaleidoscope.com/index.html"&gt;ODTUG Kaleidoscope 2010&lt;/a&gt;, &lt;span style="font-weight:bold"&gt;June 27 - July 1st Washington DC&lt;/span&gt; &lt;/strong&gt;&lt;span&gt;is a great conference for   Oracle developers and architects, offering the best content by renowned experts&lt;/span&gt;&lt;strong style="font-weight:normal"&gt;. &lt;/strong&gt;We will have the privilege of delivering two Optimizer sessions this year,  '&lt;a href="http://www.odtugkaleidoscope.com/databasedevelopment.html#colgan"&gt;Explaining the Explain plan&lt;/a&gt;' and '&lt;a href="http://www.odtugkaleidoscope.com/databasedevelopment.html#colgan"&gt;Finally Plan Stability during Database Upgrade with SQL Plan Management&lt;/a&gt;'. In the Explain the Explain plan session we will discuss each aspect of an &lt;a href="http://optimizermagic.blogspot.com/2008/02/displaying-and-reading-execution-plans.html"&gt;execution plan&lt;/a&gt; (from selectivity to parallel execution), explain what information you should be getting from the plan, and how it will affects the execution.  While in the &lt;a href="http://optimizermagic.blogspot.com/2009/01/plan-regressions-got-you-down-sql-plan.html"&gt;SQL Plan Management&lt;/a&gt; session, we will will give detailed instructions on how to capture your existing execution plans before you upgrade to 11g, as well as an in-depth discussion on what to expect from the Optimizer after you upgrade to 11&lt;em&gt;g&lt;/em&gt;.&lt;br&gt;ODTUG  is a great conference where you can learn lots in a fun and casual atmosphere.   Looking forward to seeing some of you there!&lt;div&gt;&lt;img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/3086558868352622884-6754966356556914384?l=optimizermagic.blogspot.com" alt=""&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/Xj544-qS7e4" height="1" width="1"/&gt;</description><feedburner:origLink>http://optimizermagic.blogspot.com/2010/03/odtug-kaleidoscope-conference-2010.html</feedburner:origLink></item><item><title>Clustering Factor: Row Migration’s Victim</title><link>http://feedproxy.google.com/~r/orana_dba/~3/uGxJwbRv5jY/</link><category>Performance</category><category>database</category><category>oracle</category><category>sql</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Tue, 09 Mar 2010 02:15:46 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/fffa95c0fa10e8d9</guid><description>&lt;p&gt;This article describes the effects of a high row migration rate on the clustering factor and the optimizer’s ability to select the best execution plan. &lt;/p&gt;
&lt;p&gt;In my previous article—&lt;a href="http://blog.fatalmind.com/2010/02/23/row-migration-and-row-movement/"&gt;Row Migration and Row Movement&lt;/a&gt;—I have demonstrated that the “insert empty, update everything” anti-pattern can lead to 100% row migration. This article continues the research on row migration and unveils surprising effects on the clustering factor. To be precise, the clustering factor can become &lt;em&gt;completely bogus&lt;/em&gt; in presence of a very high row migration rate. Once the clustering factor is “wrong”, it’s just a finger exercise to construct an optimizer trap and proof that row migration can affect the query plan.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;
&lt;p&gt;To start off, I create a table similar to the one in the previous article. However, I add one more column and populate it with random values from 0 to 9. I will need this column to build my optimizer trap later on.&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;CREATE TABLE row_mig1 (
  a CHAR(2000),
  b CHAR(2000),
  c CHAR(2000),
  x NUMBER      NOT NULL,
  filter NUMBER NOT NULL,
  CONSTRAINT row_mig1_pk PRIMARY KEY (x)
) ENABLE ROW MOVEMENT;

BEGIN
   FOR i IN 1..100000 LOOP
      INSERT INTO row_mig1
                    (x, filter)
             VALUES (i, trunc(dbms_random.value(0, 10)));

      UPDATE row_mig1
         SET a =&amp;#39;a&amp;#39;, b = &amp;#39;b&amp;#39;, c = &amp;#39;c&amp;#39;
       WHERE x=i;
   END LOOP;
END;
/
COMMIT;

EXEC DBMS_STATS.GATHER_TABLE_STATS(null, &amp;#39;ROW_MIG1&amp;#39;, CASCADE=&amp;gt;TRUE);&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Up till now everything is very similar to the last article, except that I used &lt;code&gt;DBMS_STATS&lt;/code&gt; instead of &lt;code&gt;ANALYZE TABLE&lt;/code&gt;. Although we know exactly that almost every row was migrated, &lt;code&gt;DBMS_STATS&lt;/code&gt; doesn’t care about that:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT num_rows, chain_cnt
       FROM user_tables
      WHERE table_name='ROW_MIG1';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
    100000          0

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, let’s have a look at the index:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT num_rows, leaf_blocks, clustering_factor
       FROM user_indexes
      WHERE index_name = 'ROW_MIG1_PK';&lt;/b&gt;

  NUM_ROWS LEAF_BLOCKS CLUSTERING_FACTOR
---------- ----------- -----------------
    100000         187               918

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, that’s an excellent clustering factor. A low clustering factor indicates that the index is in the same sequence as the table. That’s true in that case because the index column corresponds to the order in which the rows were inserted to the table. However, 918, how can this be? The clustering factor is supposed to be between the number of table blocks—which indicates a good clustering factor—and the number of index rows—which is the worst case. So, let’s look at the table size:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT t.blocks             table_blocks
          , i.clustering_factor  index_clustering_factor
          , i.num_rows           index_rows
       FROM user_indexes i
       JOIN user_tables t USING (table_name)
      WHERE index_name = 'ROW_MIG1_PK';&lt;/b&gt;

TABLE_BLOCKS INDEX_CLUSTERING_FACTOR INDEX_ROWS
------------ ----------------------- ----------
      100877                     918     100000

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;According to that, the lower bound for the clustering factor is higher than the upper bound. Hmm, let’s investigate the clustering factor manually and verify the distribution of the rows across the table blocks:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT * FROM (
       SELECT DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) block_number
            , COUNT(*) rows_in_block
         FROM row_mig1
        GROUP BY DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)
     ) WHERE ROWNUM &amp;lt;=10;&lt;/b&gt;

BLOCK_NUMBER ROWS_IN_BLOCK
------------ -------------
         523           109
         524           113
         525           109
         526           109
         527           110
         536           109
         537           110
         538           109
         539           109
         540           110

10 rows selected.

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are 109 different &lt;code&gt;ROWIDs&lt;/code&gt; that refer to the block number 523. But we know that each record has about 6k. It is impossible to fit 109 rows into a single block of 8k. However, the “insert empty, update everything” anti-pattern makes it possible. The game goes like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The very first row is inserted into a new block.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first row is updated, and fits into the same block. The free space in that particular block is still more than a kilobyte.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The second row is inserted into the very same block, because there is enough free space available in that block.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The update of the second row triggers the migration of that row to a different block.&lt;/p&gt;
&lt;p&gt;The row migration changes neither the &lt;code&gt;ROWID&lt;/code&gt; nor the index entry. That means that the forwarding address—that is, somehow, the new &lt;code&gt;ROWID&lt;/code&gt;—is stored in the original block so that the index can find the row.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The third row is inserted into the very first table block, again.&lt;/p&gt;
&lt;p&gt;Because the second row was moved into a different block, the very first block has still free space. There is only the first row—as a whole—and one forwarding address stored in that block. So, the insert of the third row can take place there.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;And so on. Until the forwarding addresses fill the block—up to &lt;code&gt;PCTFREE&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s a good one, hmm?&lt;/p&gt;
&lt;p&gt;We can even verify that:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT * FROM (
       SELECT MIN(X)                               min_x
            , MAX(x)                               max_x
            , MAX(x) - MIN(x) + 1                  diff_x
            , DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) block_number
            , COUNT(*)            rows_in_block
         FROM row_mig1
        GROUP BY DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)
        ORDER BY min_x
     ) WHERE ROWNUM &amp;lt;=10;&lt;/b&gt;

     MIN_X      MAX_X     DIFF_X BLOCK_NUMBER ROWS_IN_BLOCK
---------- ---------- ---------- ------------ -------------
         1        113        113          524           113
       114        222        109          523           109
       223        332        110          537           110
       333        441        109          538           109
       442        550        109          539           109
       551        660        110          540           110
       661        769        109          541           109
       770        878        109          542           109
       879        987        109          543           109
       988       1096        109          525           109

10 rows selected.

SQL&amp;gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;You see that the first row was inserted into block number 524. All subsequent rows up to the 113&lt;sup&gt;th&lt;/sup&gt; were put into the same block. When that block was finally filled up—with one row and 112 forwarding addresses—the game starts over in the next block. All the &lt;code&gt;INSERT&lt;/code&gt; statements took place in just 918 distinct blocks. Because the &lt;code&gt;ROWID&lt;/code&gt; is assigned during the &lt;code&gt;INSERT&lt;/code&gt;, the subsequent migration of the row due to the &lt;code&gt;UPDATE&lt;/code&gt; is not reflected in the &lt;code&gt;ROWID&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Neither &lt;code&gt;DBMS_STATS&lt;/code&gt; nor &lt;code&gt;ANALYSE TABLE&lt;/code&gt; look into the table to check if the row is really there or if the particular block it is just an accumulation of forwarding addresses. From their perspective, all the rows are in the same block—this is how the clustering factor is calculated. Although correctly calculated—technically—and up to date, the clustering factor of this index does not reflect the real situation.&lt;/p&gt;
&lt;p&gt;The “correct” value—in that sense that it reflects the data distribution correctly—for the clustering factor would be 100.000; that is, the number of rows. If the clustering factor equals the number of rows in the index—which is the worst possible case—it means that there are no two adjacent index entries that refer to the same table block. This is actually the case because no 8k block can contain two complete 6k rows.&lt;/p&gt;
&lt;h3&gt;The Clustering Factor is Wrong, So What?&lt;/h3&gt;
&lt;p&gt;So, what’s the problem if the clustering factor is wrong?&lt;/p&gt;
&lt;p&gt;The problem is that the Cost Based Optimizer (CBO) uses the clustering factor in its cost calculation for an &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; (see &lt;a href="http://www.centrexcc.com/Fallacies%20of%20the%20Cost%20Based%20Optimizer.pdf"&gt;Fallacies of the Cost Based Optimizer [pdf]&lt;/a&gt;). That means, the cost of the &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; will be too low, because the clustering factor is way too low. Luckily there is an effect that makes all that less problematic; all indexes on that table are affected.&lt;/p&gt;
&lt;p&gt;Let’s make a second index to verify that:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;CREATE INDEX row_mig1_idx ON row_mig1(filter);&lt;/b&gt;

Index created.

SQL&amp;gt; &lt;b&gt;BEGIN
       DBMS_STATS.GATHER_TABLE_STATS(null, &amp;#39;ROW_MIG1&amp;#39;,
                                     CASCADE =&amp;gt; TRUE);
     END;
     /&lt;/b&gt;

PL/SQL procedure successfully completed.

SQL&amp;gt; &lt;b&gt;SELECT i.index_name         index_name
          , t.blocks             table_blocks
          , i.clustering_factor  index_clustering_factor
          , i.num_rows           index_rows
       FROM user_indexes i
       JOIN user_tables t USING (table_name)
      WHERE table_name = 'ROW_MIG1';&lt;/b&gt;

INDEX_NAME   TABLE_BLOCKS INDEX_CLUSTERING_FACTOR INDEX_ROWS
------------ ------------ ----------------------- ----------
ROW_MIG1_PK        100877                     918     100000
ROW_MIG1_IDX       100877                    9180     100000

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The clustering factor of the new index is bigger than the one of the original index, but still way off its real value. The “correct” clustering factor for the new index would be 100.000 because there are no two adjacent index entries that refer to the table block. Well, they actually do, but there is nothing more than the forwarding address in those blocks. &lt;/p&gt;
&lt;div&gt;
&lt;h6&gt;Ten Times as High&lt;/h6&gt;
&lt;p&gt;The clustering factor is ten times as high because the filter column has ten distinct value. That means that the adjacent index entries will refer to ten blocks at least, because those entries were not inserted in the same order as they are stored in the index. On the other hand, the index on the &lt;code&gt;filter&lt;/code&gt; column is not only sorted by the &lt;code&gt;filter&lt;/code&gt; value, but also by the &lt;code&gt;ROWID&lt;/code&gt;—as every nonunique index in Oracle—so that the clustering factor is kept at a minimum. Finally, the clustering factor is ten times as high because it refers to ten times as many table blocks, but does not grow above that because the index order keeps the clustering factor at a minimum.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Although the clustering factor does not correctly reflect the efforts to fetch the table rows, it correctly reflects the relation between the two indexes. The primary key index has a lower clustering factor because it has the same sequence as the table itself. On the other side, the index on the &lt;code&gt;filter&lt;/code&gt; column has a different order, thus the value is higher. The side note explains why it is &lt;a href="http://blog.fatalmind.com/#TenTimesAsHigh"&gt;Ten Times as High&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If all indexes are affected, there is hardly any real problem I can see for a single table access. Even queries that can be executed with two different indexes, the CBO will most likely not do wrong because both clustering factors are misleading.&lt;/p&gt;
&lt;h3&gt;The Join Trap&lt;/h3&gt;
&lt;p&gt;If it’s not possible to confuse the optimizer with a single table, let’s use more of them. So, I try to build a trap where the optimizer’s decision of the join order is influenced by the phony clustering factor so that the optimizer takes the less efficient execution plan.&lt;/p&gt;
&lt;p&gt;For that purpose, I build a second table very similar to the first one. There are only two differences:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I don’t follow the “insert empty, update everything” anti-pattern—there will be no row migration.&lt;/li&gt;
&lt;li&gt;The selectivity of the &lt;code&gt;filter&lt;/code&gt; columns is slightly increased (about 6% instead of 10%).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is the overall script:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;CREATE TABLE row_mig2 (
  a CHAR(2000),
  b CHAR(2000),
  c CHAR(2000),
  x NUMBER      NOT NULL,
  filter NUMBER NOT NULL,
  CONSTRAINT row_mig2_pk PRIMARY KEY (x)
) ENABLE ROW MOVEMENT;

INSERT INTO row_mig2 (x, filter, a, b, c)
     SELECT level, trunc(dbms_random.value(0,17)), &amp;#39;a&amp;#39;, &amp;#39;b&amp;#39;, &amp;#39;c&amp;#39;
       FROM dual CONNECT BY level &amp;lt;= 100000;
COMMIT;

CREATE INDEX row_mig2_idx ON row_mig2(filter);

EXEC DBMS_STATS.GATHER_TABLE_STATS(null, &amp;#39;ROW_MIG2&amp;#39;, CASCADE=&amp;gt;TRUE);&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now let’s check the statistics:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT i.index_name         index_name
          , t.blocks             table_blocks
          , i.clustering_factor  index_clustering_factor
          , i.num_rows           index_rows
       FROM user_indexes i
       JOIN user_tables t USING (table_name)
      WHERE table_name = 'ROW_MIG2';&lt;/b&gt;

INDEX_NAME   TABLE_BLOCKS INDEX_CLUSTERING_FACTOR INDEX_ROWS
------------ ------------ ----------------------- ----------
ROW_MIG2_PK        100749                  100000     100000
ROW_MIG2_IDX       100749                  100000     100000

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Please note that the clustering factor is at the upper bound of the expected range; that means, it indicates that there are no two adjacent index entries referring to the same table block. That’s somehow logical, if we consider that every row is in its own table block.&lt;/p&gt;
&lt;p&gt;After that preparation, I can present my “trapQL”:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;SELECT *
  FROM row_mig1 d1
  JOIN row_mig2 d2 ON (d1.x = d2.x)
 WHERE d1.filter = 0
   AND d2.filter = 0;&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is a rather trivial join on the primary keys. Additionally each table is filtered on the &lt;code&gt;filter&lt;/code&gt; column for one particular value. The trap works because a &lt;code&gt;NESTED LOOPS&lt;/code&gt; join is possible in both ways. Either by filtering the first table by an &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; on &lt;code&gt;filter&lt;/code&gt; and then fetch the corresponding entry from the second by a primary key lookup, or vice versa. However, because we—as well as the optimizer—know that the second table is more selective than the first one, the more efficient way to execute that query is to first perform the &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; on the second table and then join in the first one. In that way, the number of primary key lookups is reduced and the overall performance will be better. Considering that the first table suffers from heavy row migration, that effect becomes even more relevant. However, it is of course the purpose of the discussion to proof that the optimizer is doing “wrong”:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;SET AUTOTRACE TRACEONLY;
SET TIMING ON;

SELECT /* original clustering factor */ *
  FROM row_mig1 d1
  JOIN row_mig2 d2 ON (d1.x = d2.x)
 WHERE d1.filter = 0
   AND d2.filter = 0;

SET TIMING OFF
SET AUTOTRACE OFF&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;And the result is:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;577 rows selected.

Elapsed: &lt;b&gt;00:01:08.35&lt;/b&gt;

Execution Plan
----------------------------------------------------------
Plan hash value: 4162018446

---------------------------------------------------------------------
| Id | Operation                     | Name         | Rows  | Cost  |
---------------------------------------------------------------------
|  0 | SELECT STATEMENT              |              |  5882 | 10941 |
|  1 |  NESTED LOOPS                 |              |       |       |
|  2 |   NESTED LOOPS                |              |  5882 | 10941 |
|  3 |    TABLE ACCESS BY INDEX ROWID| ROW_MIG1     | 10000 |   &lt;b&gt;938&lt;/b&gt; |
|* 4 |     &lt;b&gt;INDEX RANGE SCAN          | ROW_MIG1_IDX | 10000&lt;/b&gt; |    20 |
|* 5 |    INDEX UNIQUE SCAN          | ROW_MIG2_PK  |     1 |     0 |
|* 6 |   TABLE ACCESS BY INDEX ROWID | ROW_MIG2     |     1 |     1 |
---------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("D1"."FILTER"=0)
   5 - access("D1"."X"="D2"."X")
   6 - filter("D2"."FILTER"=0)

Statistics
----------------------------------------------------------
        913  recursive calls
          0  db block gets
      &lt;b&gt;35584&lt;/b&gt;  consistent gets
      21027  physical reads
          0  redo size
      39012  bytes sent via SQL*Net to client
        837  bytes received via SQL*Net from client
         40  SQL*Net roundtrips to/from client
         12  sorts (memory)
          0  sorts (disk)
        577  rows processed&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The optimizer has chosen to perform the &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; on &lt;code&gt;ROW_MIG1_IDX&lt;/code&gt; first. The optimizer is well aware of the fact that the &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; will return about 10000 rows; still it was preferred over the alternative execution plan.&lt;/p&gt;
&lt;p&gt;So let’s check what happens if we tell the optimizer the truth about that table’s indexes?&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;BEGIN
  DBMS_STATS.SET_INDEX_STATS(null, &amp;#39;ROW_MIG1_PK&amp;#39;, clstfct=&amp;gt;100000);
  DBMS_STATS.SET_INDEX_STATS(null, &amp;#39;ROW_MIG1_IDX&amp;#39;,clstfct=&amp;gt;100000);
END;
/

SET AUTOTRACE TRACEONLY;
SET TIMING ON;

SELECT /* updated clustering factor */ *
  FROM row_mig1 d1
  JOIN row_mig2 d2 ON (d1.x = d2.x)
 WHERE d1.filter = 0
   AND d2.filter = 0;

SET TIMING OFF
SET AUTOTRACE OFF&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The only change is that the statistics have been manually updated to the “more correct” clustering factor of 100.000. Unfortunately neither &lt;code&gt;DBMS_STATS&lt;/code&gt; nor &lt;code&gt;ANALYZE TABLE&lt;/code&gt; can be used for that purpose, so I did it manually. Please note that the table itself was not changed; most of the rows are still migrated. &lt;/p&gt;
&lt;p&gt;And the result is:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;577 rows selected.

Elapsed: &lt;b&gt;00:00:59.91&lt;/b&gt;

Execution Plan
----------------------------------------------------------
Plan hash value: 3004301745

---------------------------------------------------------------------
| Id | Operation                     | Name         | Rows  | Cost  |
---------------------------------------------------------------------
|  0 | SELECT STATEMENT              |              |  5882 | 11780 |
|  1 |  NESTED LOOPS                 |              |       |       |
|  2 |   NESTED LOOPS                |              |  5882 | 11780 |
|  3 |    TABLE ACCESS BY INDEX ROWID| ROW_MIG2     |  5882 |  &lt;b&gt;5896&lt;/b&gt; |
|* 4 |     &lt;b&gt;INDEX RANGE SCAN          | ROW_MIG2_IDX |  5882&lt;/b&gt; |    12 |
|* 5 |    INDEX UNIQUE SCAN          | ROW_MIG1_PK  |     1 |     0 |
|* 6 |   TABLE ACCESS BY INDEX ROWID | ROW_MIG1     |     1 |     1 |
---------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("D2"."FILTER"=0)
   5 - access("D1"."X"="D2"."X")
   6 - filter("D1"."FILTER"=0)

Statistics
----------------------------------------------------------
        913  recursive calls
          0  db block gets
      &lt;b&gt;20695&lt;/b&gt;  consistent gets
      12817  physical reads
          0  redo size
      39012  bytes sent via SQL*Net to client
        837  bytes received via SQL*Net from client
         40  SQL*Net roundtrips to/from client
         12  sorts (memory)
          0  sorts (disk)
        577  rows processed&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The more representative clustering factor makes the optimizer take the expected plan. The filtering takes place on the more selective table first, which matches about 5900 rows. The other table is joined in later. The execution is about 13% faster, the logical and physical gets dropped by about 40% both. &lt;em&gt;That&lt;/em&gt; makes quite a difference.&lt;/p&gt;
&lt;p&gt;The cost of the &lt;code&gt;TABLE ACCESS BY INDEX ROWID&lt;/code&gt; that follows the &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; reflects the clustering factor’s impact. The second query plan has a cost of about 5900, that actually means that each fetched row will need a block read. The original execution plan had a cost value of 938 for that step, so that the overall cost value was lower.&lt;/p&gt;
&lt;p&gt;After that I must remind the reader that the rows in table &lt;code&gt;ROW_MIG1&lt;/code&gt; are still migrated. The performance difference is not cause by the row migration per se, but by the misleading statistics that result out of the row migration.&lt;/p&gt;
&lt;h3&gt;Correcting the Row Migration&lt;/h3&gt;
&lt;p&gt;To complete the exercise, I will correct the row migration, run the statement again, and compare the performance improvement:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;ALTER TABLE row_mig1 MOVE;&lt;/b&gt;

Table altered.

SQL&amp;gt; &lt;b&gt;ALTER INDEX row_mig1_pk REBUILD;&lt;/b&gt;

Index altered.

SQL&amp;gt; &lt;b&gt;ALTER INDEX row_mig1_idx REBUILD;&lt;/b&gt;

Index altered.

SQL&amp;gt; &lt;b&gt;BEGIN
       DBMS_STATS.GATHER_TABLE_STATS(null, &amp;#39;ROW_MIG1&amp;#39;,CASCADE=&amp;gt;TRUE);
     END;
     /&lt;/b&gt;

PL/SQL procedure successfully completed.

SQL&amp;gt; &lt;b&gt;SELECT i.index_name         index_name
          , t.blocks             table_blocks
          , i.clustering_factor  index_clustering_factor
          , i.num_rows           index_rows
       FROM user_indexes i
       JOIN user_tables t USING (table_name)
      WHERE table_name = 'ROW_MIG1';&lt;/b&gt;

INDEX_NAME     TABLE_BLOCKS INDEX_CLUSTERING_FACTOR INDEX_ROWS
-------------- ------------ ----------------------- ----------
ROW_MIG1_PK          100506                  100000     100000
ROW_MIG1_IDX         100506                  100000     100000

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then execute the statement again:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;SET AUTOTRACE TRACEONLY;
SET TIMING ON;

SELECT /* no row migration */ *
  FROM row_mig1 d1
  JOIN row_mig2 d2 ON (d1.x = d2.x)
 WHERE d1.filter = 0
   AND d2.filter = 0;

SET TIMING OFF
SET AUTOTRACE OFF&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because the statistics are almost identical, the plan doesn’t change, nor does the cost. What &lt;em&gt;does&lt;/em&gt; changed is the execution time as well as the number of logical gets:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;577 rows selected.

Elapsed: &lt;b&gt;00:00:30.90&lt;/b&gt;

Execution Plan
----------------------------------------------------------
Plan hash value: 3004301745

---------------------------------------------------------------------
| Id | Operation                     | Name         | Rows  | Cost  |
---------------------------------------------------------------------
|  0 | SELECT STATEMENT              |              |  5882 | 11780 |
|  1 |  NESTED LOOPS                 |              |       |       |
|  2 |   NESTED LOOPS                |              |  5882 | 11780 |
|  3 |    TABLE ACCESS BY INDEX ROWID| ROW_MIG2     |  5882 |  &lt;b&gt;5896&lt;/b&gt; |
|* 4 |     &lt;b&gt;INDEX RANGE SCAN          | ROW_MIG2_IDX |  5882&lt;/b&gt; |    12 |
|* 5 |    INDEX UNIQUE SCAN          | ROW_MIG1_PK  |     1 |     0 |
|* 6 |   TABLE ACCESS BY INDEX ROWID | ROW_MIG1     |     1 |     1 |
---------------------------------------------------------------------            

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("D2"."FILTER"=0)
   5 - access("D1"."X"="D2"."X")
   6 - filter("D1"."FILTER"=0)

Statistics
----------------------------------------------------------
        925  recursive calls
          0  db block gets
      &lt;b&gt;14271&lt;/b&gt;  consistent gets
      11952  physical reads
          0  redo size
      39012  bytes sent via SQL*Net to client
        837  bytes received via SQL*Net from client
         40  SQL*Net roundtrips to/from client
         13  sorts (memory)
          0  sorts (disk)
        577  rows processed&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;The article investigates the effects of row migration on the clustering factor and the optimizer. A “proof of concept” SQL demonstrates that row migration can affect the optimizer. The lessons from this article are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The “insert empty, update everything” pattern can lead to a very high row migration rate.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DBMS_STATS&lt;/code&gt; doesn’t populate the &lt;code&gt;CHAIN_CNT.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A high row migration rate can cut the clustering factor, even below its theoretic minimum.&lt;/li&gt;
&lt;li&gt;A wrong clustering factor can affect the optimizer and result in an suboptimal plan.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Disclaimer&lt;/h3&gt;
&lt;p&gt;Please note that this trap was intentionally built, just to proof that row migration can potentially influence the optimizer. There are better ways to tune that particular SQL, foremost a better indexing approach.&lt;/p&gt;
&lt;p&gt;I have put many adjectives in quotation marks because they are not in line with the technical definition of the respective noun.&lt;/p&gt;
&lt;p&gt;The “insert empty, update everything“ anti-pattern was used to create a very high row migration rate. Although that anti-pattern can lead to 100% migration rate, it does not always.&lt;/p&gt;
&lt;p&gt;I have successfully verified my results on Oracle 10gR1, 11gR1 and 11gR2.&lt;/p&gt;
&lt;h3&gt;Thanks&lt;/h3&gt;
&lt;p&gt;Thanks to the guys at &lt;a href="http://www.25th-floor.com"&gt;25th-floor&lt;/a&gt; who verified my results on Oracle 11gR1.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/285/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/285/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/285/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/285/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/285/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/285/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/285/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/285/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/285/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/285/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=285&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/uGxJwbRv5jY" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2010/03/09/clustering-factor-row-migrations-victim/</feedburner:origLink></item><item><title>Row Migration and Row Movement</title><link>http://feedproxy.google.com/~r/orana_dba/~3/4hGbBEMdMf8/row-migration-and-row-movement</link><category>Performance</category><category>anit-pattern</category><category>oracle</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Tue, 23 Feb 2010 02:52:34 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/4f261c2973a3617b</guid><description>&lt;div&gt;&lt;p&gt;The Oracle database knows three distinct processes that are easily mixed up: Row Chaining, Row Migration and Row Movement.&lt;/p&gt;
&lt;p&gt;Luckily all three are well described in excellent articles: &lt;a href="http://www.akadia.com/services/ora_chained_rows.html"&gt;The Secrets of Oracle Row Chaining and Migration&lt;/a&gt; and &lt;a href="http://www.databasejournal.com/features/oracle/article.php/3676401/Row-Movement-in-Oracle.htm"&gt;Row Movement in Oracle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For the impatient, I provide some very short definitions:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;Row Chaining&lt;/dt&gt;
&lt;dd&gt;Distribution of a single table row across multiple data blocks.&lt;/dd&gt;
&lt;dt&gt;Row Migration&lt;/dt&gt;
&lt;dd&gt;Relocation of an entire table row to a new place, without updating the indexes.&lt;/dd&gt;
&lt;dt&gt;Row Movement&lt;/dt&gt;
&lt;dd&gt;Relocation of an entire table row to a new place and updating the indexes.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;This article was inspired by the question if Oracle 11r2 performs &lt;i&gt;Row Movement&lt;/i&gt; instead of &lt;i&gt;Row Migration&lt;/i&gt; for ordinary &lt;code&gt;UPDATE&lt;/code&gt; statements—that is, in absence of partitions. The short answer is: no, it doesn’t. The long answer is the rest of this article.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;
&lt;p&gt;The difference between &lt;i&gt;Row Chaining&lt;/i&gt; and &lt;i&gt;Row Migration&lt;/i&gt; is somehow understandable: If the row doesn’t fit into a single data block, it must be chained; otherwise it can be migrated as a whole. The limitation of the Row Migration is that it does not update the indexes on the table. That means, the &lt;code&gt;ROWID&lt;/code&gt; that is stored in the index still refers to the old location of the row. An additional block, the new location of the row, must be read to fetch the required data.&lt;/p&gt;
&lt;p&gt;The more modern &lt;i&gt;Row Movement&lt;/i&gt; is different as it updates the corresponding indexes—the &lt;code&gt;ROWID&lt;/code&gt; actually changes. This has benefits on the long run, because the additional block read can be avoided in the &lt;i&gt;TABLE ACCESS BY INDEX ROWID&lt;/i&gt; operation. On the short run, the actual &lt;code&gt;UPDATE&lt;/code&gt; operation is much more complex. I suppose this is the reason why a regular update does not (yet) trigger a Row Movement.&lt;/p&gt;
&lt;p&gt;The Row Movement was introduced to support an &lt;code&gt;UPDATE&lt;/code&gt; on a partition key—so that a table row is moved from one table partition to another one. In the meanwhile it is also used for flashback and space management—as described in the &lt;a href="http://www.databasejournal.com/features/oracle/article.php/3676401/Row-Movement-in-Oracle.htm"&gt;above-mentioned article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As of Oracle Release 11g2, Row Movement is optional and disabled per default. It can be enabled per table with a very trivial &lt;code&gt;ALTER TABLE&lt;/code&gt; statement. The usual reason to enable it is that one of the features which require Row Movement is used; partition key update, table shrink and flashback. There is hardly any reason not to enable row movement—the only side effect is that &lt;code&gt;ROWID&lt;/code&gt;’s might change; that should not affect well designed applications.&lt;/p&gt;
&lt;p&gt;Row Migration has some considerable problems, as pointed out in the later in this article. On the other hand, Row Movement has also a drawback; that is, the update of all the affected indexes can be very expansive. It’s a trade off between read and write performance. While Row Movement is more expansive for &lt;code&gt;UPDATES&lt;/code&gt;, it maintains best index performance. In contrast, the Row Migration favors &lt;code&gt;UPDATE&lt;/code&gt; performance over index speed. Although not true for all cases, I believe that most data is read more often than it is written—especially in our modern society where nobody every deletes data—so that Row Movement is generally the better choice.&lt;/p&gt;
&lt;p&gt;The article examines how to get 100% migrated rows with the “insert empty, update everything” anti-pattern, why &lt;code&gt;DBMS_STATS&lt;/code&gt; isn’t a perfect substitute for &lt;code&gt;ANALYZE TABLE&lt;/code&gt;, the immunity of migrated rows to &lt;code&gt;alter table ... shrink space&lt;/code&gt; and why &lt;code&gt;PCTFREE&lt;/code&gt; is still the only rescue from DBAs perspective.&lt;/p&gt;
&lt;p&gt;Just to show what I mean, I repeat a modified version of the test originally created by Tom Kyte and reused by Martin Zahn:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;CREATE TABLE row_migration_demo (
  a CHAR(2000),
  b CHAR(2000),
  c CHAR(2000),
  d CHAR(2000),
  e CHAR(2000),
  x int,
  constraint row_migration_pk primary key (x)
) enable row movement
/&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have changed the size of the CHAR field to 2k because my block size is 8k whereas it was 4k in the original example. I have also put the x column to the end for a better verification of Row Migration versus Row Chaining. Finally I enable Row Movement, which does actually not change anything but allows me to perform an &lt;code&gt;alter table ... shrink space&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The next step is a classical insert empty, update everything sequence:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;INSERT INTO row_migration_demo (x) values (1);
UPDATE row_migration_demo set a = 'a', b='b', c='c' where x=1;
COMMIT; &lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Up till now everything is perfectly fine and the entire row is in a single block:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;select * from row_migration_demo where x=1;&lt;/b&gt;

         X
----------
         1

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            0&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, lets do it again and insert a second row:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;INSERT INTO row_migration_demo (x) values (2);
UPDATE row_migration_demo set a = 'a', b='b', c='c' where x=2;
COMMIT;&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;And select it again:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;select * from row_migration_demo where x=2;&lt;/b&gt;

         X
----------
         1

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            1&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hooray, the row is migrated:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;ANALYZE TABLE row_migration_demo COMPUTE STATISTICS;&lt;/b&gt;

PL/SQL procedure successfully completed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
         2          1&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; &lt;code&gt;ANALYZE&lt;/code&gt; is deprecated. Yup, I know, but &lt;code&gt;DMBS_STATS.GATHER_TABLE_STATS&lt;/code&gt; does not propagate the &lt;code&gt;CHAIN_CNT&lt;/code&gt; column. &lt;a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1830023856761"&gt;Ask Tom!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It’s hard to figure out if the row is chained or migrated—I am actually not sure is there any difference in the data structure. Because the additional &lt;code&gt;table fetch continued row&lt;/code&gt; occurs for the very first column, I believe the row is migrated:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt;  select length(a) from row_migration_demo where x=2;

 LENGTH(A)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            2&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;To be on the safe side, I will double verify:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;INSERT INTO row_migration_demo (x, a) values (3, 'a');
UPDATE row_migration_demo
   SET b = 'b', c = 'c', d = 'd', e = 'e'
 WHERE x=3; &lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Indeed, the &lt;code&gt;a&lt;/code&gt; column is at the place where the row was inserted while the &lt;code&gt;e&lt;/code&gt; column is somewhere else:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt;  select length(&lt;b&gt;a&lt;/b&gt;) from row_migration_demo where x=3;

 LENGTH(A)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            2

SQL&amp;gt;  select length(&lt;b&gt;e&lt;/b&gt;) from row_migration_demo where x=3;

 LENGTH(A)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            3&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;To make things even worse, single rows can be migrated &lt;em&gt;and&lt;/em&gt; chained. If we insert two more records, it becomes visible:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;INSERT INTO row_migration_demo (x) values (4);
UPDATE row_migration_demo
   SET a='a', b='b', c='c', d='d', e='e'
 WHERE x=4;

INSERT INTO row_migration_demo (x) values (5);
UPDATE row_migration_demo
   SET a='a', b='b', c='c', d='d', e='e'
 WHERE x=5;

COMMIT;&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Each of those rows must be chained, because they need more space than a single block has. However, due to migration, the row is actually distributed to three blocks:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt;  select length(&lt;b&gt;a&lt;/b&gt;) from row_migration_demo where x=5;

 LENGTH(A)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            &lt;b&gt;4&lt;/b&gt;

SQL&amp;gt;  select length(&lt;b&gt;b&lt;/b&gt;) from row_migration_demo where x=5;

 LENGTH(B)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            &lt;b&gt;5&lt;/b&gt;

SQL&amp;gt;  select length(&lt;b&gt;c&lt;/b&gt;) from row_migration_demo where x=5;

 LENGTH(C)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            &lt;b&gt;7&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Please note that the “continued fetch” increased by two for the select on column &lt;code&gt;c&lt;/code&gt; . The statistics show that all but one row is chained (the very first row):&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;ANALYZE TABLE row_migration_demo COMPUTE STATISTICS;&lt;/b&gt;

PL/SQL procedure successfully completed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
         5          4&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;If we continue the game and continue to insert data in this way, we get an astonishing chain count:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;begin
   for i in 10..1000 loop
      INSERT INTO row_migration_demo (x) values (i);
      UPDATE row_migration_demo set a =&amp;#39;a&amp;#39;, b = &amp;#39;b&amp;#39;, c = &amp;#39;c&amp;#39; where x=i;
   end loop;
end;
/

SQL&amp;gt; &lt;b&gt;ANALYZE TABLE row_migration_demo COMPUTE STATISTICS;&lt;/b&gt;

PL/SQL procedure successfully completed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
       996        995&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;All the inserted rows are chained.&lt;/p&gt;
&lt;p&gt;The only way to clean up the mess is to move the table. Shrinking doesn’t help to much, especially not if there was no &lt;code&gt;DELETE&lt;/code&gt; or &lt;code&gt;UPDATE&lt;/code&gt; that has freed some space:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;alter table row_migration_demo shrink space;&lt;/b&gt;

Table altered.

SQL&amp;gt; &lt;b&gt;analyze table row_migration_demo compute statistics;&lt;/b&gt;

Table analyzed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
       996        995
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Even if some space is freed and rows are moved, the chaining is not notably reduced:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;delete from row_migration_demo where x &amp;lt; 200;&lt;/b&gt;

195 rows deleted.

SQL&amp;gt; &lt;b&gt;analyze table row_migration_demo compute statistics;&lt;/b&gt;

Table analyzed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
       801        801

SQL&amp;gt; &lt;b&gt;alter table row_migration_demo shrink space;&lt;/b&gt;

Table altered.

SQL&amp;gt; &lt;b&gt;analyze table row_migration_demo compute statistics;&lt;/b&gt;

Table analyzed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
       801        800&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Please note that the shrink has corrected one chained row. That proofs that a Row Movement assigns a new &lt;code&gt;ROWID&lt;/code&gt; and updates the index. However, one “unchained” row isn’t really the correction I would like to see. The only way to correct all chains, is to mote the table:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;alter table row_migration_demo move;&lt;/b&gt;

Table altered.

SQL&amp;gt; &lt;b&gt;alter index row_migration_pk rebuild;&lt;/b&gt;

Index altered.

SQL&amp;gt; &lt;b&gt;analyze table row_migration_demo compute statistics;&lt;/b&gt;

Table analyzed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
       801          0&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;No chaining anymore.&lt;/p&gt;
&lt;p&gt;There is actually another way to correct row migration. The &lt;a href="http://download.oracle.com/docs/cd/B12037_01/server.101/b10739/general.htm#sthref2787"&gt;Oracle documentation&lt;/a&gt; suggest to delete and re-insert all affected rows. Watch out for your triggers.&lt;/p&gt;
&lt;p&gt;From administrators‛ perspective, the only way to &lt;em&gt;prevent&lt;/em&gt; row migration in the first place is to increase &lt;code&gt;PCTFREE&lt;/code&gt;. The complete re-execution of this test with &lt;code&gt;PCTFREE&lt;/code&gt; increased to 50% “solves” the problem and no chained rows occur anymore:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;drop table row_migration_demo;
CREATE TABLE row_migration_demo (
  a CHAR(2000),
  b CHAR(2000),
  c CHAR(2000),
  d CHAR(2000),
  e CHAR(2000),
  x int,
  constraint row_migration_pk primary key (x)
) enable row movement pctfree 50
/
begin
   for i in 1..1000 loop
      INSERT INTO row_migration_demo (x) values (i);
      UPDATE row_migration_demo set a ='a', b = 'b', c = 'c' where x=i;
   end loop;
end;
/
commit;
analyze table row_migration_demo compute statistics;
select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
      1000          0&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;If this is not acceptable, there is one very last option: change the application.&lt;/p&gt;
&lt;p&gt;However, it would be very nice if an operation—similar to shrink—would move migrated rows. It would be even more compelling if a table option would allow to disable Row Migration in favor of Row Movement.&lt;/p&gt;
&lt;p&gt;UPDATE 2010-03-09: A &lt;a href="http://blog.fatalmind.com/2010/03/09/clustering-factor-row-migrations-victim/"&gt;follow up article describes the impact of Row Migration o the Clustering Factor&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Appendix&lt;/h3&gt;
&lt;p&gt;After some years of professional experience, I always wonder why a particular idea of mine should be unique. So I suspected that this feature might be there, somewhere hidden in Oracle. So I checked for hidden parameters (thanks to a &lt;a href="http://www.adp-gmbh.ch/ora/misc/x.html#ksppi"&gt;little documentation on that topic&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;select a.ksppinm parameter, b.kspftctxvl
       from x$ksppi a join x$ksppcv2 b on (a.INDX+1= b.kspftctxpn)
      where a.KSPPDESC like '%movement%';&lt;/b&gt;

PARAMETER                                KSPFTCTXVL
---------------------------------------- ----------
_disable_implicit_row_movement           FALSE

SQL&amp;gt; &lt;b&gt;alter session set "_disable_implicit_row_movement" = true;&lt;/b&gt;

Session altered.

SQL&amp;gt; &lt;b&gt;select a.ksppinm parameter, b.kspftctxvl
       from x$ksppi a join x$ksppcv2 b on (a.INDX+1= b.kspftctxpn)
      where a.KSPPDESC like '%movement%';&lt;/b&gt;

PARAMETER                                KSPFTCTXVL
---------------------------------------- ----------
_disable_implicit_row_movement           TRUE

SQL&amp;gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;As suggested by the name of the parameter, this doesn’t change anything in the direction I would like.&lt;/p&gt;
&lt;/p&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/4hGbBEMdMf8" height="1" width="1"/&gt;</description><feedburner:origLink>http://blog.fatalmind.com/2010/02/23/row-migration-and-row-movement</feedburner:origLink></item><item><title>Oracle Trace File Rotation</title><link>http://feedproxy.google.com/~r/orana_dba/~3/DcMj1668WhI/</link><category>Maintainability</category><category>oracle</category><category>trace</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Mon, 01 Feb 2010 09:30:11 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/5eb3bd5a538171aa</guid><description>&lt;p&gt;Under very rare circumstances, I need Oracle SQL trace files from a long period of time. Because trace files usually grow large—especially over several days—there is the need to rotate the trace file during that time so that they can be compressed and put away. The problem is that there is no “rotate tracefile“ button in Oracle. However, I have found a “undocumented feature“ that does exactly that—without disabling tracing.&lt;/p&gt;
&lt;p&gt;My procedure uses the &lt;code&gt;close_trace&lt;/code&gt; call of &lt;code&gt;oradebug&lt;/code&gt;. This call closes the currently written trace file for a session. &lt;a href="http://www.oracloid.com/2006/05/closing-trace-file-with-oradebug/"&gt;Alex Gorbachev&lt;/a&gt; has used this to delete big trace files that are still open. My procedure goes a little bit further and exposes one more side effect of &lt;code&gt;oradebug close_trace&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;
&lt;p&gt;&lt;b&gt;Warning: The technique described here uses the undocumented and unsupported &lt;code&gt;&lt;a href="http://www.psoug.org/reference/oradebug.html"&gt;oradebug&lt;/a&gt;&lt;/code&gt; facility. Although I am not aware of any negative side effects of this procedure, the use of this method takes place at your own risk.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;oradebug&lt;/code&gt; utility can be used from the SQL*Plus prompt. If you have an account with sufficient rights (sigh) you can attach to any session using the &lt;code&gt;oradebug setorapid&lt;/code&gt; command. To obtain the required Oracle PID, just query the &lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14237/dynviews_2022.htm"&gt;&lt;code&gt;v$process&lt;/code&gt;&lt;/a&gt; view:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT s.sid, s.serial#, p.pid &lt;/b&gt;
       &lt;b&gt;FROM v$session s, v$process p&lt;/b&gt;
      &lt;b&gt;WHERE s.paddr=p.addr;&lt;/b&gt;

       SID    SERIAL#        PID
---------- ---------- ----------
         1          1          2
[... skipped ...]
        20       3126         21

26 rows selected.

SQL&amp;gt; &lt;b&gt;oradebug setorapid 21&lt;/b&gt;
Oracle pid: 21, Unix process pid: 145, image: oracle@test.fatalmind.com
SQL&amp;gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once attached to a specific process, every &lt;code&gt;oradebug&lt;/code&gt; operation is applied to that particular process. The trick to rotate the trace file is very simple and exploits a “feature” of UNIX file systems; that is, that the name of the file is used only at the moment the file is opened. Once the file is open, all access is managed with the so called &lt;code&gt;inode&lt;/code&gt; and the name is not relevant for the read and write operations anymore. The &lt;code&gt;inode&lt;/code&gt; system has some more &lt;a href="http://en.wikipedia.org/wiki/Inode#Implications"&gt;implications&lt;/a&gt;, one of them is frequently used to rotate log files without data loss. Another implications makes it hard to delete open files, as &lt;a href="http://www.oracloid.com/2006/05/closing-trace-file-with-oradebug/"&gt;Alex Gorbachev&lt;/a&gt; explained.&lt;/p&gt;
&lt;p&gt;The log rotation trick is to rename the open file and then cause the writing process to re-open that file. All write operations to the renamed file will be written properly to new file name—more correctly expressed: to the file which can be accessed under the new name. If the writing process re-opens the file under its original name, it will create a new file because there is no file with that name anymore. Every subsequent write operation goes to the new file. The original file will not grow anymore and can be taken away safely.&lt;/p&gt;
&lt;p&gt;Although the &lt;code&gt;oradebug&lt;/code&gt; command name &lt;code&gt;close_trace&lt;/code&gt; doesn’t suggest that the trace file is reopened, it is—if SQL tracing is enabled. So we have everything to rotate a trace file. Consider the following example:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;$ &lt;b&gt;ls -lrt TEST_ora_14457.trc*&lt;/b&gt;
-rw-r----- 1 oracle oinstall 14758723 2010-02-01 TEST_ora_14457.trc
$ &lt;b&gt;mv  TEST_ora_14457.trc  TEST_ora_14457.trc.old&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;After renaming the file, the file still grows as the SQL trace is written. You can now use &lt;code&gt;oradebug close_trace&lt;/code&gt; to actually rotate the file:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;oradebug close_trace&lt;/b&gt;
Statement processed.
SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The verification in the file system shows that the ‘old’ file became bigger, and the new file is also filling up:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;$ &lt;b&gt;ls -lrt TEST_ora_14457.trc*&lt;/b&gt;
-rw-r----- 1 oracle oinstall 18878704 2010-02-01 TEST_ora_14457.trc.old
-rw-r----- 1 oracle oinstall  1555972 2010-02-01 TEST_ora_14457.trc
$ &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because I often need to trace many sessions, I wrote a very tiny script (&lt;code&gt;switch_traces.sql&lt;/code&gt;) that creates another script to re-open all trace files:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;set feedback off
set pages 0
set heading off
set echo off

spool oradebug_close_traces.sql

select cmd from (
select pid, 1 o, &amp;#39;oradebug setorapid &amp;#39; || pid cmd from v$process
UNION ALL
select pid, 2 o, &amp;#39;oradebug flush&amp;#39; from v$process
UNION ALL
select pid, 3 o, &amp;#39;oradebug close_trace&amp;#39; from v$process) order by pid,o;

spool off
set echo on
set feedback on
set pages 1000
set heading 0n

prompt
prompt c&amp;amp;p &amp;gt;&amp;gt;&amp;gt; @oradebug_close_traces.sql &amp;lt;&amp;lt;&amp;lt; to switch traces&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;This script will close all traces in all sessions and continue tracing to new files if SQL tracing is enabled. You might want to add additional &lt;code&gt;where&lt;/code&gt; clauses to limit your scope—e.g., to skip background processes. The script will also issue a &lt;code&gt;flush&lt;/code&gt; before the &lt;code&gt;close_trace&lt;/code&gt;. I don’t know if this is required or not, it doesn’t seem to make any harm. The use of the script is very simple:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;@switch_traces.sql&lt;/b&gt;
SQL&amp;gt; set feedback off
SQL&amp;gt; set pages 0
SQL&amp;gt; set heading off
SQL&amp;gt; set echo off
oradebug setorapid 1
oradebug flush
oradebug close_trace

[... skipped ...]

oradebug setorapid 29
oradebug flush
oradebug close_trace
SQL&amp;gt; set feedback on
SQL&amp;gt; set pages 1000
SQL&amp;gt; set heading on
SQL&amp;gt;
SQL&amp;gt; prompt

SQL&amp;gt; prompt c&amp;amp;p &amp;gt;&amp;gt;&amp;gt; @oradebug_close_traces.sql &amp;lt;&amp;lt;&amp;lt; to switch traces
c&amp;amp;p &amp;gt;&amp;gt;&amp;gt; @oradebug_close_traces.sql &amp;lt;&amp;lt;&amp;lt; to switch traces
SQL&amp;gt; &lt;b&gt;@oradebug_close_traces.sql&lt;/b&gt;
SQL&amp;gt; oradebug setorapid 1
ORA-00072: process &amp;quot;Unix process pid: 0, image: PSEUDO&amp;quot; is not active
SQL&amp;gt; oradebug flush
Statement processed.
SQL&amp;gt; oradebug close_trace
Statement processed.
SQL&amp;gt; oradebug setorapid 2
Oracle pid: 2, Unix process pid: 137, image: oracle@test.fatalmind.com
SQL&amp;gt; oradebug flush
Statement processed.
SQL&amp;gt; oradebug close_trace
Statement processed.

[... skipped ...]

Oracle pid: 29, Unix process pid: 138, image: oracle@test.fatalmind.com
SQL&amp;gt; oradebug flush
Statement processed.
SQL&amp;gt; oradebug close_trace
Statement processed.
SQL&amp;gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;In case you have not forgotten to rename your trace files you will see the new files growing now.&lt;/p&gt;
&lt;p&gt;Read also my previous article &lt;a href="http://myfatalmind.wordpress.com/2009/11/24/to-trace-or-not-to-trace/"&gt;To Trace or Not to Trace&lt;/a&gt; for information about fine grained SQL tracing.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/174/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/174/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/174/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/174/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/174/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/174/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/174/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/174/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/174/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/174/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=174&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/DcMj1668WhI" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2010/02/01/oracle-trace-file-rotation/</feedburner:origLink></item><item><title>Oracle JDBC PreFetch Portability</title><link>http://feedproxy.google.com/~r/orana_dba/~3/HQXRAFmFiEs/</link><category>Portability</category><category>java</category><category>JDBC</category><category>oracle</category><category>sql</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Fri, 29 Jan 2010 06:23:11 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/bbf95072067e8ac6</guid><description>&lt;p&gt;In a &lt;a href="http://myfatalmind.wordpress.com/2009/12/22/latency-security-vs-performance/"&gt;previous article about (network) latencies&lt;/a&gt;, I have presented the &lt;code&gt;OracleStatement.setRowPrefetch()&lt;/code&gt; method to reduce round trips for &lt;code&gt;SELECT&lt;/code&gt; statements. I must admit that I was a little bit wrong. Not in the essence; increasing the PreFetch size is still a great (read: simple) way to reduce latencies. However, there is a better approach to set the PreFetch size. &lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;
&lt;p&gt;Since the raise of Java, many portability issues have vanished. There are most definitely new ones—think of mobile devices—and some others are still eminent—think of Internet Explorer versus all other browsers. Nevertheless, Java improved the situation quite notable—most credible: Unicode support from the beginning. &lt;/p&gt;
&lt;p&gt;Still there are some people like me who just cast the portable JDBC class to whatever they believe it is, and do some cool things with that. Every now and then there &lt;i&gt;is&lt;/i&gt; the need to use proprietary functions, but I should have checked the portable API first. Shame on me. In fact there is the standard JDBC method &lt;code&gt;&lt;a href="http://java.sun.com/javase/6/docs/api/java/sql/Statement.html#setFetchSize%28int%29"&gt;Statement.setFetchSize()&lt;/a&gt;&lt;/code&gt; that does exactly the same. Even more interesting, it’s there since ages (&lt;a href="http://java.sun.com/products/jdbc/download.html#102spec"&gt;JDBC 2.1 at least&lt;/a&gt;). &lt;/p&gt;
&lt;p&gt;Although the documentation seems to be exactly what I was looking for, I wrote a little test program to verify. Just because I know that Oracle can profit from an increased PreFetch size and the standard provides a method to set it, it doesn’t mean that it works. Especially not if the standards says that the method is a &lt;em&gt;hint&lt;/em&gt; to the JDBC driver. So let’s double check.&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;import java.sql.*;
import oracle.jdbc.*;

class JDBCFetchSize {
  public static void main(String[] argv) throws Exception {
    if (argv.length != 4) {
      throw new RuntimeException(&amp;quot;JDBCFetchSize &amp;lt;jdbc-connect url&amp;gt; &amp;quot;
                               + &amp;quot;&amp;lt;username&amp;gt; &amp;lt;password&amp;gt; &amp;lt;FetchSize&amp;gt;&amp;quot;);
    }
    Connection con = connect(argv[0], argv[1], argv[2]);
    int roundtripoffset = getRoundTripOffset(con);

    PreparedStatement ps = con.prepareStatement(seqsql);

    setFetchSize(ps, argv[3]);

    int start = getCurrentRoundTripCount(con);
    long startts = System.currentTimeMillis();

    int rowsfetched = executeTest(ps);

    int end    = getCurrentRoundTripCount(con) - roundtripoffset;
    long endts = System.currentTimeMillis();

    ps.close();

    System.out.println(rowsfetched + &amp;quot; rows fetched in &amp;quot;
                       + (end-start) + &amp;quot; server round trips and &amp;quot;
                       + (endts-startts) + &amp;quot; ms&amp;quot;);
  }

  // oracle SQL to fetch round trip counter
  final static String rtsql =
      &amp;quot; select value &amp;quot;
    + &amp;quot;   from v$mystat ms, v$statname sn&amp;quot;
    + &amp;quot;  where ms.value &amp;gt; 0&amp;quot;
    + &amp;quot;    and ms.statistic#=sn.statistic#&amp;quot;
    + &amp;quot;    and sn.name IN (&amp;#39;SQL*Net roundtrips to/from client&amp;#39;)&amp;quot;;

  // sequence generator
  final static String seqsql =
      &amp;quot; select level from dual connect by level &amp;lt;= ?&amp;quot;;

  static PreparedStatement rtstm;

  private static Connection
    connect (String url, String user, String pass)
    throws ClassNotFoundException, SQLException
  {
    Class.forName(&amp;quot;oracle.jdbc.driver.OracleDriver&amp;quot;);
    return DriverManager.getConnection(url, user, pass);
  }

  private static int
    getRoundTripOffset(Connection con)
    throws SQLException
  {
    // The very first time we call it, it is not prepared.
    // so we don&amp;#39;t consider that at all.
    getCurrentRoundTripCount(con);

    int first = getCurrentRoundTripCount(con);
    return getCurrentRoundTripCount(con) - first;
  }

  static void
    setFetchSize(Statement stm, String size)
    throws SQLException
  {
    System.out.println(&amp;quot;getFetchSize(): &amp;quot; + stm.getFetchSize());

    stm.setFetchSize(Integer.parseInt(size));

    // uncomment the not portable OracleStatement
    // variant for verification
    // ((OracleStatement)stm).setRowPrefetch(Integer.parseInt(size));

    System.out.println(&amp;quot;getFetchSize(): &amp;quot; + stm.getFetchSize());
  }

  static int
    executeTest(PreparedStatement ps)
    throws SQLException
  {
    int cnt = 0;
    ps.setInt(1, 100000);
    ResultSet rs = ps.executeQuery();
    while (rs.next()) {
      ++cnt;
    }
    rs.close();
    return cnt;
  }

  static int
    getCurrentRoundTripCount(Connection con)
    throws SQLException
  {
    if (rtstm == null) {
      rtstm = con.prepareStatement(rtsql);
    }
    ResultSet rs = rtstm.executeQuery();
    rs.next();
    int rv = rs.getInt(1);
    rs.close();

    return rv;
  }
}&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The result proofs that the portable JDBC &lt;code&gt;setFetchSize()&lt;/code&gt; is the better choice over Oracle’s proprietary &lt;code&gt;setRowPrefetch()&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;$ java JDBCFetchSize jdbc:oracle:thin:@//host/service user password 10
getFetchSize(): 10
getFetchSize(): 10
100000 rows fetched in 10002 server round trips and 521 ms
$ java JDBCFetchSize jdbc:oracle:thin:@//host/service user password 20
getFetchSize(): 10
getFetchSize(): 20
100000 rows fetched in 5002 server round trips and 324 ms
$ java JDBCFetchSize jdbc:oracle:thin:@//host/service user password 50
getFetchSize(): 10
getFetchSize(): 50
100000 rows fetched in 2002 server round trips and 191 ms
$ &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Calling &lt;code&gt;setRowPrefetch()&lt;/code&gt; affects the number of server round trips as expected.&lt;/p&gt;
&lt;p&gt;And because consistency (=Quality) matters, I will right away update the previous article.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://download.oracle.com/docs/cd/E11882_01/java.112/e10589/oraperf.htm#CHDHDADD"&gt;Recent oracle docs&lt;/a&gt; list some limitations.&lt;/p&gt;
&lt;p&gt;After all, there is still some room for portability pitfalls. If you want to change the default value from 10 to some other value, Oracle has two solutions for that. Read again: &lt;em&gt;Oracle&lt;/em&gt; has two solutions for that. JDBC has none (correct me if I am wrong).&lt;/p&gt;
&lt;p&gt;When passing &lt;code&gt;Properties&lt;/code&gt; to the &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/DriverManager.html#getConnection%28java.lang.String,%20java.util.Properties%29"&gt;DriverManager.getConnection()&lt;/a&gt; method, Oracle allows to set the &lt;a href="http://download.oracle.com/docs/cd/B13789_01/java.101/b10979/basic.htm#g1028323"&gt;defaultRowPrefetch&lt;/a&gt; property. The other way is to cast the JDBC &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Connection.html"&gt;Connection&lt;/a&gt; to &lt;a href="http://www.oracle.com/technology/docs/tech/java/sqlj_jdbc/doc_library/javadoc/oracle.jdbc.driver.OracleConnection.html"&gt;OracleConnection&lt;/a&gt; and use the &lt;a href="http://www.oracle.com/technology/docs/tech/java/sqlj_jdbc/doc_library/javadoc/oracle.jdbc.driver.oracleconnection.html#setDefaultRowPrefetch%28int%29"&gt;setDefaultRowPrefetch()&lt;/a&gt; method (hurray — cast again!). Disclaimer: I did not test any of those.&lt;/p&gt;
&lt;p&gt;Please note that my tests were based on Oracle 11r2, other release might behave differently. During my research I found some Oracle 8.1.6 docs which also mention the portable &lt;code&gt;setFetchSize()&lt;/code&gt; method, that makes me believe it works since then.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/159/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/159/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/159/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/159/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/159/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/159/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/159/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/159/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/159/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/159/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=159&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/HQXRAFmFiEs" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2010/01/29/oracle-jdbc-prefetch-portability/</feedburner:origLink></item><item><title>Latency: Security vs. Performance</title><link>http://feedproxy.google.com/~r/orana_dba/~3/1fG5d-7Wt1Q/</link><category>Performance</category><category>java</category><category>oracle</category><category>security</category><category>sql</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Tue, 22 Dec 2009 00:01:53 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/79cffb5b104cdef6</guid><description>&lt;p&gt;At a client’s Christmas party, I have witnessed a very short talk between a network engineer and a top level manager. The network guy explained that the firewall adds about 0.2 milliseconds latency to each round trip between the application server and the database, which adds up to some hours in one particular case. So the manager asked what could be done and the network guy quickly provided two solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the application to make less round trips&lt;/li&gt;
&lt;li&gt;Accept the security risk and don’t put a firewall in between those two tiers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Funny enough the network guy explained that the second option needs the managers signature because it bypasses the corporate security guidelines and somebody must take the responsibility for that.&lt;/p&gt;
&lt;p&gt;Consider you are the manager, would you sign such a paper?&lt;/p&gt;
&lt;p&gt;I guess I would not, and I also guess my client didn’t (otherwise it was against my advice &lt;img src="http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif" alt=";)"&gt; ).&lt;/p&gt;
&lt;p&gt;In this article I will explain the term latency, give some insight why it is so easy to build latency bandit applications and show some ways to reduce latencies.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;br&gt;
&lt;h3&gt;Weekend Shopping&lt;/h3&gt;
&lt;p&gt;The best explanation for latency I ever came across is from the guys over at &lt;a href="http://www.roughsea.com" title="RoughSea Ltd."&gt;RoughSea&lt;/a&gt;. There is a video that describes shopping like that (simplified):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Drive to the super market&lt;/li&gt;
&lt;li&gt;Locate what’s needed (e.g., milk)&lt;/li&gt;
&lt;li&gt;Pay&lt;/li&gt;
&lt;li&gt;Store the item in the car&lt;/li&gt;
&lt;li&gt;Drive back home&lt;/li&gt;
&lt;li&gt;Store the item (e.g., fridge)&lt;/li&gt;
&lt;li&gt;Then start again for the next item on the shopping list (e.g., corn flakes).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;a href="http://www.roughsea.com/vids/SQL_Best_Practices.html" title="RoughSea 20 minutes Video"&gt;whole 20 minutes video&lt;/a&gt; is a great watch and covers many other frequent issues as well. If you just want to watch that particular scene, &lt;a href="http://www.youtube.com/watch?v=GbZgnAINjUw#t=5m00s" title="Weekend shopping @ YouTube"&gt;here is a direct youtube link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When watching this video it’s hard not to &lt;a href="http://en.wikipedia.org/wiki/D%27oh!" title="D&amp;#39;oh! @ Wikipedia"&gt;D’oh!&lt;/a&gt; The video makes it very obvious that most of the time is spent to drive. The actual shopping takes much less time than all the overhead for driving, parking, paying,….&lt;/p&gt;
&lt;p&gt;Latency in software has very similar causes, most prominently that the application asks the database for many items individually.&lt;/p&gt;
&lt;h3&gt;How Can This Happen?&lt;/h3&gt;
&lt;p&gt;This question has actually two sides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How can somebody implement software that way?&lt;/li&gt;
&lt;li&gt;How can a latency of 0.2 milliseconds make any difference?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First of all let’s look at the 0.2 milliseconds statement again. This value was provided by the network engineer as &lt;em&gt;additional&lt;/em&gt; latency because of the new firewall. That’s on top of the latency the network anyway, even without firewall. A typical value for a switched LAN, without firewall, is around 0.1 – 0.2 milliseconds. If a firewall adds 0.2ms to that, the overall latency raises to about 0.4ms per round trip. In other words 2500 round trips take a second. Although this sounds a lot, it’s actually not so much if you consider how many round trips take place for an average operation. A average latency of 0.4 milliseconds is still very reasonable and will cause problems only in extreme cases. Nevertheless latencies can grow because of increased network complexity or during peak load.&lt;/p&gt;
&lt;p&gt;The situation becomes more relevant because one round trip is hardly enough to perform any reasonable operation. Realistically, an application issues &lt;em&gt;many&lt;/em&gt; different SQLs to accomplish a single task. A single SQL statement might also need multiple round trips to complete; a &lt;code&gt;SELECT&lt;/code&gt; statement against an Oracle database needs typically two round trips, as explained farther down the article. An application that executes 10 &lt;code&gt;SELECT&lt;/code&gt;s has already 8ms of latencies. More complex applications might execute 100 SQL’s and reach 80 milliseconds of latencies. I have seen batch jobs that issue 50 million SQLs so that the round trip latency sums up to 10 hours or more!&lt;/p&gt;
&lt;p&gt;So, why! Why is it done that way? Because it is easier to implement. Therefore faster to implement and less error prone. Consider the shopping example again. The algorithm is very simple; just repeat all steps until the shopping list is done. This works irrespective of the size of the shopping list.&lt;/p&gt;
&lt;p&gt;An improved variant of the algorithm, that is latency aware, might look like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Drive to the super market&lt;/li&gt;
&lt;li&gt;&lt;i&gt;FOR EACH ITEM:&lt;/i&gt; Locate item (milk, corn flakes)&lt;/li&gt;
&lt;li&gt;Pay&lt;/li&gt;
&lt;li&gt;&lt;i&gt;FOR EACH ITEM:&lt;/i&gt; Store into the car&lt;/li&gt;
&lt;li&gt;Drive back home&lt;/li&gt;
&lt;li&gt;&lt;i&gt;FOR EACH ITEM:&lt;/i&gt; Store the item (e.g., fridge, larder)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Please note that what has been one loop over the complete algorithm before, was turned into three loops somewhere inside the algorithm. The &lt;a href="http://en.wikipedia.org/wiki/Cyclomatic_complexity"&gt;complexity&lt;/a&gt; has grown. Even worse, there are more complex error scenarios with the new algorithm. E.g., a very long shopping list might exceed the capacity of the car to transport all the items. Such scenarios need special error handling. Alternatively the algorithm could prevent that scenario by splitting up large purchases into multiple smaller parts. The additional complexity requires more time to implement and introduces more scenarios to test. The efforts quickly explode, especially since the &lt;em&gt;overflow&lt;/em&gt; scenarios are hard to test.&lt;/p&gt;
&lt;p&gt;One more word of warning: The case that your shopping doesn’t fit into your car might look very unlikely, and perhaps it is. However, in computer programs, there a less natural limits than in real life—e.g., what your family can eat. From personal experience I would say chances are 2:1 to hit this error scenario within the first year that implementation is used.&lt;/p&gt;
&lt;p&gt;After all, there is a trade off between software complexity and software performance. From another point of view, it is even a trade off between software development speed and software runtime speed. If you are pushed to deliver working software, better sooner than later, which algorithm would you implement?&lt;/p&gt;
&lt;p&gt;Finally, there is also a reason why latency bastard applications can be produced by accident:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;But it works in development/test environment&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Very often, development takes place on desktop hardware where everything is installed on the same machine. Obviously, there is no firewall in place nor is there any network involved. The involved latency in such an environment is only a fraction (e.g., a 10th) of the latency involved in an n-tier production deployment. In the shopping example, this would mean that the super market is in the room next to the kitchen. Item-by-item shopping might work fine in that case. Unfortunately this type of shopping infrastructure is not yet commonplace.&lt;/p&gt;
&lt;h3&gt;How to Avoid it&lt;/h3&gt;
&lt;p&gt;So let’s look into the more technical part of the story. Which techniques can be used to reduce the number of database round trips? The following list gives the three most effective ways to avoid unneeded latency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use Joins&lt;/li&gt;
&lt;li&gt;Use array/batch functionality&lt;/li&gt;
&lt;li&gt;Use advanced techniques (open cursors, fetch buffers, bulk/implicit commit, …)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Join&lt;/h3&gt;
&lt;p&gt;The most important technique to avoid latencies is to tell the database which data is needed &lt;em&gt;at once&lt;/em&gt;. A very problematic implementation is to query one table and issue another select within the fetch loop of the first statement. I refer to this method as &lt;em&gt;nested select&lt;/em&gt; as opposed to a &lt;em&gt;nested loop&lt;/em&gt; join. Both methods implement the same idea while the &lt;em&gt;nested select&lt;/em&gt; involves additional round trips. With SQL, it is always possible to construct a join that delivers the same result within the same time—but usually much faster.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;nested select&lt;/em&gt; anti-pattern can be applied to any extend. They can be nested several levels deep, or kept at one level that triggers more than just one nested select. I have mentioned the 50 million SQL batch job that took more than 10 hours, just for latency. This particular job has queried some 10 additional child tables for each record fetched from the main query. The main query has returned some million records so that the latency became the most dominant performance factor. In that particular case, the developers explained that the coding guidelines force them to use the existing functions instead of writing new SQL. Another difficulty was that the nested statements were depended on the data from the main table so that a trivial join doesn’t work (conditional join). In fact there were only three different ways to join the tables, therefore the correct solution is to make three different SQLs. In case a single result set is needed (e.g., because of a order clause), a &lt;code&gt;UNION&lt;/code&gt; can be used. After all, 10 out of 12 hours overall run time was saved just by using joins.&lt;/p&gt;
&lt;h3&gt;Batch Execution&lt;/h3&gt;
&lt;p&gt;The second major latency reduction technique is to properly use batch execution that is sometimes also called array execution. Many databases provide a way to execute more than one statement in one round trip. Not all APIs make this functionality available to the client, but &lt;a href="http://java.sun.com/j2se/1.3/docs/guide/jdbc/spec2/jdbc2.1.frame6.html" title="JDBC Batch API"&gt;JDBC does&lt;/a&gt;. However, there are some pitfalls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Error handling is terrible.&lt;/span&gt;&lt;br&gt;Although the API defines methods to identify which statement failed, the API is very cumbersome to use. To make things worse it’s not even defined if the execution of the batch stops on errors. My Best Current Practice is to set a &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Savepoint.html" title="JDBC Savepoint"&gt;Savepoint&lt;/a&gt; prior array execution. In case an error occurs, a rollback to the Savepoint can be done.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Very large batches.&lt;/span&gt;&lt;br&gt;Very large batches can have their own problems. As with the shopping example, every implementation must be prepared to use multiple batches in case the number of executions becomes very high.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Does not always work faster.&lt;/span&gt;&lt;br&gt;The actual performance gain is vendor specific and, more importantly, depends on the method used. For example, Oracle benefits from batching only if &lt;code&gt;&lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/PreparedStatement.html" title="JDBC PreparedStatement"&gt;PreparedStatements&lt;/a&gt;&lt;/code&gt; are used. Batching &lt;code&gt;Statement&lt;/code&gt; objects doesn’t give any performance gain with Oracle. On the other side, MySQL can also benefit from &lt;code&gt;Statement&lt;/code&gt; batches. As always, the advice is to &lt;strong&gt;use &lt;code&gt;PreparedStatements&lt;/code&gt;&lt;/strong&gt; &lt;strong&gt;whenever possible&lt;/strong&gt;. This will almost always result in major performance improvements.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;The JDBC API is &lt;em&gt;optional&lt;/em&gt;.&lt;/span&gt;&lt;br&gt;Prior using that API you must check if the driver supports it. Realistically I would suggest to build an abstraction that takes care of this and executes the statements individually if batch execution is not support. Ideally the JDBC driver would do so.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are important issues, that cause considerable efforts, to take care of when using batch execution. Still all the efforts can easily pay off. In many cases the execution of a &lt;code&gt;PreparedStatement&lt;/code&gt; batch with two elements takes no longer than the execution of a single statement, so that the execution is effectively twice as fast. For trivial SQL statements, a speed improvement of factor 10 is within easy reach. That justifies some efforts.&lt;/p&gt;
&lt;h3&gt;Advanced Techniques&lt;/h3&gt;
&lt;p&gt;Besides the two most powerful techniques explained above, there are many more ways to reduce latency. They can be classified in two different types: SQL improvements or API improvements.&lt;/p&gt;
&lt;p&gt;Let’s start with SQL improvements.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Join.&lt;/span&gt;&lt;br&gt;Again, just as a reminder. The most powerful way to reduce latencies.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;&lt;code&gt;IN&lt;/code&gt; clauses&lt;/span&gt;&lt;br&gt;Consider to use an &lt;code&gt;IN&lt;/code&gt; clause to fetch multiple records form the same table in one run. Unfortunately, the SQL language doesn’t guarantee that the order of the result sets corresponds to the &lt;code&gt;IN&lt;/code&gt; clause order. An additional layer (e.g., HashMap) might be used to handle that issue. Another potential problem is that the number of items in the &lt;code&gt;IN&lt;/code&gt; list has a maximum—1000 in Oracle: ORA-01795. However, if the list can grow that big, chances are good that the list itself was retrieved from the database. In that case, a join might be the better solution—just to mention it once more.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;&lt;code&gt;UNION&lt;/code&gt;&lt;/span&gt;&lt;br&gt;If data from two different tables is needed that have the same structure, you can use a &lt;code&gt;UNION&lt;/code&gt; to fetch both at once. For example a &lt;code&gt;CURRENT&lt;/code&gt; and a &lt;code&gt;HISTORY&lt;/code&gt; table might contain the life-cycle of a row. To obtain the current and all past versions, both tables must be queried. Another examples are pre-calculated aggregates; one table contains all the aggregates as of yesterday and a second table all the new records since then. To get the total figure—as of “now”—both tables must be queried.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;&lt;code&gt;RETURNING&lt;/code&gt; clause. &lt;/span&gt;&lt;br&gt;If you use a surrogate primary key that is generated from a sequence, you must usually perform two steps to fetch the surrogate key. The least troublesome solution is to first &lt;code&gt;SELECT&lt;/code&gt; the new ID and the perform the &lt;code&gt;INSERT&lt;/code&gt; with that ID. The Oracle answer to this problem is the &lt;code&gt;RETURNING&lt;/code&gt; clause of the &lt;code&gt;INSERT&lt;/code&gt; statement. Although the JDBC API &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Statement.html#getGeneratedKeys%28%29" title="getGeneratedKeys JDBC 3.0"&gt;acknowledges the problem since version 3.0&lt;/a&gt; the Oracle implementation is not very useful—it returns the &lt;code&gt;ROWID&lt;/code&gt;. A &lt;a href="http://forums.oracle.com/forums/thread.jspa?threadID=336591" title="INSERT...RETRUNING in JDBC"&gt;workaround&lt;/a&gt; is available.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;1×1 Cartesian product or &lt;code&gt;CROSS&lt;/code&gt; join&lt;/span&gt;&lt;br&gt;
&lt;p&gt;The so called &lt;a href="http://en.wikipedia.org/wiki/Cartesian_product" title="Cartesian prudction @ Wikipedia"&gt;Cartesian production&lt;/a&gt; (everything with everything) is sometimes used accidentally and results in huge result sets. That’s the reason why &lt;a href="http://blog.fatalmind.com/?%3F%3F"&gt;SQL-92&lt;/a&gt; introduced a distinct keyword for it: &lt;code&gt;CROSS&lt;/code&gt;. On the other side, the Cartesian product of two single row statements is, again, a single row: &lt;/p&gt;
&lt;p&gt;&lt;code&gt;SELECT a.*, b.* FROM a CROSS JOIN b WHERE a.id=1 AND b.id=2&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;This technique can be used to combine multiple single-row statements into a single serve round trip. Please note that this works only if both statements return exactly one row, not more, not less. This is more often a last resort option than a first choice.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the typical SQL based techniques to reduce the number of individual SQLs executed that in turn reduces the number of round trips. Another strategy to reduce the number of round trips is to use the API differently:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Batch execution.&lt;/span&gt;&lt;br&gt;Sorry for the repetition, but it’s the most important technique.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Open cursors&lt;/span&gt;&lt;br&gt;When executing the same &lt;code&gt;PreparedStatement&lt;/code&gt; multiple times in a row–with different bind values—the same&lt;code&gt; PreparedStatement&lt;/code&gt; &lt;em&gt;instance&lt;/em&gt; should be used for all executions. The first execution of a &lt;code&gt;Statement&lt;/code&gt; involves an additional round trip to open the cursor. This additional round trip can be avoided when the same &lt;code&gt;PreparedStatement&lt;/code&gt; instance is used again. As a side effect, &lt;code&gt;PARSING&lt;/code&gt; overhead is also reduced.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Increased PreFetch&lt;/span&gt;&lt;br&gt;The Oracle network layer utilizes a technique called &lt;em&gt;PreFetch&lt;/em&gt; to reduce round trips for &lt;code&gt;SELECT&lt;/code&gt; statements. Instead of retrieving every single row individually from the server, a certain number or rows is transferred in one round trip. The Oracle client library handles this &lt;em&gt;PreFetch&lt;/em&gt; transparently and still provides one row after each other to the application software. For &lt;code&gt;SELECT&lt;/code&gt; statements, PreFetch is almost as powerful as batch execution for &lt;code&gt;INSERT&lt;/code&gt;/&lt;code&gt;UPDATE&lt;/code&gt;/&lt;code&gt;DELETE&lt;/code&gt; statements and can reduce the latencies by an order of magnitude for large result sets. The current default for the Oracle JDBC driver is to fetch at most 10 rows in each server round trip. The JDBC standard provides a way manipulate this value: &lt;tt&gt;Statement.setFetchSize(20);&lt;/tt&gt; If your application typically fetches 20 records, you might set the value accordingly. There is no (notable) harm for statements that return less than 20 records. &lt;br&gt;&lt;b&gt;UPDATE 2010-01-29:&lt;/b&gt; This paragraph was updated to reflect my later findings regarding &lt;a href="http://myfatalmind.wordpress.com/2010/01/29/oracle-jdbc-prefetch-portability/"&gt;Oracle JDBC PreFetch Portability&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following chart shows various select executions with different &lt;em&gt;PreFetch&lt;/em&gt; sizes. The first pair is a mass select to retrieve 40000 rows from the database. Fetching twice as many records in one round trip, cuts the overall time down to the half. The other bars show the execution of a statement that returns 20 rows. To get reasonable values those statements were executed 1000 times in a row. The repeated execution allows the &lt;em&gt;open cursor&lt;/em&gt; optimization to be applied as well. The middle pair of bars shows the execution performance with a &lt;code&gt;PreparedStatement&lt;/code&gt; that is closed after each execution. The last bars show the same but the &lt;code&gt;PreparedStatement&lt;/code&gt; was opened only once and used then for all 1000 executions.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" height="250" src="http://myfatalmind.files.wordpress.com/2009/12/latency.png?w=580&amp;amp;h=250" title="The effects of server round trips" width="580"&gt;&lt;/p&gt;
&lt;p&gt;The latency difference between 10 records &lt;em&gt;PreFetch&lt;/em&gt; with a “closed cursor” to the 20 records &lt;em&gt;PreFetch&lt;/em&gt; with an “&lt;em&gt;open cursor&lt;/em&gt;” is 70%.&lt;/p&gt;
&lt;p&gt;Please note that the statement used for this statistics was very trivial and can be assumed to be executed instantly. The improvements visible in the chart above do therefore indicate the maximum possible improvement.&lt;/p&gt;
&lt;h3&gt;The Bottom Line&lt;/h3&gt;
&lt;p&gt;Increasing network complexity introduces additional latencies that might affect applications performance.&lt;/p&gt;
&lt;p&gt;There are many ways to reduce server round trips to make an application less sensitive to network latencies. Most of them involve revised process flows and increase application complexity.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“Firewall qualified”&lt;/em&gt; applications already have an advantage in competition and attest awareness for complex production deployments.&lt;/p&gt;
&lt;p&gt;It’s not &lt;strong&gt;fatal&lt;/strong&gt; to open your &lt;strong&gt;mind&lt;/strong&gt; to new approaches.&lt;/p&gt;
&lt;h3&gt;How Internet Works&lt;/h3&gt;
&lt;p&gt;There is another &lt;a href="http://vladstudio.com/cs/wallpaper?how_internet_works/1280x1024/low"&gt;excellent cartoon that explains latency&lt;/a&gt; in context of the Internet.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/106/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/106/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/106/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/106/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/106/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/106/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/106/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/106/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/106/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/106/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=106&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/1fG5d-7Wt1Q" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /><media:content url="http://myfatalmind.files.wordpress.com/2009/12/latency.png" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2009/12/22/latency-security-vs-performance/</feedburner:origLink></item><item><title>To Trace or Not to Trace</title><link>http://feedproxy.google.com/~r/orana_dba/~3/9NhrAGwypyk/</link><category>Maintainability</category><category>oracle</category><category>sql</category><category>trace</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Tue, 24 Nov 2009 09:49:44 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/24dc6b17ffe5dc0d</guid><description>&lt;p&gt;I have always been a fan of the oracle SQL trace facility. It’s a very powerful method to analyze performance problems, especially if you don’t know the application very well. If you don’t know oracle SQL tracing yet, you will find some info at &lt;a href="http://www.orafaq.com/wiki/SQL_Trace" title="SQL Trace @ orafaq"&gt;orafaq&lt;/a&gt; and some more details at &lt;a href="http://www.oracle-base.com/articles/10g/SQLTrace10046TrcsessAndTkprof10g.php" title="Oracle-base on SQL Tracing"&gt;orable-base&lt;/a&gt;. Obviously, Oracle has also some &lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14211/sqltrace.htm#i4640" title="Oracle&amp;#39;s SQL Trace documentation"&gt;documenation&lt;/a&gt; about it.&lt;/p&gt;
&lt;p&gt;In short, the Oracle SQL tracing facility causes the database server to write all executed SQL statements to a file for later analysis. One of the main issues with SQL tracing is the huge size of the trace files; every statement execution is logged, potentially with additional data about the execution plan, wait events, and bind values.&lt;/p&gt;
&lt;p&gt;This article introduces the new possibilities of Oracle 10g to enable tracing on a very fine level. With a properly instrumented application, tracing can be enabled for specific users or program modules only. With such a fine control over the tracing, the generated data volume can be kept small and SQL tracing becomes a much more useful tool.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;The first part of the article explains how a Java application can be instrumented to allow fine-grained tracing while the second part shows the &lt;a href="http://blog.fatalmind.com/#admininterface"&gt;administrative interface&lt;/a&gt; to enable tracing for specific modules.&lt;/p&gt;
&lt;h3&gt;Instrumenting the Application&lt;/h3&gt;
&lt;p&gt;First things first. Before the database can selectively trace for specific users or modules, the application must tell the database to which user or module an actual SQL statement belongs.&lt;/p&gt;
&lt;p&gt;Luckily we don’t have to explicitly specify this for each individual statement. It’s more like a session property that can be set at any time. Once set, it affects all SQLs until a different value is set.&lt;/p&gt;
&lt;p&gt;The three values that affect tracing are &lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt;, &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt;. Those must be properly set in order to allow selective tracing. With properly I mean two aspects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Allow meaningful filtering&lt;/span&gt;&lt;br&gt;&lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt; can be used freely. &lt;em&gt;&lt;code&gt;MODULE&lt;/code&gt;&lt;/em&gt; and &lt;code&gt;ACTION&lt;/code&gt; depend on each other; it’s not possible to trace a specific action in every module. In other words, a &lt;code&gt;MODULE&lt;/code&gt; is a container for &lt;code&gt;ACTIONS&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Don’t introduce a performance impact&lt;/span&gt;&lt;br&gt;
&lt;p&gt;The Oracle database provides a way to set the &lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt;, &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; without doing an additional server round-trip. The information is sent piggyback with the next round-trip. You must take care to use the proper implementation to not introduce an additional server round-trip that would introduce a major performance impact.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;UPDATE:&lt;/b&gt; The issue about server round tips is also the topic of later article: &lt;a href="http://blog.fatalmind.com/2009/12/22/latency-security-vs-performance/"&gt;Latency: Security vs. Performance&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The Required JDBC API&lt;/h3&gt;
&lt;p&gt;Since all of the described functionality is an oracle specific extension, the JDBC standard doesn’t provide an API to set the required attributes. To access that functionality you must &lt;em&gt;cast&lt;/em&gt; the standard JDBC &lt;code&gt;Connection&lt;/code&gt; to an &lt;code&gt;OracleConnection&lt;/code&gt; object first. That’s all, the &lt;code&gt;OracleConnection&lt;/code&gt; class provides the two methods we need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://download.oracle.com/docs/cd/B13789_01/java.101/b10979/endtoend.htm" title="Oracle End To End Metrics JDBC"&gt;&lt;code&gt;OracleConnection.setEndToEndMetrics()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://download.oracle.com/docs/cd/B13789_01/java.101/b10979/endtoend.htm" title="Oracle End To End Metrics JDBC"&gt;&lt;code&gt;OracleConnection.getEndToEndMetrics()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The naming might confuse in the first place, but when you consider that the actual identifier for a real human being is intended to be put into the &lt;code&gt;CLIENT_IDENTIFER&lt;/code&gt; field, it might become clear that this is really for end-to-end measurements.&lt;/p&gt;
&lt;p&gt;The actual data is passed as an &lt;code&gt;String&lt;/code&gt; array that contains an entry for the properties we are interested in —along with others. You can get all of them and provide new ones. Remember that neither &lt;code&gt;getEndToEndMetrics()&lt;/code&gt; nor &lt;code&gt;setEndToEndMetrics()&lt;/code&gt; causes a server round trip. In case you change any property, the change is sent piggyback with the next server round-trip. I have used the following class for my tests:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;class OracleEndToEnd {
 String oldData[];
 OracleConnection ocon;

 public
   OracleEndToEnd(Connection con, String module,
                  String client , String action)
   throws SQLException
 {
   if (con instanceof OracleConnection) {
     ocon = (OracleConnection)con;
     oldData = ocon.getEndToEndMetrics();
     if (oldData == null) {
       oldData =
          new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
     }

     String[] metrics =
        new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];

     int minidx = (metrics.length &amp;lt; oldData.length)
                                  ?
                   metrics.length : oldData.length;
     System.arraycopy(oldData, 0, metrics, 0, minidx);

     if (action != null) {
       metrics[OracleConnection.END_TO_END_ACTION_INDEX] = action;
     }
     if (client != null) {
       metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = client;
     }
     if (module != null) {
       metrics[OracleConnection.END_TO_END_MODULE_INDEX] = module;
     }
     ocon.setEndToEndMetrics(metrics, (short) 0);
   }
 }

 public void
 endSection()
 throws SQLException
 {
   if (ocon != null) {
     ocon.setEndToEndMetrics(oldData, (short) 0);
   }
 }
}&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;This class is intended to be used as wrapper around a specific section of your code (not to say module or action) like this:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;OracleEndToEnd oee = new OracleEndToEnd(con, "MOD1", null, "ACT1");
try {
   selectIt(con);
} finally {
   oee.endSection();
}&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;When done carefully, a hierarchical structure of &lt;code&gt;MODULES&lt;/code&gt; and &lt;code&gt;ACTIONS&lt;/code&gt; can be build. This code was of course only my proof-of-concept code.&lt;/p&gt;
&lt;p&gt;To instrument your existing application, you should look out for central places where you can add the code as shown above. In example, many applications use a database connection pool explicitly. By instrumenting the pool and passing additional parameters—like &lt;code&gt;CLIENT_IDENTIFER&lt;/code&gt; and &lt;code&gt;MODULE&lt;/code&gt;—to the &lt;code&gt;get()&lt;/code&gt;/&lt;code&gt;borrow()&lt;/code&gt;/&lt;code&gt;whatever()&lt;/code&gt; method, much of the overhead can be centralized.&lt;/p&gt;
&lt;p&gt;The best way to instrument your application depends to the frameworks in use. That’s the time to mention that I am &lt;a href="http://www.fatalmind.com/consulting/" title="Markus Winand&amp;#39;s consulting page"&gt;consultant&lt;/a&gt;, let me know if I can help you &lt;img src="http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif" alt=";)"&gt; &lt;/p&gt;
&lt;h3&gt;Performance Comparison&lt;/h3&gt;
&lt;p&gt;I have written an example Java implementation—on basis of the class presented above—to check the performance impact of a properly instrumented application. To make the long story short: in a networked environment there isn’t any considerable impact when properly instrumenting a Java application. In case the application and the database are hosted on the same node, if no network involved, the impact might become noticeable (8% in my experiment), but this was still an extreme case. Generally I believe that a &lt;strong&gt;proper&lt;/strong&gt; instrumentation does not impact performance in a noticeable way.&lt;/p&gt;
&lt;p&gt;For my performance experiment, I used a program that issued the same SQL statement many thousand times. To keep the execution time of the statement low, I have used a very trivial statement:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;select 1 from dual&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have executed the program with and without instrumentation for the following scenarios:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Without any tracing enabled&lt;/li&gt;
&lt;li&gt;With full tracing enabled (trace everything)&lt;/li&gt;
&lt;li&gt;Trace only every second SQL execution (only with the instrumented code)&lt;/li&gt;
&lt;li&gt;Trace only 1% of the executed statements (only with the instrumented code).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The results are in the chart in a networked environment—application and database hosted on different nodes:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Performance comparison" height="250" src="http://myfatalmind.files.wordpress.com/2009/11/frame1.png?w=580&amp;amp;h=250&amp;amp;h=250" title="Performance comparison" width="580"&gt;&lt;/p&gt;
&lt;p&gt;There is &lt;strong&gt;NO&lt;/strong&gt; measurable impact when tracing is disabled. This is the most important aspect. You can just add the needed &lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt;, &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; and it will not harm performance—when done properly. When tracing is enabled, there actually &lt;strong&gt;IS&lt;/strong&gt; a performance drawback. I am not sure where this comes from; I could imagine that the additional data written into the trace file—module name and so on—cause the regression . However, that impact is still &lt;strong&gt;minor&lt;/strong&gt;, 5% in my extreme test scenario. For test case 3 and 4 there is no direct comparison because partial tracing requires the instrumented code. The checked bars are the results of the code without instrumentation but full tracing enabled—the only way to gather similar SQL trace files. You see that the performance impact relates to the traced volume. When tracing only a very small fraction of the overall code the impact is hardly measurable.&lt;/p&gt;
&lt;p&gt;All of this requires a &lt;strong&gt;proper instrumentation&lt;/strong&gt; of the application not to cause additional round trips.&lt;/p&gt;
&lt;h3&gt;The Administrative Interface&lt;/h3&gt;
&lt;p&gt;With Oracle 10g, yet another way to enable tracing is provided in form of the &lt;a href="http://download.oracle.com/docs/cd/E11882_01/appdev.112/e10577/d_monitor.htm#i1003993"&gt;&lt;code&gt;DBMS_MONITOR&lt;/code&gt;&lt;/a&gt; package. This package adds some great tools to perform SQL Tracing in a real world scenario.&lt;/p&gt;
&lt;p&gt;Instead of&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;ALTER SESSION SET SQL_TRACE=TRUE;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;ALTER SESSION SET EVENT='10046 trace name context forever, level 4'&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;we can now use&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.SESSION_TRACE_ENABLE(binds=&amp;gt;true);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which is actually the same in pink—except the less cryptic syntax—no benefit. Very often we want to enable tracing from &lt;em&gt;outside&lt;/em&gt; the current session, which is also possible with the same procedure:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.SESSION_TRACE_ENABLE(sid, serial#, binds=&amp;gt;true);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Again, a nicer way to do what was possible before; with &lt;code&gt;DBMS_SYSTEM.SET_EV&lt;/code&gt;. But now, you have only one package to remember: &lt;code&gt;DMBS_MONITOR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Sometimes we want to trace as session that will be opened somewhat later. For example, a batch job that runs at night. For this case the traditional solution was to create a &lt;code&gt;SYSTEM LOGON&lt;/code&gt; trigger. The body of the trigger typically checks some conditions to decide to trace or not to trace. Very often, the decision is based on the user-name that is fetched via &lt;code&gt;SYS_CONTEXT/USERENV&lt;/code&gt; or information from &lt;code&gt;v$session&lt;/code&gt;. Many times, applied guesswork is added to the trigger. As a last resort, if it’s not even possible to trace all session that are opened during a specific time frame, it’s also possible to trace much more than needed and filter afterwards.&lt;/p&gt;
&lt;p&gt;In a real world scenario, tracing everything is often not feasible because of the &lt;em&gt;huge data volume&lt;/em&gt; created. Many people also argue that there is a considerable performance hit. Although I admit, and demonstrate below, that there &lt;em&gt;IS&lt;/em&gt; a performance hit, it is usually negligible if neither waits nor binds are traced. The reason that tracing can’t be enabled globally on a production system, is the disk space requirement.&lt;/p&gt;
&lt;h3&gt;The DBMS_MONITOR Killer Features&lt;/h3&gt;
&lt;p&gt;The real power of &lt;code&gt;DBMS_MONTOR&lt;/code&gt; isn’t that it provides a more convenient interface to enable tracing. The real power comes from&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the ability to persistently configure tracing&lt;/li&gt;
&lt;li&gt;the ability to perform fine grained, selective tracing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With &lt;code&gt;DBMS_MONITOR&lt;/code&gt;, the need for &lt;code&gt;LOGON&lt;/code&gt; triggers is gone. E.g., to enable tracing for all active and all &lt;em&gt;future&lt;/em&gt; session for a specific service—like &lt;code&gt;SERVICE_NAME&lt;/code&gt; in the connect descriptor—use the following:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE('SERVICE_NAME');&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;This will enable tracing for all current and future sessions with the specified &lt;code&gt;SERVICE_NAME&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The second killer feature is the ability to perform selective tracing based on&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;client identifier&lt;/li&gt;
&lt;li&gt;module&lt;/li&gt;
&lt;li&gt;action&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are session properties can be set the the application. When done properly, the application can change those properties at a &lt;em&gt;very high frequency&lt;/em&gt;—for every statement—&lt;em&gt;without&lt;/em&gt; introducing a considerable performance hit.&lt;/p&gt;
&lt;p&gt;For example, if the application sets the &lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt; to reflect the user ID of a websites user, tracing can be enabled for that particular user only. Similarly selective tracing on &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; level is possible. Consider the following activities happening in a session:&lt;/p&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr align="left"&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;statement&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MOD1&lt;/td&gt;
&lt;td&gt;ACT1&lt;/td&gt;
&lt;td&gt;select * from …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MOD1&lt;/td&gt;
&lt;td&gt;ACT2&lt;/td&gt;
&lt;td&gt;insert into …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MOD2&lt;/td&gt;
&lt;td&gt;ACT1&lt;/td&gt;
&lt;td&gt;update…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MOD2&lt;/td&gt;
&lt;td&gt;ACT3&lt;/td&gt;
&lt;td&gt;delete …&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;With &lt;code&gt;DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE&lt;/code&gt; we can enable tracing for &lt;code&gt;MOD1&lt;/code&gt; only if you like:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE('SERVICE_NAME', 'MOD1');&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The effect is as expected: only the statements attributed to MOD1 will be traced. Consequently you can drill down to trace only ACT1 within MOD1:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE(&amp;#39;SERVICE_NAME&amp;#39;, &amp;#39;MOD1&amp;#39;,
                                  &amp;#39;ACT1&amp;#39;, waits=&amp;gt;true, binds=&amp;gt;true);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;This will trace only the &lt;em&gt;insert&lt;/em&gt; statement, with wait and bind data. Similar possibilities exist for the &lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE(&amp;#39;CLIENT_IDENTIFIER&amp;#39;,
                                         waits=&amp;gt;false, binds=&amp;gt;true);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Remember that all of this is persistent, even across database restarts. So we need to disable it again. Of course we will forget what was enabled, so there is a table to query for that:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; SELECT trace_type, primary_id,
            qualifier_id1 ID1, qualifier_id2 ID2,
            waits, binds
       FROM dba_enabled_traces;&lt;/pre&gt;
&lt;pre&gt;TRACE_TYPE             PRIMARY_ID        ID1    ID2   WAITS BINDS
---------------------- ----------------- ------ ----- ----- -----
SERVICE_MODULE_ACTION  SERVICE_NAME      MOD1   ACT1  TRUE  TRUE
SERVICE                SERVICE_NAME                   TRUE  FALSE
SERVICE_MODULE         SERVICE_NAME      MOD1         TRUE  FALSE
CLIENT_ID              CLIENT_IDENTIFIER              FALSE TRUE&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;With these methods we can enable tracing on a very fine granularity in a very convenient way. Since the tracing can be focused, space and performance issues are less problematic.&lt;/p&gt;
&lt;h3&gt;Résumé&lt;/h3&gt;
&lt;p&gt;Once an application is instrumented, fine-grained tracing can be applied very straight. Because of the reduced overhead and disk space requirements, SQL Tracing might become a more frequently used tool to troubleshoot poor performance.&lt;/p&gt;
&lt;p&gt;You can choose the effort you put into instrumentation by yourself. For example, you might choose to set the &lt;code&gt;MODULE&lt;/code&gt; only for one particular module that you need to trace right now, and don’t adopt other modules. In a step-by-step manner you can extend the instrumentation as needed.&lt;/p&gt;
&lt;p&gt;During my experiments for this article I have found some traps you should be aware of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;The &lt;code&gt;ACTION&lt;/code&gt; seems to consider only 8 characters&lt;/span&gt;&lt;br&gt;During my tests I noticed that the significant length of the &lt;code&gt;ACTION&lt;/code&gt; seems to be only 8 characters (&lt;a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_appinf.htm#i999254" title="DBMS_APPLICATION_INFO package"&gt;although documented as 32 bytes&lt;/a&gt;). However, any attempt to enable tracing for an &lt;code&gt;ACTION&lt;/code&gt; longer than 8 bytes failed on my 10g test machine.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;A wrong implementation might introduce a major performance penalty&lt;/span&gt;&lt;br&gt;Double check your implementation for that.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;&lt;code&gt;SERVICE_NAME&lt;/code&gt; might be set to an unexpected value&lt;/span&gt;&lt;br&gt;Depending on the way the application connects, &lt;code&gt;SERVICE_NAME&lt;/code&gt; might not be present so that it defaults to &lt;code&gt;SYS$USER&lt;/code&gt;. If it doesn’t work on the first try, verify that the sessions you want to trace has the expected &lt;code&gt;SERVICE_NAME&lt;/code&gt; (&lt;code&gt;select serivce_name from v$session....&lt;/code&gt;). Use the &lt;a href="http://www.rojotek.com/blog/2008/01/04/oracle-sid-service_name/" title="SID != SERVICE_NAME"&gt;correct connect URL&lt;/a&gt; with the thin driver.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;When using &lt;code&gt;DBMS_MONITOR&lt;/code&gt;, wait event tracing is &lt;em&gt;TRUE&lt;/em&gt; per default&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s not &lt;strong&gt;fatal&lt;/strong&gt; to open your &lt;strong&gt;mind&lt;/strong&gt; to new approaches.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/45/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/45/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/45/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/45/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/45/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/45/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/45/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/45/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/45/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/45/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=45&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/9NhrAGwypyk" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /><media:content url="http://myfatalmind.files.wordpress.com/2009/11/frame1.png?w=580&amp;h=250" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2009/11/24/to-trace-or-not-to-trace/</feedburner:origLink></item><item><title>Pipelined Functions: Better Than DBMS_OUTPUT</title><link>http://feedproxy.google.com/~r/orana_dba/~3/GZhAxPzKqUU/</link><category>Portability</category><category>oracle</category><category>pl/sql</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Wed, 11 Nov 2009 07:36:06 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/7b1f219d99ed5c13</guid><description>&lt;p&gt;Every now and then I need some PL/SQL that prints something to the terminal. The traditional solution for this is &lt;code&gt;DBMS_OUTPUT.PUT_LINE&lt;/code&gt;. If you used it before, you probably know that there are some obstacles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don’t forget to &lt;code&gt;set serveroutput on&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Don’t forget to set an appropriate buffer size (but there is still a absolute maximum)&lt;/li&gt;
&lt;li&gt;Don’t wonder about missing blanks&lt;/li&gt;
&lt;li&gt;No way to &lt;em&gt;flush&lt;/em&gt; the output&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In case you don’t know yet, the first three can be taken care of by issuing the following in SQL*Plus:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;set serveroutput on size 1000000 format wrapped;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, the absolute buffer size limit remains. The years have passed, and the limitations were accepted. Until I noticed that there is an alternative.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;
&lt;p&gt;With release 9iR2, Oracle delivered a package to format the &lt;code&gt;PLAN_TABLE&lt;/code&gt;’s content: &lt;code&gt;DBMS_XPLAN&lt;/code&gt;. A very nifty tool which finally rendered the SQLs to format the explain plan useless. But explain plan is not today’s topic; lets just focus in how the formatted plan reaches our screen:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;select * from table(DBMS_XPLAN.DISPLAY);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The easiest way to use the package is shown above. It’s obvious that the data is actually fetched by regular &lt;code&gt;select&lt;/code&gt; statement. You will also notice this by SQL*Plus artifacts like the feedback line (8 rows selected):&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; explain plan for select * from dual;

Explained.

SQL&amp;gt;  select * from table(DBMS_XPLAN.DISPLAY);

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------
Plan hash value: 272002086

---------------------------------------------------------------------
| Id | Operation         | Name | Rows | Bytes | Cost (%CPU)| Time  |
---------------------------------------------------------------------
|  0 | SELECT STATEMENT  |      |    1 |     2 |     2   (0)| 00:01 |
|  1 |  TABLE ACCESS FULL| DUAL |    1 |     2 |     2   (0)| 00:01 |
---------------------------------------------------------------------

8 rows selected.

SQL&amp;gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The table you see isn’t actually a table (as the database knows it), it’s just formatted output. When using &lt;code&gt;DBMS_XPLAN&lt;/code&gt;, I always noted to myself to investigate this. Some years later, the time has come.&lt;/p&gt;
&lt;h3&gt;Pipelined Table Functions&lt;/h3&gt;
&lt;p&gt;Oracle’s (pipelined) table functions are PL/SQL functions that return data as (more or less) regular tables that can be accessed with regular SQL. The &lt;em&gt;pipelined&lt;/em&gt; feature makes it possible to fetch the first records while the function is still running.&lt;/p&gt;
&lt;p&gt;It seems that the primary target of this feature are custom analytic functions and data re-arranging like pivot. However, I use it to get rid of &lt;code&gt;DBMS_OUTPUT&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Hello World&lt;/h3&gt;
&lt;p&gt;The following is the most simplistic example how to use a pipelined function to return arbitrary output:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;create or replace type piped_output_table as table of varchar2(4000);
/&lt;/b&gt;

create or replace
 function hello_world &lt;b&gt;return piped_output_table pipelined&lt;/b&gt; IS
 text varchar2(4000);
begin
 text := 'Hello world';
&lt;b&gt; pipe row(text);
 RETURN;&lt;/b&gt;
END;
/
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;hello_world&lt;/code&gt; function must be wrapped in a &lt;code&gt;table()&lt;/code&gt; expression to access it like a regular table in SQL:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt;  select * from table(hello_world);
COLUMN_VALUE
---------------------------------------------------------------------
Hello world

1 row selected.

SQL&amp;gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;We have already mastered everything important to replace &lt;code&gt;DBMS_OUTPUT&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define a &lt;code&gt;table type&lt;/code&gt; to be used as return type of the function&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;pipelined&lt;/code&gt; keyword&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;PIPE ROW()&lt;/code&gt; statement to actually produce a row.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;RETURN&lt;/code&gt; statement without an argument.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, there are two more issues to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You might want to have a different column name (instead of &lt;code&gt;COLUMN_VALUE&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You might need to produce output from nested functions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Custom Column Names&lt;/h3&gt;
&lt;p&gt;The first issue is rather simple; to name the returned column(s) you must create a object type and base the table type upon this object type:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;create or replace type piped_output is object (output varchar2(4000));
/&lt;/b&gt;

create or replace type piped_output_table is table of &lt;b&gt;piped_output&lt;/b&gt;;
/

create or replace function
   hello_world return piped_output_table pipelined IS
   &lt;b&gt;rec piped_output := piped_output(NULL);&lt;/b&gt;
begin
   &lt;b&gt;rec.output&lt;/b&gt; := 'Hello world';
   pipe row(&lt;b&gt;rec&lt;/b&gt;);
   RETURN;
END;
/
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The column header can have an arbitrary column name. Since it is a column name, all respective limitations apply (e.g., no spaces).&lt;/p&gt;
&lt;h3&gt;Nested Functions&lt;/h3&gt;
&lt;p&gt;The final requirement to replace &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; is produce output from nested function calls. With &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; this is rather straight:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;CREATE OR REPLACE PACKAGE dbms_output_nested IS
   PROCEDURE hello_world;
END;
/

CREATE OR REPLACE PACKAGE body dbms_output_nested IS
   PROCEDURE nested_procedure IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE('  hello world from nested function') ;
   END nested_procedure;

   PROCEDURE hello_world IS
   BEGIN
      DBMS_OUTPUT.ENABLE();
      DBMS_OUTPUT.PUT_LINE('hello world') ;
      nested_procedure;
   END hello_world;
END dbms_output_nested;
/&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unfortunately, I am not aware of an easy way to accomplish this with pipelined table functions. It seems that there is no way to directly pipe one functions output into the output pipe of another function. So far, the only solution I found is to explicitly copy the records over:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;CREATE OR REPLACE PACKAGE piped_nested IS
   TYPE piped_output IS RECORD (output VARCHAR2(4000));
   TYPE piped_output_table IS TABLE OF piped_output;

   FUNCTION hello_world
     RETURN piped_nested.piped_output_table PIPELINED;
&lt;b&gt;   FUNCTION nested_procedure
     RETURN piped_nested.piped_output_table PIPELINED;&lt;/b&gt;
END;
/

CREATE OR REPLACE PACKAGE BODY piped_nested IS
   FUNCTION nested_procedure
     RETURN piped_nested.piped_output_table PIPELINED IS
      rec piped_nested.piped_output;
   BEGIN
      rec.output := '   hello world from nested function';
      PIPE ROW(rec);
      RETURN;
   END;

   FUNCTION hello_world
     RETURN piped_nested.piped_output_table PIPELINED IS
      rec piped_nested.piped_output;
   BEGIN
      rec.output := 'Hello World';
      PIPE ROW(rec);

&lt;b&gt;      FOR r IN (select * from table(nested_procedure)) LOOP
         rec.output := r.output;
         PIPE ROW(rec);
      END LOOP;&lt;/b&gt;

      RETURN;
   END;
END piped_nested;
/
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;You see that there are two major drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;nested_procedure&lt;/code&gt; is public&lt;/li&gt;
&lt;li&gt;The copy bloats the &lt;code&gt;hello_world&lt;/code&gt; function&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As mentioned above, I don’t have a good solution for this (let me know if you find something).&lt;/p&gt;
&lt;h3&gt;Mixing DBMS_OUTPUT with Pipelined Functions&lt;/h3&gt;
&lt;p&gt;If you have legacy PL/SQL code that uses &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; and would like to migrate to pipelined functions, you might consider the following wrapper:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;CREATE OR REPLACE PACKAGE dbms_output_table IS
   TYPE piped_output IS RECORD (dbms_output VARCHAR2(4000));
   TYPE piped_output_table IS TABLE OF piped_output;
   FUNCTION display RETURN piped_output_table PIPELINED;
END;
/

CREATE OR REPLACE PACKAGE BODY dbms_output_table IS
   FUNCTION display RETURN piped_output_table PIPELINED IS
      rec piped_output;
      p_status NUMBER;
   BEGIN
      DBMS_OUTPUT.GET_LINE(rec.dbms_output, p_status);
      WHILE p_status = 0 LOOP
         PIPE ROW(rec);
         DBMS_OUTPUT.GET_LINE(rec.dbms_output, p_status);
      END LOOP;
      RETURN;
   END;
END;
/&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;With this function you can retrieve the current content of the &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; buffer via a pipelined table function. Please note that the producer of the &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; must enable the &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; facility and SQL*Plus must be &lt;code&gt;set serverout off&lt;/code&gt; (otherwise SQL*Plus would retrieve it):&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt;  exec dbms_output_nested.hello_world;
hello world
hello world from nested function

PL/SQL procedure successfully completed.

SQL&amp;gt; set serverout off
SQL&amp;gt;  exec dbms_output_nested.hello_world;

PL/SQL procedure successfully completed.

SQL&amp;gt; select * from table(dbms_output_table.display);

DBMS_OUTPUT
---------------------------------------------------------------------
hello world
 hello world from nested function

SQL&amp;gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The Best of Both Worlds?&lt;/h3&gt;
&lt;p&gt;This—and similar methods—can be used so that nested functions still use the &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; interface, while the front end function returns its output as pipelined table. A mixed approach allows you to hide the nested function—no need to have them in the public package declaration—but reapplies some of the &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; limitations; the maximum buffer size (although at smaller level), no flush.&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;CREATE OR REPLACE PACKAGE mixed_package IS
   TYPE piped_output IS RECORD (output VARCHAR2(4000));
   TYPE piped_output_table IS TABLE OF piped_output;
   FUNCTION hello_world RETURN piped_output_table PIPELINED;
END;
/

CREATE OR REPLACE PACKAGE BODY mixed_package IS
   PROCEDURE prepare_dbms_output IS
   BEGIN
      DBMS_OUTPUT.DISABLE; -- removes any stale data
      DBMS_OUTPUT.ENABLE(1000000);
   END;

   PROCEDURE nested_function IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE('   Hello world from nested function');
   END;

   FUNCTION hello_world RETURN piped_output_table PIPELINED IS
      rec piped_output;
      p_status NUMBER;
   BEGIN
      prepare_dbms_output;

      rec.output := 'Hello World';
      PIPE ROW(rec);

      nested_function;

      DBMS_OUTPUT.GET_LINE(rec.output, p_status);
      WHILE p_status = 0 LOOP
         PIPE ROW(rec);
         DBMS_OUTPUT.GET_LINE(rec.output, p_status);
      END LOOP;

      RETURN;
   END;
END;
/
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can also use other methods to return row data from nested functions and pass it on to the PIPE ROW statement in the topmost stack frame. No matter in which way pass rows from nested functions, you must be aware that they are passed as a bulk when the nested function finishes. That means, the are kind of &lt;em&gt;flushed&lt;/em&gt; once for each nested function call. If all you data is returned from a nested function, you might &lt;em&gt;effectively disable the pipelined&lt;/em&gt; feature.&lt;/p&gt;
&lt;h3&gt;Final Comparison and Critique&lt;/h3&gt;
&lt;p&gt;The user interface of pipelined table functions is much better than the well established &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;No separate API needed to access the returned data&lt;/span&gt;&lt;br&gt;Most programming languages don’t have a convenient way to access the &lt;code&gt;serveroutput&lt;/code&gt;. Very often the only way to fetch the buffer is to manually call &lt;code&gt;DBMS_OUTPUT.GET_LINES&lt;/code&gt;. On a quick view, only the perl DBI/DBD::Oracle framework has addressed the problem (a little bit):
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://search.cpan.org/~pythian/DBD-Oracle-1.23/Oracle.pm#dbms_output_enable_/_dbms_output_put_/_dbms_output_get" title="DBD::Oracle"&gt;perl DBD::Oracle documentation about DBMS_OUTPUT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:45027262935845" title="Ask Tom on DBMS_OUTPUT via JDBC"&gt;Tom Kyte about fetching DBMS_OUTPUT buffer with JDBC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://forums.oracle.com/forums/thread.jspa?threadID=204531" title="Fetching DBMS_OUTPUT with php"&gt;forums.oracle.com entry about php and DBMS_OUTPUT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But wait, your scripts are anyways intended for SQL*Plus usage only? Besides the fact that you &lt;em&gt;never &lt;/em&gt;know in which context your scripts are used, there are also other user interfaces than SQL*Plus to access an Oracle database. There are many IDE that support database access. Most of those have support for &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; in the meanwhile but it’s different in each implementation. You can avoid a great deal of questions where to find the output of your script in tool XYZ by using table functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;No size limitation&lt;/span&gt;&lt;br&gt;But wait, your script isn’t returning much data? Yes, you usually know how much data your script will return. With a call to &lt;code&gt;DBMS_OUTPUT.ENABLE&lt;/code&gt; you can make the buffer big enough. But if you are wrong? You are lucky if the user knows how to enlarge the buffer. You are even more happy if the hard limit of the buffer size is not reached (1,000,000 bytes). I know of a case where the user was required to change a script to use &lt;code&gt;UTL_FILE&lt;/code&gt; because the output was a few megabytes (luckily they had access to the database back-end, where the file was written).&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Pipelined&lt;/span&gt;&lt;br&gt;Self-explaining? No major usability benefit, I guess. However, it is a benefit to see that something is happening while the function is running. It is definitely a benefit not to buffer everything in memory until the end—that is probably the reason why there is a hard limit on the buffer size.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I guess the article gave you insight to the major drawback of the pipelined table function method as well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Producing output from nested function calls is a pain in the a**&lt;/span&gt;&lt;br&gt;It is possible, but inconvenient—if not delusive because it exploits internal functions in the public interface. The use of collections in the nested function makes it possible to maintain a clean public interface. However, copying the data over and over again is inconvenient, if not problematic, because it might reintroduce the size issue (when using &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; the already mentioned limit of 1,000,000 bytes per nested function call apply).&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;DML in the table function cannot participate in the caller’s transaction&lt;/span&gt;&lt;br&gt;&lt;b&gt;UPDATE 2010-03-11:&lt;/b&gt; &lt;a href="http://blog.sydoracle.com/"&gt;Gary&lt;/a&gt; &lt;a href="http://blog.fatalmind.com/2009/11/11/pipelined-functions-better-then-dbms_output/#comments"&gt;commented&lt;/a&gt; that
&lt;pre&gt;select * from table(hello_world)&lt;/pre&gt;
&lt;p&gt;works only if the function does not perform any DML. Gary is of course right; any attempt to do so results in &lt;a href="http://ora-14551.ora-code.com/"&gt;ORA-14551&lt;/a&gt;. The only possibility to perform DML in the function is to use an &lt;a href="http://www.orafaq.com/wiki/Autonomous_transaction"&gt;autonomous transaction&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My personal Best Current Practice is to use other methods (collections or even &lt;code&gt;DBMS_OUTPUT&lt;/code&gt;) in the private functions, but use a pipelined function interface for the user. This is sometimes very inconvenient to code but the improved user experience is worth that effort.&lt;/p&gt;
&lt;p&gt;Of course I would have some wishes how the situation could be improved. The most powerful way would be to allow something like this:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;PIPE ROW(nested_pipelined_function);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Where &lt;code&gt;nested_pipelined_function&lt;/code&gt; doesn’t need to be part of the public package interface. Each &lt;code&gt;PIPE ROW()&lt;/code&gt; in the nested function should directly output as if was written in the calling function. On the other hand, it would also be great to have something to pipe a complete collection at once:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;PIPE ROWS(collection);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am well aware that the usage of pipelined table functions as demonstrated in this article might not reflect the intended usage of that feature, thus its fitness for that purpose is limited.&lt;/p&gt;
&lt;p&gt;It’s not &lt;strong&gt;fatal&lt;/strong&gt; to open your &lt;strong&gt;mind&lt;/strong&gt; to new approaches.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Similar articles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://berxblog.blogspot.com/2009/01/pipelined-function-vs-dbmsoutput.html"&gt;http://berxblog.blogspot.com/2009/01/pipelined-function-vs-dbmsoutput.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/4/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/4/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/4/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/4/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/4/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/4/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/4/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/4/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/4/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/4/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=4&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana_dba/~4/GZhAxPzKqUU" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2009/11/11/pipelined-functions-better-then-dbms_output/</feedburner:origLink></item></channel></rss>
