expert one on one oracle


  Expert one-on-one Oracle 2     This page intentionally left blank Expert one-on-one Oracle 3 Content  Introduction.....................................................................................................................................18 What this Book is About.......................................................................................................18 Who Should Use this Book?.................................................................................................19 How This Book is Structured...............................................................................................20 Understanding the Database............................................ 21 Database Structures and Utilities ........................................ 22 Performance.......................................................... 23 Advanced SQL Features................................................ 23 Extensibility .......................................................... 24 Security .............................................................. 25 Appendices .......................................................... 26 Conventions ............................................................................................................................26 Source Code and Updates.....................................................................................................27 Setting Up........................................................................................................................................28 Overview..................................................................................................................................28 The SQL*PLUS Environment ..............................................................................................28 Setting up AUTOTRACE in SQL*PLUS .................................. 30 C Compilers.............................................................................................................................32 Coding Conventions..............................................................................................................32 Other Issues.............................................................................................................................33 Chapter 1: Developing Successful Oracle Applications.........................................................35 Overview..................................................................................................................................35 My Approach ..........................................................................................................................36 The Black Box Approach ......................................................................................................37 How (and how not) to Develop Database Applications.................................................41 Understanding Oracle Architecture...................................... 41 Understanding Concurrency Control .................................... 48 Database Independence? ............................................... 57 How Do I Make it Run Faster? .......................................... 71 The DBA‐Developer Relationship ....................................... 73 Summary..................................................................................................................................74 Chapter 2: Architecture .................................................................................................................76 Overview..................................................................................................................................76 The Server................................................................................................................................76 The Files...................................................................................................................................84 Parameter Files ....................................................... 84 Data Files ............................................................ 87 Temp Files ........................................................... 91 Control Files.......................................................... 91 Redo Log Files ........................................................ 92 Expert one-on-one Oracle 4 Files Wrap‐Up ........................................................ 96 The Memory Structures ........................................................................................................97 PGA and UGA........................................................ 97 SGA ................................................................ 103 Memory Structures Wrap‐Up .......................................... 115 The Processes ........................................................................................................................115 Server Processes ..................................................... 116 Background Processes ................................................ 122 Slave Processes ...................................................... 130 Summary................................................................................................................................132 Chapter 3: Locking and Concurrency.......................................................................................133 Overview................................................................................................................................133 What are Locks?....................................................................................................................133 Locking Issues ......................................................................................................................136 Lost Updates ........................................................ 136 Blocking ............................................................ 140 Deadlocks ........................................................... 141 Lock Escalation ...................................................... 146 Types of Lock........................................................................................................................147 DML Locks.......................................................... 147 DDL Locks .......................................................... 155 Latches and Internal Locks (Enqueues).................................. 159 Manual Locking and User‐Defined Locks................................ 160 What is Concurrency Control?...........................................................................................161 Transaction Isolation Levels ........................................... 162 READ UNCOMMITTED .............................................. 163 READ COMMITTED ................................................. 165 REPEATABLE READ ................................................. 167 SERIALIZABLE...................................................... 170 Read‐Only Transactions............................................... 172 Summary................................................................................................................................173 Chapter 4: Transactions...............................................................................................................175 Overview................................................................................................................................175 Transaction Control Statements........................................................................................175 Integrity Constraints and Transactions ...........................................................................182 Bad Transaction Habits.......................................................................................................184 Distributed Transactions....................................................................................................191 Redo and Rollback...............................................................................................................194 Summary................................................................................................................................198 Chapter 5: Redo and Rollback ...................................................................................................200 Overview................................................................................................................................200 Redo ........................................................................................................................................200 What Does a COMMIT Do?............................................ 201 Expert one-on-one Oracle 5 What Does a ROLLBACK Do? ......................................... 208 How Much Redo Am I Generating?..................................... 209 Can I Turn Off Redo Log Generation?................................... 221 Cannot Allocate a New Log?........................................... 224 Block Cleanout....................................................... 226 Log Contention ...................................................... 230 Temporary Tables and Redo/Rollback................................... 232 Analyzing Redo...................................................... 235 Rollback .................................................................................................................................236 What Generates the Most/Least Undo? .................................. 236 SET TRANSACTION ................................................. 236 ʹORA‐01555: snapshot too oldʹ ......................................... 237 Summary................................................................................................................................250 Chapter 6: Database Tables........................................................................................................252 Overview................................................................................................................................252 Types of Tables ....................................................................................................................252 Terminology..........................................................................................................................254 High Water Mark .................................................... 254 FREELISTS .......................................................... 255 PCTFREE and PCTUSED.............................................. 258 INITIAL, NEXT, and PCTINCREASE ................................... 265 MINEXTENTS and MAXEXTENTS..................................... 265 LOGGING and NOLOGGING ......................................... 266 INITRANS and MAXTRANS .......................................... 266 Heap Organized Table ........................................................................................................266 Index Organized Tables......................................................................................................271 Index Organized Tables Wrap‐up ....................................................................................286 Index Clustered Tables .......................................................................................................286 Index Clustered Tables Wrap‐up......................................................................................295 Hash Cluster Tables.............................................................................................................295 Hash Clusters Wrap‐up.......................................................................................................306 Nested Tables .......................................................................................................................306 Nested Tables Syntax ................................................. 307 Nested Table Storage ................................................. 317 Nested Tables Wrap‐up ......................................................................................................320 Temporary Tables ................................................................................................................321 Temporary Table Wrap‐up ............................................ 329 Object Tables ........................................................................................................................330 Object Table Wrap‐up ................................................ 339 Summary................................................................................................................................339 Chapter 7: Indexes........................................................................................................................341 Overview................................................................................................................................341 An Overview of Oracle Indexes ........................................................................................342 Expert one-on-one Oracle 6 B*Tree Indexes......................................................................................................................343 Reverse Key Indexes.................................................. 348 Descending Indexes .................................................. 350 When should you use a B*Tree Index? .................................. 351 B*Trees Wrap‐up..................................................................................................................360 Bitmap Indexes .....................................................................................................................361 When Should you use a Bitmap Index?.................................. 362 Bitmap Indexes Wrap‐up....................................................................................................364 Function‐Based Indexes......................................................................................................364 Important Implementation Details...................................... 365 Function‐Based Index Example ........................................ 365 Caveat .............................................................. 375 Function‐Based Index Wrap‐up.........................................................................................376 Application Domain Indexes.............................................................................................376 Application Domain Indexes Wrap‐up............................................................................378 Frequently Asked Questions About Indexes .................................................................378 Do Indexes Work On Views? .......................................... 378 Indexes and Nulls .................................................... 378 Indexes on Foreign Keys .............................................. 382 Why isnʹt my Index Getting Used?...................................... 383 Are my Indexes Being Used?........................................... 389 Myth: Space is Never Reused in an Index................................ 390 Myth: Most Discriminating Elements Should be First ..................... 394 Summary................................................................................................................................398 Chapter 8: Import and Export ....................................................................................................400 Overview................................................................................................................................400 A Quick Example .................................................................................................................400 Why You Might Use IMP and EXP...................................................................................402 Detecting Corruption ................................................. 402 Extracting DDL ...................................................... 403 Cloning Schemas..................................................... 403 Transporting Tablespaces ............................................. 403 Rebuilding Instances.................................................. 403 Copying Data between Platforms....................................... 404 How They Work ...................................................................................................................404 The Options ......................................................... 404 Large Exports........................................................ 409 Subsetting Data ...................................................... 414 Transporting Data.................................................... 415 Getting the DDL ..................................................... 421 Backup and Recovery................................................. 429 IMP/EXP is not a Reorganization Tool (Any More)........................ 429 Importing into Different Structures ..................................... 430 Expert one-on-one Oracle 7 Direct Path Exports................................................... 435 Caveats and Errors ...............................................................................................................436 Cloning ............................................................. 436 Using IMP/EXP Across Versions ....................................... 445 Where did my Indexes go? ............................................ 446 Named versus Default‐Named Constraints .............................. 449 National Language Support (NLS) Issues................................ 453 Tables Spanning Multiple Tablespaces .................................. 455 Summary................................................................................................................................461 Chapter 9: Data Loading .............................................................................................................462 Overview................................................................................................................................462 An Introduction to SQL*LOADER...................................................................................462 How to ... ................................................................................................................................469 Load Delimited Data ................................................. 469 Load Fixed Format Data............................................... 473 Load Dates .......................................................... 476 Load Data Using Sequences and Other Functions......................... 477 Update Existing Rows and Insert New Rows............................. 483 Load Report‐Style Input Data .......................................... 486 Load a File into a LONG RAW or LONG Field ........................... 489 Load Data with Embedded Newlines ................................... 490 Unload Data......................................................... 502 Load LOBs .......................................................... 514 Load VARRAYS/Nested Tables with SQLLDR ........................... 526 Call SQLLDR from a Stored Procedure .................................. 529 Caveats ...................................................................................................................................535 You Cannot Pick a Rollback Segment to Use ............................. 535 TRUNCATE Appears to Work Differently ............................... 535 SQLLDR Defaults to CHAR(255) ....................................... 535 Command Line Overrides Control File .................................. 536 Summary................................................................................................................................536 Chapter 10: Tuning Strategies and Tools ................................................................................537 Overview................................................................................................................................537 Identifying the Problem .....................................................................................................537 My Approach ........................................................................................................................539 Tuning is a Constant thing............................................. 540 Bind Variables and Parsing (Again).................................................................................545 Am I Using Bind Variables? ........................................... 562 Bind Variables and Parsing Wrap‐Up..............................................................................565 SQL_TRACE, TIMED_STATISTICS, and TKPROF.....................................................565 Setting Up Tracing ................................................... 566 Using and Interpreting TKPROF Output ................................ 569 Using and Interpreting Raw Trace Files ................................. 580 Expert one-on-one Oracle 8 SQL_TRACE, TIMED_STATISTICS, and TKPROF Wrap‐Up...................................593 DBMS_PROFILER ...............................................................................................................594 Instrumentation....................................................................................................................594 StatsPack ................................................................................................................................597 Setting up StatsPack .................................................. 597 StatsPack Wrap‐Up................................................... 618 V$ Tables ...............................................................................................................................619 V$EVENT_NAME.................................................... 619 V$FILESTAT and V$TEMPSTAT ....................................... 620 V$LOCK ............................................................ 620 V$MYSTAT ......................................................... 620 V$OPEN_CURSOR................................................... 622 V$PARAMETER ..................................................... 623 V$SESSION ......................................................... 623 V$SESSION_EVENT.................................................. 626 V$SESSION_LONGOPS............................................... 627 V$SESSION_WAIT ................................................... 627 V$SESSTAT ......................................................... 627 V$SESS_IO .......................................................... 627 V$SQL, V$SQLAREA ................................................. 627 V$STATNAME ...................................................... 628 V$SYSSTAT ......................................................... 628 V$SYSTEM_EVENT .................................................. 628 Summary................................................................................................................................628 Chapter 11: Optimizer Plan Stability .......................................................................................630 Overview................................................................................................................................630 An Overview of the Feature...............................................................................................630 Uses of Optimizer Plan Stability ......................................................................................634 A Method to Implement Tuning........................................ 634 A Development Tool ................................................. 640 To See the Indexes Used............................................... 642 To See what SQL is Executed by an Application .......................... 642 How Optimizer Plan Stability Works..............................................................................643 OUTLINES and OUTLINE_HINTS ..................................... 643 Creating Stored Outlines....................................................................................................646 Privileges Needed for Stored Outlines .................................. 646 Using DDL .......................................................... 647 Using ALTER SESSION ............................................... 648 The OUTLN User .................................................................................................................649 Moving Outlines from Database to Database................................................................650 Getting Just the Right Outline ..........................................................................................651 Managing Outlines..............................................................................................................654 Via DDL ............................................................ 654 Expert one-on-one Oracle 9 The OUTLN_PKG Package ............................................ 657 Caveats ...................................................................................................................................661 Outline Names and Case .............................................. 661 ALTER SESSION Issue................................................ 663 DROP USER does not Drop Outlines.................................... 663 ʹCURSOR_SHARING = FORCEʹ and Outlines ............................ 664 Outlines Use Simple Text Matching..................................... 665 Outlines by Default are in the SYSTEM Tablespace ....................... 666 OR‐Expansion ....................................................... 666 Performance......................................................... 667 The Namespace of Outlines is Global ................................... 672 Errors you Might Encounter...............................................................................................673 ORA‐18001 ʺno options specified for ALTER OUTLINEʺ .................. 673 ORA‐18002 ʺthe specified outline does not existʺ ......................... 674 ORA‐18003 ʺan outline already exists with this signatureʺ ................. 674 ORA‐18004 ʺoutline already existsʺ ..................................... 674 ORA‐18005‐18007 .................................................... 674 Summary................................................................................................................................675 Chapter 12: Analytic Functions .................................................................................................676 Overview................................................................................................................................676 An Example ...........................................................................................................................676 How Analytic Functions Work..........................................................................................681 The Syntax .......................................................... 681 The Functions........................................................ 698 Examples ................................................................................................................................702 The TOP‐N Query.................................................... 702 Pivot Query ......................................................... 714 Accessing Rows Around Your Current Row ............................. 723 Caveats ...................................................................................................................................728 PL/SQL and Analytic functions ........................................ 728 Analytic Functions in the Where Clause ................................. 730 NULLS and Sorting................................................... 731 Performance......................................................... 733 Summary................................................................................................................................734 Chapter 13: Materialized Views ................................................................................................735 Overview................................................................................................................................735 A Brief History......................................................................................................................735 What youʹll need to run the Examples .............................................................................737 An Example ...........................................................................................................................737 Uses of Materialized Views ...............................................................................................745 How Materialized Views Work.........................................................................................745 Setting Up........................................................... 746 Internal Mechanics ................................................... 747 Expert one-on-one Oracle 10 Making sure your View gets used ....................................................................................750 Constraints .......................................................... 750 Dimensions.......................................................... 756 DBMS_OLAP........................................................................................................................767 Estimating Size ...................................................... 767 Dimension Validation................................................. 769 Recommending Materialized Views .................................... 772 Caveats ...................................................................................................................................774 Materialized Views are Not Designed for OLTP Systems .................. 774 Query Rewrite Integrity............................................... 774 Summary................................................................................................................................775 Chapter 14: Partitioning..............................................................................................................777 Overview................................................................................................................................777 The Uses of Partitioning .....................................................................................................777 Increased Availability................................................. 777 Reduced Administrative Burden ....................................... 780 Enhanced DML and Query Performance ................................ 781 How Partitioning Works.....................................................................................................783 Table Partitioning Schemes ............................................ 784 Partitioning Indexes .................................................. 789 Summary................................................................................................................................813 Chapter 15:Autonomous Transactions.....................................................................................814 Overview................................................................................................................................814 An Example ...........................................................................................................................814 Why Use Autonomous Transactions? ..............................................................................817 Auditing that Can Not be Rolled Back .................................. 817 A Method to Avoid a Mutating Table ................................... 821 Performing DDL in Triggers ........................................... 822 Writing to the Database ............................................... 828 To Develop More Modular Code ....................................... 839 How They Work ...................................................................................................................839 Transactional Control................................................. 840 Scope ............................................................... 842 Ending an Autonomous Transaction .................................... 849 Savepoints .......................................................... 850 Caveats ...................................................................................................................................853 No Distributed Transactions ........................................... 853 PL/SQL Only ........................................................ 853 The Entire Transaction Rolls Back ...................................... 853 Transaction‐Level Temporary Tables ................................... 855 Mutating Tables...................................................... 857 Errors You Might Encounter ..............................................................................................860 ORA‐06519 ʺactive autonomous transaction detected and rolled backʺ....... 860 Expert one-on-one Oracle 11 ORA‐14450 ʺattempt to access a transactional temp table already in useʺ..... 860 ORA‐00060 ʺdeadlock detected while waiting for resourceʺ ................ 861 Summary................................................................................................................................861 Chapter 16: Dynamic SQL ..........................................................................................................862 Overview................................................................................................................................862 Dynamic SQL versus Static SQL.......................................................................................862 Why Use Dynamic SQL? ....................................................................................................865 How to Use Dynamic SQL .................................................................................................866 DBMS_SQL.......................................................... 866 Native Dynamic SQL ................................................. 874 DBMS_SQL versus Native Dynamic SQL ................................ 880 Caveats ...................................................................................................................................906 It Breaks the Dependency Chain........................................ 907 The Code is More Fragile.............................................. 908 It is Harder to Tune................................................... 908 Summary................................................................................................................................908 Chapter 17: interMedia................................................................................................................910 Overview................................................................................................................................910 A Brief History......................................................................................................................910 Uses of interMedia Text......................................................................................................911 Searching for Text .................................................... 912 Managing a Variety of Documents...................................... 914 Indexing Text from Many Data Sources ................................. 915 Itʹs an Oracle Database, After All ....................................... 919 Generating Themes................................................... 920 Searching XML Applications........................................... 922 How interMedia Text Works .............................................................................................923 interMedia Text Indexing ............................................. 927 About ABOUT....................................................... 931 Section Searching .................................................... 932 Caveats ...................................................................................................................................940 It is NOT Document Management ...................................... 940 Index Synchronization ................................................ 940 Indexing Information Outside the Database.............................. 941 Document Services ................................................... 942 The Catalog Index .................................................... 943 Errors You May Encounter .................................................................................................945 Index Out of Date .................................................... 946 External Procedure Errors ............................................. 946 The Road Ahead...................................................................................................................947 Summary................................................................................................................................947 Chapter 18: C‐Based External Procedures................................................................................949 Overview................................................................................................................................949 Expert one-on-one Oracle 12 When Are They Used?.........................................................................................................949 How Are They Implemented? ...........................................................................................951 Configuring Your Server ....................................................................................................953 Verify the extproc Program ............................................ 956 Verify the Database Environment ...................................... 956 Verify the Listener.................................................... 958 The First Test.........................................................................................................................959 Compile extproc.c Code............................................... 959 Set Up the SCOTT/TIGER Account ..................................... 960 Create the demolib Library ............................................ 961 Installing and Running................................................ 962 Our First External Procedure..............................................................................................963 The Wrapper ........................................................ 964 The C Code.......................................................... 976 Building the extproc ................................................. 1004 Installing and Running............................................... 1008 LOB to File External Procedure (LOB_IO) ....................................................................1009 The LOB_IO Call Specification ........................................ 1010 The LOB_IO Pro*C Code ............................................. 1012 Building the extproc ................................................. 1017 Installing and Using LOB_IO ......................................... 1019 Errors You May Encounter ...............................................................................................1025 ORA‐28575 ʺunable to open RPC connection to external procedure agentʺ .. 1025 ORA‐28576 ʺlost RPC connection to external procedure agentʺ ............ 1026 ORA‐28577 ʺargument %s of external procedure %s has unsupported datatype  %sʺ................................................................ 1027 ORA‐28578 ʺprotocol error during callback from an external procedureʺ.... 1027 ORA‐28579 ʺnetwork error during callback from external procedure agentʺ . 1028 ORA‐28580 ʺrecursive external procedures are not supportedʺ ............ 1028 ORA‐28582 ʺa direct connection to this agent is not allowedʺ .............. 1029 ORA‐06520 ʺPL/SQL: Error loading external libraryʺ ..................... 1029 ORA‐06521 ʺPL/SQL: Error mapping functionʺ .......................... 1030 ORA‐06523 ʺMaximum number of arguments exceededʺ ................. 1031 ORA‐06525 ʺLength Mismatch for CHAR or RAW dataʺ.................. 1032 ORA‐06526 ʺUnable to load PL/SQL libraryʺ ............................ 1032 ORA‐06527 ʺExternal procedure SQLLIB error: %sʺ ...................... 1033 Summary..............................................................................................................................1033 Chapter 19: Java Stored Procedures........................................................................................1035 Overview..............................................................................................................................1035 Why Use Java Stored Procedures? ..................................................................................1035 How They Work .................................................................................................................1037 Passing Data........................................................ 1042 Useful Examples .................................................... 1053 Expert one-on-one Oracle 13 Possible Errors ....................................................................................................................1061 ORA‐29549 Java Session State Cleared ................................. 1061 Permissions Errors .................................................. 1061 ORA‐29531 no method X in class Y .................................... 1062 Summary..............................................................................................................................1063 Chapter 20: Using Object Relational Features......................................................................1064 Overview..............................................................................................................................1064 Reasons for Using These Features ..................................................................................1065 How Object Relational Features Work ..........................................................................1065 Adding Data Types to your System................................................................................1066 Adding Data Types Wrap‐Up..........................................................................................1082 Using Types to Extend PL/SQL .......................................................................................1083 Creating a New PL/SQL Data Type .................................... 1083 Unique Uses for Collections .......................................... 1095 Using Types to Extend PL/SQL Wrap‐Up .....................................................................1101 Object Relational Views...................................................................................................1102 The Types .......................................................... 1102 The O‐R View ...................................................... 1103 Summary..............................................................................................................................1118 Chapter 21: Fine Grained Access Control..............................................................................1120 Overview..............................................................................................................................1120 An Example .........................................................................................................................1120 Why Use this Feature?.......................................................................................................1121 Ease of Maintenance ................................................. 1121 Performed in the Server .............................................. 1122 Avoids Shared User Accounts ........................................ 1124 Supports Shared User Accounts ....................................... 1124 Hosting an Application as an ASP ..................................... 1124 How it Works ......................................................................................................................1125 Example 1: Implementing a Security Policy ............................. 1127 Example 2: Using Application Contexts ................................ 1132 Caveats .................................................................................................................................1154 Referential Integrity ................................................. 1154 Cursor Caching ..................................................... 1160 Export/Import ...................................................... 1168 Debugging ......................................................... 1172 Errors You Might Encounter ............................................................................................1173 ORA‐28110: policy function or package  has error. ........ 1173 ORA‐28112: failed to execute policy function. ........................... 1175 ORA‐28113: policy predicate has error.................................. 1176 ORA‐28106: input value for argument #2 is not valid. .................... 1178 Summary..............................................................................................................................1178 Chapter 22: n‐Tier Authentication ..........................................................................................1180 Expert one-on-one Oracle 14 Overview..............................................................................................................................1180 Why Use n‐Tier Authentication? ....................................................................................1180 The Mechanics of n‐Tier Authentication ......................................................................1183 Granting the Privilege ............................................... 1194 Auditing Proxy Accounts..................................................................................................1195 Caveats .................................................................................................................................1196 Summary..............................................................................................................................1198 Chapter 23: Invoker and Definer Rights ...............................................................................1199 Overview..............................................................................................................................1199 An Example .........................................................................................................................1199 When to Use Invoker Rights............................................................................................1203 Developing Generic Utilities .......................................... 1203 Data Dictionary Applications ......................................... 1208 Generic Object Types ................................................ 1211 Implementing your own Access Control................................ 1212 When to Use Definer Rights ............................................................................................1215 Performance and Scalability .......................................... 1215 Security ............................................................ 1216 How they Work...................................................................................................................1217 Definer Rights ...................................................... 1217 Definer Rights and Roles ............................................. 1221 Invoker Rights ...................................................... 1222 Caveats .................................................................................................................................1235 Invoker Rights and Shared Pool Utilization ............................. 1235 Performance........................................................ 1239 Code must be more Robust in Handling Errors.......................... 1242 Side Effects of Using SELECT *........................................ 1244 Beware of the ʹHiddenʹ Columns ...................................... 1245 Java and Invoker Rights.............................................. 1247 Errors You Might Encounter ............................................................................................1254 Summary..............................................................................................................................1254 Appendix AA: Necessary Supplied Packages ......................................................................1256 Overview..............................................................................................................................1256 Why Use the Supplied Packages?...................................................................................1257 About The Supplied Packages ........................................................................................1257 Appendix AB: DBMS_ALERT and DBMS_PIPE.................................................................1260 Overview..............................................................................................................................1260 Why You Might Use Them ........................................... 1260 Set Up ............................................................. 1261 DBMS_ALERT....................................................................................................................1261 Concurrent Signals by More than One Session .......................... 1264 Repeated Calls to Signal by a Session .................................. 1266 Many Calls to Signal by Many Sessions before a Wait Routine is Called .... 1267 Expert one-on-one Oracle 15 Summary..............................................................................................................................1268 DBMS_PIPE ........................................................................................................................1268 Pipe Servers versus External Routines.................................. 1271 Online Example..................................................... 1273 Summary..............................................................................................................................1273 Appendix AC: DBMS_APPLICATION_INFO.....................................................................1274 Overview..............................................................................................................................1274 Using the Client Info.........................................................................................................1275 Using V$SESSION_LONGOPS......................................................................................1278 Summary..............................................................................................................................1283 Appendix AD: DBMS_JAVA...................................................................................................1284 Overview..............................................................................................................................1284 LONGNAME and SHORTNAME..................................................................................1284 Setting Compiler Options ................................................................................................1285 SET_OUTPUT.....................................................................................................................1289 loadjava and dropjava.......................................................................................................1290 Permission Procedures......................................................................................................1291 Summary..............................................................................................................................1293 Appendix AE: DBMS_JOB.......................................................................................................1294 Overview..............................................................................................................................1294 Running a Job Once...........................................................................................................1298 Ongoing Jobs ......................................................................................................................1302 Custom Scheduling............................................................................................................1305 Monitoring the Jobs and Finding the Errors.................................................................1307 Summary..............................................................................................................................1310 Appendix AF: DBMS_LOB ......................................................................................................1311 Overview..............................................................................................................................1311 How do I Load LOBs? .......................................................................................................1312 substr ....................................................................................................................................1312 SELECT FOR UPDATE and Java ...................................... 1313 Conversions.........................................................................................................................1316 From BLOB to VARCHAR2 and Back Again ............................ 1316 Converting From LONG/LONG RAW to a LOB ......................... 1321 Performing a Mass One‐Time Conversion Illustration .................... 1323 Performing an ʹon the flyʹ Conversion.................................. 1327 How to Write a BLOB/CLOB to Disk.............................................................................1330 Displaying a LOB on the Web Using PL/SQL..............................................................1331 Summary..............................................................................................................................1333 Appendix AG: DBMS_LOCK..................................................................................................1334 Overview..............................................................................................................................1334 Summary..............................................................................................................................1338 Appendix AH: DBMS_LOGMNR ..........................................................................................1339 Overview..............................................................................................................................1339 Expert one-on-one Oracle 16 Overview..............................................................................................................................1341 Step 1: Creating the Data Dictionary ................................... 1341 Step 2: Using Log Miner.............................................. 1345 Options and Usage.............................................................................................................1352 Using Log Miner to Find Out When...............................................................................1355 PGA Usage...........................................................................................................................1357 Log Miner Limits................................................................................................................1359 Oracle Object Types ................................................. 1359 Chained or Migrated Rows ........................................... 1363 Other limits ........................................................ 1366 V$LOGMNR_CONTENTS ..............................................................................................1367 Summary..............................................................................................................................1370 Appendix AI: DBMS_OBFUSCATION_TOOLKIT............................................................1372 Overview..............................................................................................................................1372 The Wrapper .......................................................................................................................1374 Caveats .................................................................................................................................1392 Key Management ...............................................................................................................1394 The Client Application Manages and Stores Keys........................ 1394 Store the Keys in the Same Database ................................... 1395 Store the Keys in the File System with the Database...................... 1396 Summary..............................................................................................................................1397 Appendix AJ: DBMS_OUTPUT..............................................................................................1398 Overview..............................................................................................................................1398 How DBMS_OUTPUT Works.........................................................................................1399 DBMS_OUTPUT and Other Environments..................................................................1404 Getting Around the Limits...............................................................................................1409 Using A Small Wrapper Function or Another Package ................... 1409 Creating DBMS_OUTPUT Functionality................................ 1411 Summary..............................................................................................................................1418 Appendix AK: DBMS_PROFILER..........................................................................................1419 Overview..............................................................................................................................1419 Caveats .................................................................................................................................1431 Summary..............................................................................................................................1432 Appendix AL: DBMS_UTILITY..............................................................................................1433 Overview..............................................................................................................................1433 COMPILE_SCHEMA ........................................................................................................1433 ANALYZE_SCHEMA........................................................................................................1438 ANALYZE_SCHEMA with a Changing Schema......................... 1440 ANALYZE_SCHEMA does not Analyze Everything ..................... 1440 ANALYZE_DATABASE ...................................................................................................1442 FORMAT_ERROR_STACK.............................................................................................1442 FORMAT_CALL_STACK ................................................................................................1444 GET_TIME...........................................................................................................................1448 Expert one-on-one Oracle 17 GET_PARAMETER_VALUE ...........................................................................................1449 NAME_RESOLVE..............................................................................................................1450 NAME_TOKENIZE ...........................................................................................................1453 COMMA_TO_TABLE, TABLE_TO_COMMA ............................................................1457 DB_VERSION and PORT_STRING ..............................................................................1459 GET_HASH_VALUE.........................................................................................................1460 Summary..............................................................................................................................1466 Appendix AM: UTL_FILE ........................................................................................................1467 Overview..............................................................................................................................1467 The UTL_FILE_DIR init.ora parameter .........................................................................1467 Accessing Mapped Windows Drives .............................................................................1469 Handling Exceptions .........................................................................................................1471 Dumping a Web Page to Disk .........................................................................................1472 1023 Byte Limit ...................................................................................................................1473 Reading A Directory..........................................................................................................1474 Summary..............................................................................................................................1476 Appendix AN: UTL_HTTP.......................................................................................................1477 Overview..............................................................................................................................1477 UTL_HTTP Functionality.................................................................................................1477 Adding SSL to UTL_HTTP ..............................................................................................1480 Really Using UTL_HTTP..................................................................................................1487 A Better UTL_HTTP ..........................................................................................................1490 Summary..............................................................................................................................1501 Appendix AO: UTL_RAW........................................................................................................1503 Overview..............................................................................................................................1503 Appendix AP: UTL_SMTP and Sending Mail .....................................................................1506 Overview..............................................................................................................................1506 UTL_SMTP ‐ a larger example.........................................................................................1506 Loading and using the JavaMail API .............................................................................1512 Summary..............................................................................................................................1521 Appendix AQ: UTL_TCP..........................................................................................................1522 Overview..............................................................................................................................1522 The SocketType..................................................................................................................1523 Summary..............................................................................................................................1537 Appendix B: Support, Errata and p2p.wrox.com..................................................................1539 Overview..............................................................................................................................1539 The Online Forums at p2p.wrox.com.............................................................................1539 How To Enroll For Support........................................... 1539 Why This System Offers The Best Support .............................. 1540 Checking the Errata Online at http://www.wrox.com/ ...............................................1541 Finding an Erratum on the Web Site ................................... 1541 Add an Erratum .................................................... 1541 How to Tell Us Exactly What You Think ......................................................................1543 Expert one-on-one Oracle 18 Introduction  The inspiration for the material contained in this book comes from my experiences  developing Oracle software and working with fellow Oracle developers, helping them  build reliable and robust applications based on the Oracle database. The book is basically a  reflection of what I do everyday and of the issues I see people encountering each and  every day.  I covered what I felt was most relevant ‐ namely the Oracle database and its architecture. I  could have written a similarly‐titled book explaining how to develop an application using  a specific language and architecture ‐ for example, one using Java Server Pages that speak  to Enterprise Java Beans, that use JDBC to communicate with Oracle. However, at the end  of the day, you really do need to understand the topics covered here in order to build such  an application successfully. This book deals with what I believe needs to be universally  known to develop successfully with Oracle, whether you are a Visual Basic programmer  using ODBC, a Java programmer using EJBs and JDBC, or a Perl programmer using DBI  Perl. This book does not promote any specific application architecture; it does not compare  3‐tier to client‐server. Rather, it covers what the database can do and what you must  understand about the way it works. Since the database is at the heart of any application  architecture, the book should have a broad audience.  What this Book is About  One of the problems with having plenty of development options is in trying to figure out  which one might be the best choice for your particular needs. Everyone wants as much  flexibility as possible (as many choices as they can possibly have) but they also want  things to be very cut and dry; in other words, easy. Oracle presents the developer with  almost unlimited choice. No one ever says ʹyou canʹt do that in Oracleʹ‐ they say ʹhow  many different ways would you like to do that in Oracle?ʹ I hope that this book will help  you make the correct choice.  It is a book for those people who appreciate the choice but would also like some guidelines  and practical implementation details on Oracle features and functions. For example,  Oracle has a really neat feature called the virtual private database. Oracle documentation  tells you how to use this feature and what it does. Oracle documentation does not,  however, tell you when you should use this feature and, perhaps even more importantly,  when you should not use this feature. It does not always tell you the implementation details  of this feature, and if youʹre not aware of them, this can really come back to haunt you (Iʹm  not referring to bugs, but really the way it is supposed to work and what the feature was  really designed to do).  Expert one-on-one Oracle 19 Who Should Use this Book?  The target audience for this book is anyone who develops applications with Oracle as the  database backend. It is a book for professional Oracle developers who need to know how  to get things done in the database. The practical nature of the book means that many  sections should also be very interesting to the DBA. Most of the examples in the book use  SQL*PLUS to demonstrate the key features, so you wonʹt find out how to develop a really  cool GUI ‐ but you will find out how the Oracle database works, what its key features can  do and when they should (and should not) be used.  It is a book for anyone who wants to get more out of Oracle with less work. It is for anyone  who wants to see new ways to use existing features. It is for anyone who wants to see how  these features can be applied in the real world (not just examples of how to use the feature  but why the feature is relevant in the first place). Another category of people that would  find this book of interest would be the technical manager in charge of the developers who  work on Oracle projects. In some respects, it is just as important that they understand why  knowing the database is crucial to success. This book can provide ammunition for the  manager who would like to get their personnel trained in the correct technologies, or in  ensuring that they already know what they need to know.  In order to get the most out of this book, the reader should have:  • Knowledge of SQL.  You donʹt have to be the best SQL coder ever, but a good  working knowledge would help.   • An understanding of PL/SQL.  This is not a pre‐requisite but will help you to  ʹabsorbʹ the examples. This book will not, for example, teach you how to program a  FOR loop or declare a record type ‐ the Oracle documentation and numerous books  cover this well. However, thatʹs not to say that you wonʹt learn a lot about PL/SQL  by reading this book. You will. Youʹll become very intimate with many features of  PL/SQL and youʹll see new ways to do things, become aware of packages/features  that perhaps you did not know existed.   • Exposure to some 3GL language such as C or Java.  I believe that anyone who can  read and write code in a 3GL language will be able to successfully read and  understand the examples in this book.   • Familiarity with the Oracle Server Concepts Manual.   A few words on that last point: due to its vast size, many people find the Oracle  documentation set to be somewhat intimidating. If you are just starting out or havenʹt read  any of it as yet, I can tell you that the Oracle8i Concepts manual is exactly the right place to  start. It is about 800 pages long and touches on many of the major Oracle concepts that  you need to know about. It may not give you each, and every technical detail (this is what  the other 10,000 to 20,000 pages of documentation are for) but it will educate you on all of  the important concepts. This manual touches the following topics (to name a few):  Expert one-on-one Oracle 20 • The structures in the database, how data is organized and stored.   • Distributed processing.   • Oracleʹs memory architecture.   • Oracleʹs process architecture.   • Schema objects you will be using (tables, indexes, clusters, and so on).   • Built‐in data types and user‐defined data types.   • SQL‐stored procedures.   • How transactions work.   • The optimizer.   • Data integrity.   • Concurrency control.   I will come back to these topics myself time and time again. These are the fundamentals ‐  without knowledge of them, your Oracle applications will be prone to failure. I encourage  you to read through the manual and get an understanding of some of these topics.  How This Book is Structured  To help you use this book, it is organized into six discrete sections (described below).  These are not rigid divisions, but they will help you navigate quickly to the area you need  most. This book has 23 chapters and each is like a ʹmini‐bookʹ‐ a virtually standalone  component. Occasionally, I refer to examples or features in other chapters (the Security  section, in particular, relies a little more on examples and concepts that are built up over  several chapters) but you could pretty much pick a chapter out of the book and read it  standalone. You will not have to read Chapter 10 to understand or make use of Chapter 14,  for example.  The format and style of each chapter is virtually identical:  • An introduction to the feature or capability.   • Why you might want to use it (or not). I outline the times you would consider using  this feature and when you would not want to use it.   • How to use this feature. Not just a copy of the SQL reference here but rather step by  step ‐ here is what you need, here is what you have to do, these are the switches  you need to go through to get started. Things covered in this section will be:  How to implement it  Examples, examples, and examples  Debugging this feature  Caveats of using this feature  Expert one-on-one Oracle 21 Handling errors (proactively)   • A summary to bring it all together.   There will be lots of examples, and lots of code, all of which will be available for download  at http://www.apress.com/ Following is a detailed breakdown of the content of each  section:  Understanding the Database  • Chapter 1, Developing Successful Oracle Applications.  This chapter sets out my  essential approach to database programming. All databases are not created equal  and in order to develop database‐driven applications successfully and on time, you  need to understand exactly what your particular database can do and how it does it.  If you do not know what your database can do, you run the risk of continually ʹre‐ inventing the wheelʹ ‐ developing functionality that the database already provides.  If you do not know how your database works you are likely to develop applications  that perform poorly and do not behave in a predictable manner.  The chapter takes an empirical look at some applications where a lack of basic  understanding of the database has lead to project failure. With this example‐driven  approach, the chapter discusses the basic features and functions of the database that  you, the developer, need to understand. The bottom line is that you cannot afford to  treat the database as a black box that will simply ʹchurn out the answersʹ and take  care of scalability and performance by itself.   • Chapter 2, Architecture.  The Oracle database is a highly complex tool. Every time  you connect to a database or issue an UPDATE command, a whole host of  processes occur in the background to make sure that youʹre application runs  smoothly and that data integrity is maintained. For example, the database ensures  that it has enough information to restore the data to its original state should it need  to. It will cache program data and automatically re‐use it where appropriate. And  so on. Most of the time all of this is occurs transparently (to the developer, at least)  but when problems occur, half the battle is knowing where to look to fix them.  This chapter covers the three major components of the Oracle architecture ‐ its  memory structures (specifically, the System Global Area), its physical processes,  and its set of files (parameter files, redo log files...). Understanding the Oracle  architecture is fundamental to understanding the unique way in which Oracle  implements certain features and how this will affect your application.   • Chapter 3, Locking and Concurrency.  Different databases have different ways of  doing things (what works well in SQL Server may not work as well in Oracle) and  Expert one-on-one Oracle 22 understanding how Oracle implements locking and concurrency control is  absolutely vital to the success of your application.  This chapter discussed Oracleʹs basic approach to these issues, the types of locks  that can be applied (DML, DDL, latches...) and the problems that can arise if locking  is not implemented carefully (deadlocking, blocking and escalation). The  concurrency control section discusses the functions provided by Oracle that allow  us to control how users can access and modify the database.   • Chapter 4, Transactions.  Transactions are a fundamental feature of all databases ‐  they are part of what distinguishes a database from a file system. And yet, they are  often misunderstood and many developers do not even know that they are  accidentally not using them. This chapter examines how transactions should be  used in Oracle and also exposes some ʹbad habitsʹ that have been picked up when  developing with other databases. In particular, we look at the implications of  atomicity and how it affects statements in Oracle. We then move on to discuss  transaction control statements (COMMIT, SAVEPOINT, ROLLBACK), integrity  constraints and distributed transactions (the two‐phase commit). Finally, we look at  some real world issues surrounding use of transactions ‐ how they are logged, and  the role of redo and undo.   Database Structures and Utilities  • Chapter 5, Redo and Rollback.  It can be said that the developer does not need to  understand the detail of redo and rollback as much as the DBA, but developers do  need to know the roles they play in the database. After first defining redo, we  examine what exactly a COMMIT does. We also consider issues such as how much  redo is being generated, turning off logging and also analyzing redo.  In the rollback section of the chapter we first look what generates the most and least  undo, before looking at the set transaction SQL statement. This is generally used to  pick a large rollback section for some very large operation. Then we focus on the  infamous ʹORA‐01555 snapshot too oldʹ error, looking at causes and solutions.   • Chapter 6, Tables.  Oracle now supports numerous types of table. This chapter  looks at each different type ‐ heap organized (the default, ʹnormalʹ table), index  organized, index clustered, hash clustered, nested, temporary, and object ‐ and  discusses when, how, and why you should use them. Most of time the heap‐ organized table is sufficient, but you should be able to recognize when one of the  other types might be more appropriate.   • Chapter 7, Indexes.  Indexes are a crucial aspect of your application design. Correct  implementation requires an in‐depth knowledge of the data, how it is distributed,  Expert one-on-one Oracle 23 how it will be used. Too often, indexes are treated as an afterthought in application  development, and performance suffers as a consequence.  This chapter we look in detail at the different types of indexes, including B*Tree,  bitmap, function‐based, and application domain indexes, and discuss where they  should and should not be used. Weʹll also answer some of those common queries in  the Frequently Answered Questions section, such as ʹDo indexes work on views?ʹ and  ʹWhy isnʹt my index getting used?   • Chapter 8, Import and Export.  Import and export are two of the oldest tools  supplied with Oracle, used to extract tables, schemas or entire database definitions  from one Oracle instance to be imported into another instance or schema, yet many  developers do not how to use them properly. We cover topics such as large exports,  sub‐setting and transporting data, and using them as backup or reorganization  tools. The chapter finishes by highlighting some of the potential pitfalls and  problems in the use of these tools.   • Chapter 9, Data Loading.  This chapter focuses on SQLLDR and covers the various  ways in which we can use this tool to load and modify data in the database. Issues  covered include loading delimited data, updating existing rows and inserting new  ones, unloading data, and how to call SQLLDR from a stored procedure. Again,  SQLLDR it is a well‐established and crucial tool but is the source of many questions  with regard to its practical use.   Performance  • Chapter 10, Tuning Strategies and Tools.  This is one of my ʹspecialist topicsʹ and  here I detail my approach to tuning Oracle applications and then embark on a  highly practical guide to the tools and techniques that I use. The opening section  concentrates on application tuning, covering topics such as bind variables and  parsing, SQL_TRACE, TIMED_STATISTICS and TKPROF, the DBMS_PROFILER,  and the importance of logging in your applications. With the application fully  tuned, attention turns to the database and specifically to the StatsPack group of  utilities and the V$ tables you will use in tuning efforts.   • Chapter 11, Optimizer Plan Stability.  Developers using Oracle 8i (and later) now  have the ability to save a set of ʹhints to the serverʹ, known as an optimizer plan,  detailing how best to execute a specific SQL statement in the database. This has  obvious performance benefits and we take a detailed look at how you can generate  these outlines and how to manage them.   Advanced SQL Features  • Chapter 12, Analytic Functions.  Certain questions are asked of the database very  regularly, but the queries that can answer them are difficult to write in straight SQL  Expert one-on-one Oracle 24 (and will not always perform quickly, anyway). Oracle 8.1.6 introduced analytic  functions. These functions add extensions to the SQL language that make such  queries easier to code, and dramatically increase performance over the equivalent  straight SQL query. This chapter deals with the way in which analytical functions  work, looking at the full syntax (including the function, partition and windowing  clauses) and then gives full, practical examples of how these functions can be used.   • Chapter 13, Materialized Views.  Certain ʹaggregateʹ queries must process  potentially terabytes of data to produce an answer. The performance implications  are clear ‐ especially if it is a common query, meaning that a vast amount of data  has to be processed each and every time the question is asked. With this feature, we  simply do some of the work beforehand ‐ we summarize the data needed to answer  a certain query in a materialized view and future queries are directed at this  summary data. Furthermore, the database can recognize similar queries that make  use of this summary data, and automatically re‐writes the query to allow them to  do so. This chapter discusses how all this works and how to set up materialized  views, including use of constraints, dimensions, and the DBMS_OLAP package.   • Chapter 14, Partitioning.  Partitioning is designed to facilitate the management of  very large tables and indexes, by implementing a ʹdivide‐and‐conquerʹ logic ‐  basically breaking up a table or index into many smaller, and more manageable,  pieces. It is an area where the DBA and developer must work together to maximize  application availability and performance. This chapter covers both table and index  partitioning. We look at partitioning using local indexes (common in data  warehouses) and global indexes (common in OLTP systems).   • Chapter 15, Autonomous Transactions.  With this feature, we can create a sub‐ transaction that can commit or rollback changes independently of its parent  transaction. We look at the situations when this might be desired, such as when  auditing an ʹillegalʹ attempt to modify secure information, to avoid mutating a table  or as a way of performing DDL in triggers. The discussion will span issues such as  transactional control, scope, ending an autonomous transaction, and savepoints.   • Chapter 16, Dynamic SQL.  In this chapter, we compare two methods of using SQL  statements in our programs: ʹnormalʹ static SQL and dynamic SQL. Dynamic SQL is  the SQL executed at run‐time, but was not known at compile time. We will look at  two methods of using dynamic SQL in your programs, namely with the supplied  built‐in package DBMS_SQL and native dynamic SQL, a declarative method for use  with PL/SQL. There are various reasons why you would choose one over the other,  such as whether the bind variables are known at compile time, whether you know  the outputs at compile time, and whether a given statement will be executed once,  or many times in a session and we will explore these issues in detail.   Extensibility  • Chapter 17, interMedia.  This chapter focuses on interMedia Text. Rather than a  detailed ʹhow to use interMedia Textʹ, we will cover what it is and what it provides,  Expert one-on-one Oracle 25 and the features of the database that enable this functionality. We look at how to  search for text, manage a variety of documents, index text from many data sources,  and search XML applications. The chapter finishes with a look at some of the  caveats of interMedia, including the synchronization of indexes and indexing  information outside of the database.   • Chapter 18, C‐Based External Procedures.  With Oracle 8.0 came the ability to  implement procedures in the database server that were written in languages other  than PL/SQL‐ for example, C or Java. These are referred to as external procedures. In  this chapter, we will cover C‐based procedures from an architectural perspective.  We see how to configure your server to use these procedures, test the installation,  and create an example procedure for passing and manipulating various types of  variables. We also examine the LOB to File (LOB_IO) external procedure, which  writes the CLOBs, BLOBs, and BFILEs to disk.   • Chapter 19, Java Stored Procedures.  With judicious use of small amounts of Java,  we can achieve a great deal of useful functionality that is beyond the reach of  PL/SQL. In this chapter, we look at practical examples of where this ability is useful  ‐ such as when getting a directory listing or running an operating system command.  Again, we round off the chapter with some of the errors that you may encounter  when you try to use this feature, and some possible solutions.   • Chapter 20, Using Object Relational Features.  The availability of object‐relational  features in the database (with Oracle 8i onwards) greatly extends the set of data  types available to the developer ‐ but when should they be used (and, equally,  when shouldnʹt they be used)? In this chapter, we show how to add new data types  to your system (we create a new PL/SQL data type) and look at the unique uses for  collections. Finally we look at object relational views, which are for those of you  who want to work with object relational features but still present a relational view  of the data to the application.   Security  • Chapter 21, Fine Grained Access Control.  This feature allows you to attach a  predicate, at run‐time, to all queries issued to a database. The fact that this feature is  implemented on the server means that any application that can access the database  can use the feature. Further reasons to use this feature include ease of maintenance  and the ability to host an application as an ASP. You will also see how it works by  testing a couple of examples, one based on implementing a security policy and one  using application contexts. The chapter is rounded off with a section on caveats,  which include referential integrity, import and export issues, and a section on  errors.   • Chapter 22, n‐Tier Authentication.  In this chapter we will discuss the effects of the  Web, which gives rise to situations where your client presents their credentials to a  middle‐tier application server, before actually accessing your database. We will see  Expert one-on-one Oracle 26 how this feature can be implemented and how it works. We will look at how you  can grant privileges and audit proxy accounts.   • Chapter 23, Invoker and Definer Rights.  Starting with Oracle 8i, we can now  grant a different set of privileges to different users of a stored procedure. With  invoker rights, we can now develop a stored procedure that executes with the  privilege set of the invoker at run‐time We examine why this feature might be  useful, such as in the development of generic utilities and data dictionary  applications and why, in the majority of cases, definer rights is still the correct  choice. In the ʹhow it worksʹ section, we look at what exactly happens when we  compile definers and invokers rights procedures.   Appendices  • Appendix A, Necessary Supplied Packages.  Many of these packages are  overlooked in development efforts ‐ or their intention not truly understood. Here I  try to make sense of them, show you how to use them and extend them.   Conventions  We have used a number of different styles of text and layout in this book to help you  differentiate between the different kinds of information. Here are examples of the styles  we use and an explanation of what they mean:  Code has several fonts. If itʹs a word that weʹre talking about in the text, for example when  discussing a PL/SQL SELECT query, itʹs in this font. If itʹs a block of code that you can type  as a program and run, then it is in a gray box:   tkyte@DEV816> create or replace procedure StaticEmpProc(p_job in varchar2)    2  as    3    begin    4      for x in (select ename from emp where job = p_job)    5        loop    6          dbms_output.put_line( x.ename );    7        end loop;    8  end;    9  /        Procedure created.  In this book we have also shown line numbers directly from the SQL*PLUS session, for  ease of reference.   Note Advice, hints, and background information comes in this type of font. Expert one-on-one Oracle 27 Important  Important pieces of information come in boxes like this. Bullets appear indented, with each new bullet marked as follows:  • Important Words are in a bold type font   • Words that appear on the screen in menus like File or Window, are in a similar font  to that you would see on a Windows desktop   • Keys that you press on the keyboard like Ctrl and Enter, are in italics   Source Code and Updates  As you work through the examples in this book, you may decide that you prefer to type in  all the code by hand. Many readers prefer this because it is a good way to get familiar with  the coding techniques that are being used.  Whether you want to type the code in or not, we have made all the source code for this  book available at our web site at the following address:  http://www.apress.com/   If youʹre one of those readers who likes to type in the code, you can use our files to check  the results you should be getting they should be your first stop if you think you might  have typed in an error. If youʹre one of those readers who does not like typing, then  downloading the source code from our web site is a must!   Also, errata sheets are available for all our books at http://www.apress.com/ on each  bookʹs page. If you find an error that hasnʹt already been reported, please let us know.  Expert one-on-one Oracle 28 Setting Up  Overview  In this section I will describe how to set up an environment capable of executing the  examples in this book. I will cover:  • How to set up the SCOTT/TIGER demonstration schema   • The environment you need to have up and running   • How to configure AUTOTRACE, a SQL*PLUS facility   • The C compiler set up   • The coding conventions I use in this book   The SQL*PLUS Environment  Most of the examples in this book are designed to run 100 percent in the SQL*PLUS  environment. The notable exceptions are the C‐based examples where, of course, you  require a C compiler, separate from Oracle (see the C Compilers section a little later). Other  than that, SQL*PLUS is the only thing that you need to set up and configure. SQL*PLUS  provides many useful options and commands that weʹll make fequent use of throughout  this book. For example, almost all of the examples in this book use DBMS_OUTPUT in  some fashion. In order for DBMS_OUTPUT to work, the following SQL*PLUS command  must be issued:   SQL> set serveroutput on  If you are like me, you will soon get tired of typing this in each and every time.  Fortunately, SQL*PLUS allows us to set up a login.sql file, a script that is executed each  and every time we start a SQL*PLUS session. Further, it allows us to set an environment  variable, SQLPATH, so that it can find this start‐up script, regardless of the directory in  which it is stored.  The login.sql script that I used for all examples in this book is as follows:   define _editor=vi        set serveroutput on size 1000000        set trimspool on  set long 5000  set linesize 100  set pagesize 9999  Expert one-on-one Oracle 29 column plan_plus_exp format a80        column global_name new_value gname  set termout off  select lower(user) || ʹ@ʹ ||  decode(global_name, ʹORACLE8.WORLDʹ, ʹ8.0ʹ, ʹORA8I.WORLDʹ,  ʹ8iʹ, global_name ) global_name from global_name;  set sqlprompt ʹ&gname> ʹ  set termout on  Where:  • DEFINE EDITOR=VI sets the default editor for SQL*PLUS. You may set this to be  your favorite text editor (not a word processor) such as Notepad or EMACs.   • SET SERVEROUTPUT ON SIZE 1000000 enables DBMS_OUTPUT to be on by  default (hence we donʹt have to type it in each and every time). Also set the default  buffer size to be as large as possible.   • SET TRIMSPOOL ON ensures that, when spooling text, lines will be blank‐trimmed  and not fixed width. If this is set to off (the default setting), spooled lines will be as  wide as your linesize setting   • SET LONG 5000 sets the default number of bytes displayed when selecting LONG  and CLOB columns.   • SET LINESIZE 100 sets the width of the lines displayed by SQL*PLUS to be 100  characters.   • SET PAGESIZE 9999 sets the pagesize, which controls how frequently SQL*PLUS  prints out headings, to a big number (we get one set of headings per page).   • COLUMN PLAN_PLUS_EXP FORMAT A80 sets the default width of the EXPLAIN  PLAN output we receive with AUTOTRACE. A width of a80 is generally enough to  hold the full plan.   The next section of the login.sql sets up my SQL*PLUS prompt, starting with the line:   column global_name new_value gname  This directive tells SQL*PLUS to take the last value it retrieves for any column named  GLOBAL_NAME, and place it into the substitution variable GNAME. We then have the  following query:   select lower(user) || ʹ@ʹ ||  decode(global_name, ʹORACLE8.WORLDʹ, ʹ8.0ʹ, ʹORA8I.WORLDʹ,  ʹ8iʹ, global_name ) global_name from global_name;  This selects the GLOBAL_NAME from the database, using the DECODE function to assign  familiar names to some of my more common database instances, and concatenates it with  Expert one-on-one Oracle 30 the username with which I am currently logged in. Finally, we reflect this information in  the SQL*PLUS prompt:   set sqlprompt ʹ&gname> ʹ  Thus my prompt will look something like this:  tkyte@TKYTE816>  In this manner, I know who I am, as well as where I am. Another very handy script to have  in the same directory as login.sql is this connect.sql script:   set termout off  connect &1  @login  set termout on  SQL*PLUS will only execute the login.sql script when it initially starts up. Generally, we  want it to execute every time we connect. I have just trained myself to use:   tkyte@TKYTE816> @connect scott/tiger  instead of just CONNECT SCOTT/TIGER. This way, my prompt is always set properly, as  are all other settings, such as SERVEROUTPUT.  Setting up AUTOTRACE in SQL*PLUS  Throughout the book it will be useful for us to monitor the performance of the queries we  execute by getting a report of the execution plan used by the SQL optimizer, along with  other useful execution statistics. Oracle provides a tool called EXPLAIN PLAN that, with  use of the EXPLAIN PLAN command, allows us to generate this execution plan output.   Note For information about interpreting the output of EXPLAIN PLAN, see the Oracle8i  Designing and Tuning for Performance guide.  However, SQL*PLUS provides an AUTOTRACE facility that allows us to see the execution  plans of the queries weʹve executed, and the resources they used, without having to use  the EXPLAIN PLAN command. The report is generated after successful SQL DML (that is,  SELECT, DELETE, UPDATE, and INSERT) statements. This book makes extensive use of  this facility. There is more than one way to to configure the AUTOTRACE facility. These  are the steps that I use:  • cd [ORACLE_HOME]/rdbms/admin   • log into SQL*PLUS as SYSTEM   Expert one-on-one Oracle 31 • run @utlxplan   • run CREATE PUBLIC SYNONYM PLAN_TABLE FOR PLAN_TABLE;   • run GRANT ALL ON PLAN_TABLE TO PUBLIC;   If you wish, you can replace the GRANT...TO PUBLIC with a GRANT to a specific user.  By granting the privelege to the PUBLIC role, you are effectively letting anyone trace  using SQL*PLUS. This is not a bad thing in my opinion as it prevents each and every user  from having to install their own plan table. The alternative is for you to run @UTLXPLAN  in every schema where you want to use the AUTOTRACE facility.  The next step is to create and grant the PLUSTRACE role:  • cd [ORACLE_HOME]/sqlplus/admin   • log into SQL*PLUS as SYS   • run @plustrce   • run GRANT PLUSTRACE TO PUBLIC;   Again, if you wish, you can replace PUBLIC in the GRANT with a specific user.   Controlling the Execution Plan Report  You can control the information displayed in the execution plan report by setting the  AUTOTRACE system variable.  SET AUTOTRACE OFF   No AUTOTRACE report is generated. This is the default.   SET AUTOTRACE ON  EXPLAIN   The AUTOTRACE report shows only the optimizer  execution path.   SET AUTOTRACE ON  STATISTICS   The AUTOTRACE report shows only the SQL statement  execution statistics.   SET AUTOTRACE ON   The AUTOTRACE report includes both the optimizer  execution path, and the SQL statement execution statistics.  SET AUTOTRACE  TRACEONLY   Like SET AUTOTRACE ON, but suppresses the printing of  the userʹs query output, if any.   Interpreting the Execution Plan  The execution plan shows the SQL optimizerʹs query execution path. Each line of the  Execution Plan has a sequential line number. SQL*PLUS also displays the line number of  the parent operation.  The execution plan consists of four columns displayed in the following order:  Expert one-on-one Oracle 32 Column Name  Description  ID_PLUS_EXP   Shows the line number of each execution step.  PARENT_ID_PLUS_EXP   Shows the relationship between each step, and its parent.  This column is useful for large reports.  PLAN_PLUS_EXP   Shows each step of the report.  OBJECT_NODE_PLUS_EXP   Shows the database links or parallel query servers used.  The format of the columns may be altered with the COLUMN command. For example, to  stop the PARENT_ID_PLUS_EXP column being displayed, enter:   SQL> column parent_id_plus_exp noprint  C Compilers  The Oracle‐supported compiler varies by operating system. On Microsoft Windows, I  used Microsoft Visual C/C++. I used only the command line portion of this tool (nmake  and cl). None of my examples use the GUI development environment. However, they may  also be developed in this environment if you wish. It would be up to you to configure the  appropriate include files, and link in the proper libraries. Each makefile contained in this  book are very small and simple ‐ it is obvious what include files and libraries are necessary.   On Sun Solaris, the supported C compiler is the Sun SparcsWorks compiler. Again, Iʹve  used only the command line tools, make and cc, in order to compile the scripts.  Coding Conventions  The only coding convention used in this book that I would like to explicitly point out, is  how I name variables in PL/SQL code. For example, consider a package body like this:  create or replace package body my_pkg  as     g_variable varchar2(25);           procedure p( p_variable in varchar2 )     is        l_variable varchar2(25);     begin        null;     end;  end;  /  Expert one-on-one Oracle 33 Here I have three variables, a global package variable G_VARIABLE, a formal parameter  to the procedure, P_VARIABLE, and finally a local variable, L_VARIABLE. I name my  variables according to their scope ‐ all globals begin with G_, parameters with P_, and  local variables with L_. The main reason for this is to distinguish PL/SQL variables from  columns in a database table. For example, a procedure such as:   create procedure p( ENAME in varchar2 )  as  begin     for x in ( select * from emp where ename = ENAME ) loop        Dbms_output.put_line( x.empno );     end loop;  end;  would always print out every row in the EMP table. SQL sees ename = ENAME, and  compares the ename column to itself (of course). We could use ename = P.ENAME ‐ that is,  qualify the reference to the PL/SQL variable with the procedure name, but this is too easy  to forget, leading to errors.  I always name my variables after the scope. That way, I can easily distinguish parameters  from local variables and globals, in addition to removing any ambiguity with respect to  column names and variable names.  Other Issues  Each chapter in this book is fairly self‐contained. At the beginning of each chapter, I  dropped my testing account, and recreated it. That is, each chapter started with a clean  schema ‐ no objects. If you work the examples from start to finish in each chapter, you will  want to do the same. When I query the data dictionary and such, to see what objects have  been created as a side effect of some command or another, it might get confusing if you  have left over objects from other examples. Also, I tended to reuse table names (especially  the table T), so if you donʹt clean up the schema between chapters, you may hit a conflict  there.  Additionally, if you attempt to manually drop the objects created by the example (as  opposed to just dropping the user via drop user USERNAME cascade, and recreating it),  you must be aware that the Java objects are all in mixed case. So, if you run the example in  Chapter 19, Java Stored Procedures:   tkyte@TKYTE816> create or replace and compile    2  java source named ʺdemoʺ    3  as    4  import java.sql.SQLException;  Expert one-on-one Oracle 34 ...  you will find that in order to drop it, you need to:  tkyte@TKYTE816> drop java source ʺdemoʺ;        Java dropped.  Remember to use the double quotes around the identifier for Java objects as they are  created and stored in mixed case.  Expert one-on-one Oracle 35 Chapter 1: Developing Successful Oracle  Applications  Overview  I spend the bulk of my time working with Oracle database software and, more to the point,  with people who use this software. Over the last twelve years, Iʹve worked on many  projects ‐ successful ones as well as failures, and if I were to encapsulate my experiences  into a few broad statements, they would be:  • An application built around the database ‐ dependent on the database ‐ will  succeed or fail based on how it uses the database.   • A development team needs at its heart a core of ʹdatabase savvyʹ coders who are  responsible for ensuring the database logic is sound and the system is tuned.   These may seem like surprisingly obvious statements, but in my experience, I have found  that too many people approach the database as if it were a ʹblack boxʹ ‐ something that  they donʹt need to know about. Maybe they have a SQL generator that will save them from  the hardship of having to learn SQL. Maybe they figure they will just use it like a flat file  and do ʹkeyed readsʹ. Whatever they figure, I can tell you that thinking along these lines is  most certainly misguided; you simply cannot get away with not understanding the  database. This chapter will discuss why you need to know about the database, specifically  why you need to understand:  • The database architecture, how it works, and what it looks like.   • What concurrency controls are, and what they mean to you.   • How to tune your application from day one.   • How some things are implemented in the database, which is not necessary the same  as how you think they should be implemented.   • What features your database already provides for you and why it is generally better  to use a provided feature then to build your own.   • Why you might want more than a cursory knowledge of SQL.   Now this may seem like a long list of things to learn before you start, but consider this  analogy for a second: if you were developing a highly scalable, enterprise application on a  brand new operating system (OS), what would be the first thing you would do? Hopefully,  you answered, ʹfind out how this new OS works, how things will run on it, and so onʹ. If  you did not, you would fail.  Consider, for example, one of the early versions of Windows (Windows 3.x, say). Now this,  like UNIX, was a ʹmulti‐taskingʹ operating system. However, it certainly didnʹt multi‐task  Expert one-on-one Oracle 36 like UNIX did ‐ it used a non‐preemptive multi‐tasking model (meaning that if the  running application didnʹt give up control, nothing else could run ‐ including the  operating system). In fact, compared to UNIX, Windows 3.x was not really a multi‐tasking  OS at all. Developers had to understand exactly how the Windows ʹmulti‐taskingʹ feature  was implemented in order to develop effectively. If you sit down to develop an  application that will run natively on an OS, then understanding that OS is very important.  What is true of applications running natively on operating systems is true of applications  that will run on a database: understanding that database is crucial to your success. If you  do not understand what your particular database does or how it does it, your application  will fail. If you assume that because your application ran fine on SQL Server, it will  necessarily run fine on Oracle then, again, your application is likely to fail.  My Approach  Before we begin, I feel it is only fair that you understand my approach to development. I  tend to take a database‐centric approach to problems. If I can do it in the database, I will.  There are a couple of reasons for this ‐ the first and foremost being that I know that if I  build functionality in the database, I can deploy it anywhere. I am not aware of a server  operating system on which Oracle is not available ‐ from Windows to dozens of UNIX  systems to the OS/390 mainframe, the same exact Oracle software and options are  available. I frequently build and test solutions on my laptop, running Oracle8i on  Windows NT. I deploy them on a variety of UNIX servers running the same database  software. When I have to implement a feature outside of the database, I find it extremely  hard to deploy that feature anywhere I want. One of the main features that makes Java  appealing to many people ‐ the fact that their programs are always compiled in the same  virtual environment, the Java Virtual Machine (JVM), and so are highly portable ‐ is the  exact same feature that make the database appealing to me. The database is my Virtual  Machine. It is my ʹvirtual operating systemʹ.  My approach is to do everything I can in the database. If my requirements go beyond  what the database environment can offer, I do it in Java outside of the database. In this  way, almost every operating system intricacy will be hidden from me. I still have to  understand how my ʹvirtual machinesʹ work (Oracle, and occasionally a JVM) ‐ you need  to know the tools you are using ‐ but they, in turn, worry about how best to do things on a  given OS for me.  Thus, simply knowing the intricacies of this one ʹvirtual OSʹ allows you to build  applications that will perform and scale well on many operating systems. I do not intend  to imply that you can be totally ignorant of your underlying OS ‐ just that as a software  developer building database applications you can be fairly well insulated from it, and you  will not have to deal with many of its nuances. Your DBA, responsible for running the  Oracle software, will be infinitely more in tune with the OS (if he or she is not, please get a  Expert one-on-one Oracle 37 new DBA!). If you develop client‐server software and the bulk of your code is outside of  the database and outside of a VM (Java Virtual Machines perhaps being the most popular  VM), you will have to be concerned about your OS once again.   I have a pretty simple mantra when it comes to developing database software:  • You should do it in a single SQL statement if at all possible.   • If you cannot do it in a single SQL Statement, then do it in PL/SQL.   • If you cannot do it in PL/SQL, try a Java Stored Procedure.   • If you cannot do it in Java, do it in a C external procedure.   • If you cannot do it in a C external routine, you might want to seriously think about  why it is you need to do it...   Throughout this book, you will see the above philosophy implemented. Weʹll use PL/SQL  and Object Types in PL/SQL to do things that SQL itself cannot do. PL/SQL has been  around for a very long time, over thirteen years of tuning has gone into it, and you will  find no other language so tightly coupled with SQL, nor any as optimized to interact with  SQL. When PL/SQL runs out of steam ‐ for example, when we want to access the network,  send e‐mailsʹ and so on ‐ weʹll use Java. Occasionally, weʹll do something in C, but  typically only when C is the only choice, or when the raw speed offered by C is required.  In many cases today this last reason goes away with native compilation of Java ‐ the ability  to convert your Java bytecode into operating system specific object code on your platform.  This lets Java run just as fast as C.  The Black Box Approach  I have an idea, borne out by first‐hand experience, as to why database‐backed software  development efforts so frequently fail. Let me be clear that Iʹm including here those  projects that may not be documented as failures, but take much longer to roll out and  deploy than originally planned because of the need to perform a major ʹre‐writeʹ, ʹre‐ architectureʹ, or ʹtuningʹ effort. Personally, I call these delayed projects ʹfailuresʹ: more  often than not they could have been completed on schedule (or even faster).  The single most common reason for failure is a lack of practical knowledge of the database  ‐ a basic lack of understanding of the fundamental tool that is being used. The ʹblackboxʹ  approach involves a conscious decision to protect the developers from the database. They  are actually encouraged not to learn anything about it! In many cases, they are prevented  from exploiting it. The reasons for this approach appear to be FUD‐related (Fear,  Uncertainty, and Doubt). They have heard that databases are ʹhardʹ, that SQL, transactions  and data integrity are ʹhardʹ. The solution ‐ donʹt make anyone do anything ʹhardʹ. They  treat the database as a black box and have some software tool generate all of the code.  They try to insulate themselves with many layers of protection so that they do not have to  touch this ʹhardʹ database.  Expert one-on-one Oracle 38 This is an approach to database development that Iʹve never been able to understand. One  of the reasons I have difficulty understanding this approach is that, for me, learning Java  and C was a lot harder then learning the concepts behind the database. Iʹm now pretty  good at Java and C but it took a lot more hands‐on experience for me to become competent  using them than it did to become competent using the database. With the database, you  need to be aware of how it works but you donʹt have to know everything inside and out.  When programming in C or Java, you do need to know everything inside and out and  these are huge languages.  Another reason is that if you are building a database application, then the most important  piece of software is the database. A successful development team will appreciate this and will  want its people to know about it, to concentrate on it. Many times Iʹve walked into a  project where almost the opposite was true.   A typical scenario would be as follows:  • The developers were fully trained in the GUI tool or the language they were using  to build the front end (such as Java). In many cases, they had had weeks if not  months of training in it.   • The team had zero hours of Oracle training and zero hours of Oracle experience.  Most had no database experience whatsoever.   • They had massive performance problems, data integrity problems, hanging issues  and the like (but very pretty screens).   As a result of the inevitable performance problems, I would be called in to help solve the  difficulties. I can recall one particular occasion when I could not fully remember the syntax  of a new command that we needed to use. I asked for the SQL Reference manual, and I was  handed an Oracle 6.0 document. The development was taking place on version 7.3, five  years after the release of version.6.0! It was all they had to work with, but this did not  seem to concern them at all. Never mind the fact that the tool they really needed to know  about for tracing and tuning didnʹt really exist back then. Never mind the fact that features  such as triggers, stored procedures, and many hundreds of others, had been added in the  five years since the documentation to which they had access was written. It was very easy  to determine why they needed help‐ fixing their problems was another issue all together.  The idea that developers building a database application should be shielded from the  database is amazing to me but still the attitude persists. Many people still take the attitude  that developers should be shielded from the database, they cannot take the time to get  trained in the database ‐ basically, they should not have to know anything about the  database. Why? Well, more than once Iʹve heard ʹ... but Oracle is the most scalable  database in the world, my people donʹt have to learn about it, itʹll just do thatʹ. It is true;  Oracle is the most scalable database in the world. However, I can write bad code that does  not scale in Oracle easier then I can write good, scaleable code in Oracle. You can replace  Expert one-on-one Oracle 39 Oracle with any technology and the same will be true. This is a fact ‐ it is easier to write  applications that perform poorly than it is to write applications that perform well. It is  sometimes too easy to build a single‐user system in the worldʹs most scalable database if  you donʹt know what you are doing. The database is a tool and the improper use of any  tool can lead to disaster. Would you take a nutcracker and smash walnuts with it as if it  were a hammer? You could but it would not be a proper use of that tool and the result  would be a mess. Similar effects can be achieved by remaining ignorant of your database.  I was recently working on a project where the system architects had designed a very  elegant architecture. A web browser client would talk over HTTP to an application server  running Java Server Pages (JSP). The application logic would be 100 percent generated by  a tool and implemented as EJBs (using container managed persistence) and would be  physically located on another application server. The database would hold tables and  indexes and nothing else.  So, we start with a technically complex architecture: we have four entities that must talk to  each other in order to get the job done: web browser to a JSP in the Application Server to  an EJB to the database. It would take technically competent people to develop, test, tune,  and deploy this application. I was asked to help benchmark this application post‐ development. The first thing I wanted to know about was their approach to the database:  • What did they feel would be the major choke points, areas of contention?   • What did they view as the major obstacles to overcome?   They had no idea. When asked, ʹOK, when we need to tune a generated query, who can  help me rewrite the code in the EJB?ʹ The answer was, ʹOh, you cannot tune that code, you  have to do it all in the databaseʹ. The application was to remain untouched. At that point, I  was ready to walk away from the project ‐ it was already clear to me that there was no  way this application would work:  • The application was built without a single consideration for scaling the database  level.   • The application itself could not be tuned or touched.   • Experience shows that 80 to 90 percent of all tuning is done at the application level,  not at the database level.   • The developers had no idea what the beans did in the database or where to look for  potential problems.   That was shown to be the case in the first hour of testing. As it turns out, the first thing the  application did was a:  select * from t for update;  Expert one-on-one Oracle 40 What this did was to force a serialization of all work. The model implemented in the  database was such that before any significant work could proceed, you had to lock an  extremely scarce resource. That immediately turned this application into a very large  single user system. The developers did not believe me (in another database, employing a  shared read lock, the observed behavior was different). After spending ten minutes with a  tool called TKPROF (youʹll hear a lot more about this in Tuning Strategies and Tools,  Chapter 10) I was able to show them that, yes, in fact this was the SQL executed by the  application (they had no idea ‐ they had never seen the SQL). Not only was it the SQL  executed by the application but by using two SQL*PLUS sessions I was able to show them  that session two will wait for session one to completely finish its work before proceeding.  So, instead of spending a week benchmarking the application, I spent the time teaching  them about tuning, database locking, concurrency control mechanisms, how it worked in  Oracle versus Informix versus SQL Server versus DB2 and so on (it is different in each  case). What I had to understand first, though, was the reason for the SELECT FOR  UPDATE. It turned out the developers wanted a repeatable read.  Note Repeatable read is a database term that says if I read a row once in my transaction  and I read the row again later in the same transaction, the row will not have changed  ‐ the read is repeatable.  Why did they want this? They had heard it was a ʹgood thingʹ. OK, fair enough, you want  repeatable read. The way to do that in Oracle is to set the isolation level to serializable  (which not only gives you a repeatable read for any row of data, it gives you a repeatable  read for a query ‐ if you execute the same query two times in a transaction, youʹll get the  same results). To get a repeatable read in Oracle, you do not want to use SELECT FOR  UPDATE, which you only do when you want to physically serialize access to data.  Unfortunately, the tool they utilized did not know about that ‐ it was developed primarily  for use with another database where this was the way to get a repeatable read.  So, what we had to do in this case, in order to achieve serializable transactions, was to  create a logon trigger in the database that altered the session for these applications and set  the isolation level to serializable. We went back to the tool they were using and turned off  the switches for repeatable reads and re‐ran the application. Now, with the FOR UPDATE  clause removed, we got some actual concurrent work done in the database.   That was hardly the end of the problems on this project. We had to figure out:  • How to tune SQL without changing the SQL (thatʹs hard, weʹll look at some  methods in Chapter 11 on Optimizer Plan Stability).   • How to measure performance.   • How to see where the bottlenecks were.   • How and what to index. And so on.   Expert one-on-one Oracle 41 At the end of the week the developers, who had been insulated from the database, were  amazed at what the database could actually provide for them, how easy it was to get that  information and, most importantly, how big a difference it could make to the performance  of their application. We didnʹt do the benchmark that week (they had some reworking to  do!) but in the end they were successful ‐ just behind schedule by a couple of weeks.  This is not a criticism of tools or technologies like EJBs and container managed persistence.  This is a criticism of purposely remaining ignorant of the database and how it works and  how to use it. The technologies used in this case worked well ‐ after the developers got  some insight into the database itself.  The bottom line is that the database is typically the cornerstone of your application. If it  does not work well, nothing else really matters. If you have a black box and it does not  work well ‐ what are you going to do about it? About the only thing you can do is look at  it and wonder why it is not doing so well. You cannot fix it, you cannot tune it, you quite  simply do not understand how it works ‐ and you made the decision to be in this position.  The alternative is the approach that I advocate: understand your database, know how it  works, know what it can do for you, and use it to its fullest potential.  How (and how not) to Develop Database Applications  Thatʹs enough hypothesizing, for now at least. In the remainder of this chapter, I will take  a more empirical approach, discussing why knowledge of the database and its workings  will definitely go a long way towards a successful implementation (without having to  write the application twice!). Some problems are simple to fix as long as you understand  how to find them. Others require drastic rewrites. One of the goals of this book is to help  you avoid the problems in the first place.  Note In the following sections, I discuss certain core Oracle features without delving into  exactly what these features are and all of the ramifications of using them. For  example, I discuss just one of the implications of using Multi‐Threaded Server (MTS)  architecture‐ a mode in which you can (and sometimes have to) configure Oracle in  order to support multiple database connections. I will not, however, go fully into  what MTS is, how it works and so on. Those facts are covered in detail in the Oracle  Server Concepts Manual (with more information to be found in the Net8  Administrators Guide).   Understanding Oracle Architecture  I was working on a project recently where they decided to use only the latest, greatest  technologies: everything was coded in Java with EJBs. The client application would talk to  the database server using beans ‐ no Net8. They would not be passing SQL back and forth  Expert one-on-one Oracle 42 between client and server, just EJB calls using Remote Method Invocation (RMI) over  Internet Inter‐Orb Protocol (IIOP).  Note If you are interested in the details of RMI over IIOP you can refer to  http://java.sun.com/products/rmi‐iiop/   This is a perfectly valid approach. This functionality works and can be extremely scalable.  The people responsible for the architecture understood Java, EJBs, the protocols involved ‐  all of that stuff. They felt they were in a strong position to successfully build such a project.  When their application would not scale beyond a couple of users they decided that the  database was at fault and severely doubted Oracleʹs claim to be the ʹmost scaleable  database everʹ.  The problem was not the database but a lack of knowledge of how the database worked ‐ a  lack of knowledge that meant that certain key decisions were made at design time that  doomed this particular application to failure. In order to deploy EJBs in the database  Oracle must be configured to run in MTS mode rather than dedicated server mode. What  the team did not understand, crucially, was how using MTS with EJBs in the database  would affect them. Without this understanding, and without a general knowledge of how  Oracle worked, two key decisions were made:  • We will run some stored procedures that take 45 seconds or longer (much longer at  times) in our beans.   • We will not support the use of bind variables. All of our queries will hard code the  constant values in the predicate. All inputs to stored procedures will use strings.  This is ʹeasierʹ than coding bind variables.   These two seemingly minor decisions guaranteed that the project would fail ‐ utterly  guaranteed it. They made it so that a highly scalable database would literally fall over and  fail with a very small user load. A lack of knowledge of how the database worked more  then overwhelmed their intricate knowledge of Java beans and distributed processing. If  they had taken time to learn a bit more about the way Oracle worked, and consequently  followed the following two simple guidelines, then their project would have had a much  better chance of success the first time out.  Do not run Long Transactions Under MTS  The decision to run 45+ second transactions under MTS betrayed a lack of understanding  of what MTS was designed to do and how it works in Oracle. Briefly, MTS works by  having a shared pool of server processes that service a larger pool of end users. It is very  much like connection pooling ‐ since process creation and management are some of the  most expensive operations you can ask an operating system to perform, MTS is very  Expert one-on-one Oracle 43 beneficial in a large‐scale system. So, I might have 100 users but only five or ten shared  servers.  When a shared server gets a request to run an update, or execute a stored procedure, then  that shared server is dedicated to that task until completion. No one else will use that  shared server until that update completes or that stored procedure finishes execution.  Thus, when using MTS your goal must be to have very short statements. MTS is designed  to scale up On‐Line Transaction Processing (OLTP) systems ‐ a system characterized by  statements that execute with sub‐second response times. Youʹll have a single row update,  insert a couple of line items, and query records by primary key. You wonʹt (or shouldnʹt)  run a batch process that takes many seconds or minutes to complete.   If all of our statements execute very rapidly, then MTS works well. We can effectively  share a number of processes amongst a larger community of users. If, on the other hand,  we have sessions that monopolize a shared server for extended periods of time then we  will see apparent database ʹhangsʹ. Say we configured ten shared servers for 100 people. If,  at some point, ten people simultaneously execute the process that takes 45 seconds or  longer then every other transaction (including new connections) will have to wait. If some  of the queued sessions want to run that same long process, then we have a big problem ‐  the apparent ʹhangʹ wonʹt last 45 seconds, it will appear to last much longer for most  people. Even if we only have a few people wanting to execute this process simultaneously  rather than ten, we will still observe what appears to be a large degradation in  performance from the server. We are taking away, for an extended period of time, a  shared resource and this is not a good thing. Instead of having ten shared servers  processing quick requests on a queue, we now have five or six (or less). Eventually the  system will be running at some fraction of its capability, solely due to this resource being  consumed.  The ʹquick and dirtyʹ solution was to start up more shared servers, but the logical  conclusion to this is that you need a shared server per user and this is not a reasonable  conclusion for a system with thousands of users (as this system was). Not only would that  introduce bottlenecks into the system itself (the more servers you have to manage ‐ the  more processing time spent managing), but also it is simply not the way MTS was  designed to work.  The real solution to this problem was simple: do not execute long running transactions  under MTS. Implementing this solution was not. There was more then one way to  implement this and they all required fundamental architectural changes. The most  appropriate way to fix this issue, requiring the least amount of change, was to use  Advanced Queues (AQ).  Note AQ is a message‐oriented middleware hosted in the Oracle database. It provides the  ability for a client session to enqueue a message into a database queue table. This  Expert one-on-one Oracle 44 message is later, typically immediately after committing, ʹdequeuedʹ by another  session and the content of the message is inspected. This message contains  information for the other session to process. It can be used to give the appearance of  lightening fast response times by decoupling the long running process from the  interactive client.  So, rather than execute a 45‐second process, the bean would place the request, along with  all its inputs, on a queue and execute it in a loosely coupled (asynchronous) rather than  tightly coupled (synchronous) fashion. In this way, the end user would not have to wait 45  seconds for a response ‐ the system would apparently be much more responsive  While this approach sounds easy ‐ just drop in ʹAQʹ and the problem is fixed ‐ there was  more to it than that. This 45‐second process generated a transaction ID that was required  by the next step in the interface in order to join to other tables ‐ as designed, the interface  would not work without it. By implementing AQ, we were not waiting for this transaction  ID to be generated here ‐ we were just asking the system to do it for us at some point. So,  the application was stuck. On the one hand, we could not wait 45 seconds for the process  to complete, but on the other hand, we needed the generated ID in order to proceed to the  next screen and we could only get that after waiting 45 seconds. To solve this problem,  what we had to do was to synthesize a pseudo‐transaction ID, modify the long running  process to accept this generated pseudo ID and have it update a table when it was done,  by which mechanism the real transaction ID was associated with the pseudo id. That is,  instead of the transaction ID being an output of the long running process, it would be an  input to it. Further, all ʹdownstreamʹ tables would have to use this pseudo‐transaction ID ‐  not the real one (since the real one would not be generated for a while). We also had to  review the usage of this transaction ID in order to see what impact this change might have  on other modules and so on.   Another consideration was the fact that, while we were running synchronously, if the 45‐ second process failed then the end user was alerted right away. They would fix the error  condition (fix the inputs, typically) and resubmit the request. Now, with the transaction  being processed asynchronously under AQ, we donʹt have that ability. New functionality  had to be added in order to support this delayed response. Specifically, we needed some  workflow mechanism to route any failed transaction to the appropriate person.  The upshot of all this is that we had to undertake major changes in the database structure.  New software had to be added (AQ). New processes had to be developed (workflows and  such). On the plus side, the removal of 45 seconds of lag time from an interactive process  not only solved the MTS architecture issue, it enhanced the user experience ‐ it meant that  the end user got much faster ʹapparentʹ response times. On the down side, all of this  delayed the project considerably because none of it was detected until immediately before  deployment, during scalability testing. It is just too bad that it was not designed the right  Expert one-on-one Oracle 45 way from the beginning. With knowledge of how MTS worked physically, it would have  been clear that the original design would not scale very well.  Use Bind Variables  If I were to write a book about how to build non‐scalable Oracle applications, then Donʹt use  Bind Variables would be the first and last chapter. This is a major cause of performance  issues and a major inhibitor of scalability. The way the Oracle shared pool (a very  important shared memory data structure) operates is predicated on developers using bind  variables. If you want to make Oracle run slowly, even grind to a total halt ‐ just refuse to  use them.  Bind variable is a placeholder in a query. For example, to retrieve the record for employee  123, I can query:   select * from emp where empno = 123;  Alternatively, I can query:  select * from emp where empno = :empno;  In a typical system, you would query up employee 123 maybe once and then never again.  Later, you would query up employee 456, then 789, and so on. If you use literals (constants)  in the query then each and every query is a brand new query, never before seen by the  database. It will have to be parsed, qualified (names resolved), security checked,  optimized, and so on ‐ in short, each and every unique statement you execute will have to  be compiled every time it is executed.  The second query uses a bind variable, :empno, the value of which is supplied at query  execution time. This query is compiled once and then the query plan is stored in a shared  pool (the library cache), from which it can be retrieved and reused. The difference between  the two in terms of performance and scalability is huge, dramatic even.   From the above description it should be fairly obvious that parsing a statement with hard‐ coded variables (called a hard parse) will take longer and consume many more resources  than reusing an already parsed query plan (called a soft parse). What may not be so  obvious is the extent to which the former will reduce the number of users your system can  support. Obviously, this is due in part to the increased resource consumption, but an even  larger factor arises due to the latching mechanisms for the library cache. When you hard  parse a query, the database will spend more time holding certain low‐level serialization  devices called latches (see Chapter 3, Locking and Concurrency, for more details). These  latches protect the data structures in the shared memory of Oracle from concurrent  modifications by two sessions (else Oracle would end up with corrupt data structures) and  Expert one-on-one Oracle 46 from someone reading a data structure while it is being modified. The longer and more  frequently we have to latch these data structures, the longer the queue to get these latches  will become. In a similar fashion to having long transactions running under MTS, we will  start to monopolize scarce resources. Your machine may appear to be under‐utilized at  times ‐ and yet everything in the database is running very slowly. The likelihood is that  someone is holding one of these serialization mechanisms and a line is forming ‐ you are  not able to run at top speed. It only takes one ill behaved application in your database to  dramatically affect the performance of every other application. A single, small application  that does not use bind variable will cause the relevant SQL of other well tuned  applications to get discarded from the shared pool over time. You only need one bad  apple to spoil the entire barrel.  If you use bind variables, then everyone who submits the same exact query that references  the same object will use the compiled plan from the pool. You will compile your  subroutine once and use it over and over again. This is very efficient and is the way the  database intends you to work. Not only will you use fewer resources (a soft parse is much  less resource intensive), but also you will hold latches for less time and need them less  frequently. This increases your performance and greatly increases your scalability.  Just to give you a tiny idea of how huge a difference this can make performance‐wise, you  only need to run a very small test:  tkyte@TKYTE816> alter system flush shared_pool;        System altered.  Here I am starting with an ʹemptyʹ shared pool. If I was to run this test more than one time,  I would need to flush the shared pool every time, or else the non‐bind variable SQL below  would, in fact, be cached and appear to run very fast.  tkyte@TKYTE816> set timing on  tkyte@TKYTE816> declare    2      type rc is ref cursor;    3      l_rc rc;    4      l_dummy all_objects.object_name%type;    5      l_start number default dbms_utility.get_time;    6  begin    7      for i in 1 .. 1000    8      loop    9          open l_rc for   10          ʹselect object_name   11             from all_objects   12            where object_id = ʹ || i;  Expert one-on-one Oracle 47  13          fetch l_rc into l_dummy;   14          close l_rc;   15      end loop;   16      dbms_output.put_line   17      ( round( (dbms_utility.get_time‐l_start)/100, 2 ) ||   18        ʹ seconds...ʹ );   19  end;   20  /  14.86 seconds...        PL/SQL procedure successfully completed.  The above code uses dynamic SQL to query out a single row from the ALL_OBJECTS table.  It generates 1000 unique queries with the values 1, 2, 3, ... and so on ʹhard‐codedʹ into the  WHERE clause. On my 350MHz Pentium laptop, this took about 15 seconds (the speed  may vary on different machines).  Next, we do it using bind variables:  tkyte@TKYTE816> declare    2      type rc is ref cursor;    3      l_rc rc;    4      l_dummy all_objects.object_name%type;    5      l_start number default dbms_utility.get_time;    6  begin    7      for i in 1 .. 1000    8      loop    9          open l_rc for   10          ʹselect object_name   11             from all_objects   12            where object_id = :xʹ   13          using i;   14          fetch l_rc into l_dummy;   15          close l_rc;   16      end loop;   17      dbms_output.put_line   18      ( round( (dbms_utility.get_time‐l_start)/100, 2 ) ||   19        ʹ seconds...ʹ );   20  end;   21  /  1.27 seconds...        PL/SQL procedure successfully completed.  Expert one-on-one Oracle 48 We use the same logic here ‐ the only thing that has changed is the fact that we are not  hard coding the values 1, 2, 3… and so on into the query ‐ we are using bind variables  instead. The results are pretty dramatic. The fact is that not only does this execute much  faster (we spent more time parsing our queries then actually executing them!) it will let  more users use your system simultaneously.  Executing SQL statements without bind variables is very much like compiling a  subroutine before each and every method call. Imagine shipping Java source code to your  customers where, before calling a method in a class, they had to invoke the Java compiler,  compile the class, run the method, and then throw away the byte code. Next time they  wanted to execute the exact same method, they would do the same thing; compile it, run it,  and throw it away. You would never consider doing this in your application ‐ you should  never consider doing this in your database either.  Note In Chapter 10, Tuning Strategies and Tools, we will look at ways to identify whether  or not you are using bind variables, different ways to use them, an ʹauto binderʹ  feature in the database and so on. We will also discuss a specialized case where you  donʹt want to use bind variables.  As it was, on this particular project, rewriting the existing code to use bind variables was  the only possible course of action. The resulting code ran orders of magnitude faster and  increased many times the number of simultaneous users that the system could support.  However, it came at a high price in terms of time and effort. It is not that using bind  variables is hard, or error prone, itʹs just that they did not do it initially and thus were  forced to go back and revisit virtually all of the code and change it. They would not have  paid this price if they had understood that it was vital to use bind variables in their  application from day one.   Understanding Concurrency Control  Concurrency control is one area where databases differentiate themselves. It is an area that  sets a database apart from a file system and that sets databases apart from each other. As a  programmer, it is vital that your database application works correctly under concurrent  access conditions, and yet this is something people fail to test time and time again.  Techniques that work well if everything happens consecutively do not work so well when  everyone does them simultaneously. If you donʹt have a good grasp of how your  particular database implements concurrency control mechanisms, then you will:  • Corrupt the integrity of your data.   • Run slower than you should with a small number of users.   • Decrease your ability to scale to a large number of users.   Expert one-on-one Oracle 49 Notice I donʹt say, ʹyou might...ʹ or ʹyou run the risk of...ʹ but rather that invariably you will  do these things. You will do these things without even realizing it. Without correct  concurrency control, you will corrupt the integrity of your database because something  that works in isolation will not work as you expect in a multi‐user situation. You will run  slower than you should because youʹll end up waiting for data. Youʹll lose your ability to  scale because of locking and contention issues. As the queues to access a resource get  longer, the wait gets longer and longer. An analogy here would be a backup at a tollbooth.  If cars arrive in an orderly, predictable fashion, one after the other, we never have a  backup. If many cars arrive simultaneously, queues start to form. Furthermore, the  waiting time does not increase in line with the number of cars at the booth. After a certain  point we are spending considerable additional time ʹmanagingʹ the people that are waiting  in line, as well as servicing them (in the database, we would talk about context switching).  Concurrency issues are the hardest to track down ‐ the problem is similar to debugging a  multi‐threaded program. The program may work fine in the controlled, artificial  environment of the debugger but crashes horribly in the ʹreal worldʹ. For example, under  ʹrace conditionsʹ you find that two threads can end up modifying the same data structure  simultaneously. These kinds of bugs are terribly hard to track down and fix. If you only  test your application in isolation and then deploy it to dozens of concurrent users, you are  likely to be (painfully) exposed to an undetected concurrency issue.  Over the next two sections, Iʹll relate two small examples of how the lack of understanding  concurrency control can ruin your data or inhibit performance and scalability.  Implementing Locking  The database uses locks to ensure that, at most, one transaction is modifying a given piece  of data at any given time. Basically, they are the mechanism that allows for concurrency ‐  without some locking model to prevent concurrent updates to the same row, for example,  multi‐user access would not be possible in a database. However, if overused or used  improperly, locks can actually inhibit concurrency. If you or the database itself locks data  unnecessarily, then fewer people will be able to concurrently perform operations. Thus,  understanding what locking is and how it works in your database is vital if you are to  develop a scalable, correct application.  What is also vital is that you understand that each database implements locking  differently. Some have page‐level locking, others row level; some implementations  escalate locks from row‐level to page‐level, some do not; some use read locks, others do  not; some implement serializable transactions via locking and others via read‐consistent  views of data (no locks). These small differences can balloon into huge performance issues  or downright bugs in your application if you do not understand how they work.   The following points sum up Oracleʹs locking policy:  Expert one-on-one Oracle 50 • Oracle locks data at the row level on modification only. There is no lock escalation  to a block or table level, ever.   • Oracle never locks data just to read it. There are no locks placed on rows of data by  simple reads.   • A writer of data does not block a reader of data. Let me repeat ‐ reads are not  blocked by writes. This is fundamentally different from almost every other database,  where reads are blocked by writes.   • A writer of data is blocked only when another writer of data has already locked the  row it was going after. A reader of data never blocks a writer of data.   These facts must be taken into consideration when developing your application and you  must also realize that this policy is unique to Oracle. A developer who does not  understand how his or her database handles concurrency will certainly encounter data  integrity issues (this is particularly common when a developer moves from another  database to Oracle, or vice versa, and neglects to take the differing concurrency  mechanisms into account in their application).  One of the side‐effects of Oracleʹs ʹnon‐blockingʹ approach is that if you actually want to  ensure that no more than one user has access to a row at once, then you, the developer,  need to do a little work yourself. Consider the following example. A developer was  demonstrating to me a resource‐scheduling program (for conference rooms, projectors, etc.)  that he had just developed and was in the process of deploying. The application  implemented a business rule to prevent the allocation of a resource to more than one  person, for any given period of time. That is, the application contained code that  specifically checked that no other user had previously allocated the time slot (as least the  developer thought it did). This code queried the schedules table and, if no rows existed  that overlapped that time slot, inserted the new row. So, the developer was basically  concerned with two tables:   create table resources ( resource_name varchar2(25) primary key, ... );  create table schedules( resource_name varchar2(25) references resources,                          start_time    date,                          end_time      date );  And, before making, say, a room reservation, the application would query:  select count(*)    from schedules   where resource_name = :room_name     and (start_time between :new_start_time and :new_end_time          or          end_time between :new_start_time and :new_end_time)  Expert one-on-one Oracle 51 It looked simple and bullet‐proof (to the developer anyway); if the count came back zero,  the room was yours. If it came back non‐zero, you could not reserve it for that period.  Once I knew what his logic was, I set up a very simple test to show him the error that  would occur when the application went live. An error that would be incredibly hard to  track down and diagnose after the fact ‐ one would be convinced it must be a database bug.   All I did was get someone else to use the terminal next to him. They both navigated to the  same screen and, on the count of three, each hit the Go button and tried to reserve the  same room for the exact same time. Both people got the reservation ‐ the logic, which  worked perfectly in isolation, failed in a multi‐user environment. The problem in this case  was caused by Oracleʹs non‐blocking reads. Neither session ever blocked the other session.  Both sessions simply ran the above query and then performed the logic to schedule the  room. They could both run the query to look for a reservation, even if the other session  had already started to modify the schedules table (the change wouldnʹt be visible to the  other session until commit, by which time it was too late). Since they were never  attempting to modify the same row in the schedules table, they would never block each  other and, thus, the business rule could not enforce what it was intended to enforce.  The developer needed a method of enforcing the business rule in a multi‐user  environment, a way to ensure that exactly one person at a time made a reservation on a  given resource. In this case, the solution was to impose a little serialization of his own ‐ in  addition to performing the count(*) above, the developer must first:   select * from resources where resource_name = :room_name FOR UPDATE;  A little earlier in the chapter, we discussed an example where use of the FOR UPDATE  clause caused problems, but here it is what makes this business rule work in the way  intended. What we did here was to lock the resource (the room) to be scheduled  immediately before scheduling it, in other words before we query the Schedules table for  that resource. By locking the resource we are trying to schedule, we have ensured that no  one else is modifying the schedule for this resource simultaneously. They must wait until  we commit our transaction ‐ at which point, they would be able to see our schedule. The  chance of overlapping schedules is removed. The developer must understand that, in the  multi‐user environment, they must at times employ techniques similar to those used in  multi‐threaded programming. The FOR UPDATE clause is working like a semaphore in  this case. It serializes access to the resources tables for that particular row ‐ ensuring no  two people can schedule it simultaneously.  This is still highly concurrent as there are potentially thousands of resources to be reserved  ‐ what we have done is ensure that only one person modifies a resource at any time. This  is a rare case where the manual locking of data you are not going to actually update is  called for. You need to be able to recognize where you need to do this and, perhaps as  importantly, where not to (I have an example of when not to below). Additionally, this  Expert one-on-one Oracle 52 does not lock the resource from other people reading the data as it might in other  databases, hence this will scale very well.  Issues such as the above have massive implications when attempting to port an  application from database to database (I return to this theme a little later in the chapter),  and this trips people up time and time again. For example, if you are experienced in other  databases, where writers block readers and vice versa then you may have grown reliant on  that fact to protect you from data integrity issues. The lack of concurrency is one way to  protect yourself from this ‐ that is how it works in many non‐Oracle databases. In Oracle,  concurrency rules supreme and you must be aware that, as a result, things will happen  differently (or suffer the consequences).  For 99 percent of the time, locking is totally transparent and you need not concern yourself  with it. It is that other 1 percent that you must be trained to recognize. There is no simple  checklist of ʹif you do this, you need to do thisʹ for this issue. This is a matter of  understanding how your application will behave in a multi‐user environment and how it  will behave in your database.   Multi‐Versioning  This is a topic very closely related to concurrency control, as it forms the foundation for  Oracleʹs concurrency control mechanism ‐ Oracle operates a multi‐version read‐consistent  concurrency model. In Chapter 3, Locking and Concurrency, weʹll cover the technical aspects  of this in more detail but, essentially, it is the mechanism by which Oracle provides for:  • Read‐consistent queries:  Queries that produce consistent results with respect to a  point in time.   • Non‐blocking queries:  Queries are never blocked by writers of data, as they would  be in other databases.   These are two very important concepts in the Oracle database. The term multi‐versioning  basically comes from the fact that Oracle is able to simultaneously maintain multiple  versions of the data in the database. If you understand how multi‐versioning works, you  will always understand the answers you get from the database. Before we explore in a  little more detail how Oracle does this, here is the simplest way I know to demonstrate  multi‐versioning in Oracle:  tkyte@TKYTE816> create table t    2  as    3  select * from all_users;  Table created.        tkyte@TKYTE816> variable x refcursor  Expert one-on-one Oracle 53       tkyte@TKYTE816> begin    2          open :x for select * from t;    3  end;    4  /        PL/SQL procedure successfully completed.        tkyte@TKYTE816> delete from t;        18 rows deleted.        tkyte@TKYTE816> commit;        Commit complete.        tkyte@TKYTE816> print x        USERNAME                          USER_ID CREATED  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐  SYS                                     0 04‐NOV‐00  SYSTEM                                  5 04‐NOV‐00  DBSNMP                                 16 04‐NOV‐00  AURORA$ORB$UNAUTHENTICATED             24 04‐NOV‐00  ORDSYS                                 25 04‐NOV‐00  ORDPLUGINS                             26 04‐NOV‐00  MDSYS                                  27 04‐NOV‐00  CTXSYS                                 30 04‐NOV‐00  ...  DEMO                                   57 07‐FEB‐01        18 rows selected.  In the above example, we created a test table, T, and loaded it with some data from the  ALL_USERS table. We opened a cursor on that table. We fetched no data from that cursor:  we just opened it.  Note Bear in mind that Oracle and does not ʹanswerʹ the query, does not copy the data  anywhere when you open a cursor ‐ imagine how long it would take to open a  cursor on a one billion row table if it did. The cursor opens instantly and it answers  the query as it goes along. In other words, it would just read data from the table as  you fetched from it.  Expert one-on-one Oracle 54 In the same session (or maybe another session would do this), we then proceeded to delete  all data from that table. We even went as far as to COMMIT work on that delete. The rows  are gone ‐ but are they? In fact, they are retrievable via the cursor. The fact is that the  resultset returned to us by the OPEN command was pre‐ordained at the point in time we  opened it. We had touched not a single block of data in that table during the open, but the  answer was already fixed in stone. We have no way of knowing what the answer will be  until we fetch the data ‐ however the result is immutable from our cursorʹs perspective. It  is not that Oracle copied all of the data above to some other location when we opened the  cursor; it was actually the delete command that preserved our data for us by placing it into  a data area called a rollback segment.  This is what read‐consistency is all about and if you do not understand how Oracleʹs  multi‐versioning scheme works and what it implies, you will not be able to take full  advantage of Oracle nor will you be able to write correct applications in Oracle (ones that  will ensure data integrity).  Letʹs look at the implications of multi‐versioning, read‐consistent queries and non‐ blocking reads. If you are not familiar with multi‐versioning, what you see below might be  surprising. For the sake of simplicity, we will assume that the table we are reading stores  one row per database block (the smallest unit of storage in the database), and that we are  fullscanning the table in this example.  The table we will query is a simple accounts table. It holds balances in accounts for a bank.  It has a very simple structure:   create table accounts  ( account_number number primary key,    account_balance number  );  In reality the accounts table would have hundreds of thousands of rows in it, but for  simplicity weʹre just going to consider a table with four rows (we will visit this example in  more detail in Chapter 3, Locking and Concurrency):  Row  Account Number  Account Balance  1  123  $500.00  2  234  $250.00  3  345  $400.00  4  456  $100.00  What we would like to do is to run the end‐of‐day report that tells us how much money is  in the bank. That is an extremely simple query:   Expert one-on-one Oracle 55 select sum(account_balance) from accounts;  And, of course, in this example the answer is obvious: $1250. However, what happens if  we read row 1, and while weʹre reading rows 2 and 3, an Automated Teller Machine (ATM)  generates transactions against this table, and moves $400 from account 123 to account 456?  Our query counts $500 in row 4 and comes up with the answer of $1650, doesnʹt it? Well,  of course, this is to be avoided, as it would be an error ‐ at no time did this sum of money  exist in the account balance column. It is the way in which Oracle avoids such occurrences,  and how Oracleʹs methods differ from every other database, that you need to understand.  In practically every other database, if you wanted to get a ʹconsistentʹ and ʹcorrectʹ answer  to this query, you would either have to lock the whole table while the sum was calculated  or you would have to lock the rows as you read them. This would prevent people from  changing the answer as you are getting it. If you lock the table up‐front, youʹll get the  answer that was in the database at the time the query began. If you lock the data as you  read it (commonly referred to as a shared read lock, which prevents updates but not other  readers from accessing the data), youʹll get the answer that was in the database at the point  the query finished. Both of these methods inhibit concurrency a great deal. The table lock  would prevent any updates from taking place against the entire table for the duration of  your query (for a table of four rows, this would only be a very short period ‐ but for tables  with hundred of thousands of rows, this could be several minutes). The ʹlock as you goʹ  method would prevent updates on data you have read and already processed and could  actually cause deadlocks between your query and other updates.  Now, I said earlier that you would not be able to take full advantage of Oracle if you did  not understand the concept of multi‐versioning. Here is one reason why that is true.  Oracle uses multi‐versioning to get the answer, as it existed at the point in time the query  began, and the query will take place without locking a single thing (while our account  transfer transaction updates rows 1 and 4, these rows will be locked to other writers ‐ but  not locked to other readers, such as our SELECT SUM...query). In fact, Oracle doesnʹt have  a ʹshared readʹ lock common in other databases ‐ it does not need it. Everything inhibiting  concurrency that can be removed, has been removed.  So, how does Oracle get the correct, consistent answer ($1250) during a read without  locking any data‐ in other words, without decreasing concurrency? The secret lies in the  transactional mechanisms that Oracle uses. Whenever you modify data, Oracle creates  entries in two different locations. One entry goes to the redo logs where Oracle stores  enough information to redo or ʹroll forwardʹ the transaction. For an insert this would be  the row inserted. For a delete, it is a message to delete the row in file X, block Y, row slot Z.  And so on. The other entry is an undo entry, written to a rollback segment. If your  transaction fails and needs to be undone, Oracle will read the ʹbeforeʹ image from the  rollback segment and restore the data. In addition to using this rollback segment data to  undo transactions, Oracle uses it to undo changes to blocks as it is reading them ‐ to  Expert one-on-one Oracle 56 restore the block to the point in time your query began. This gives you the ability to read  right through a lock and to get consistent, correct answers without locking any data  yourself.   So, as far as our example is concerned, Oracle arrives at its answer as follows:  Time  Query  Account transfer transaction  T1  Reads row 1, sum = $500 so far    T2    Updates row 1, puts an exclusive lock  on row 1 preventing other updates.  Row 1 now has $100  T3  Reads row 2, sum = $750 so far    T4  Reads row 3, sum = $1150 so far    T5    Updates row 4, puts an exclusive lock  on block 4 preventing other updates  (but not reads). Row 4 now has $500.  T6  Reads row 4, discovers that row 4  has been modified. It will  actually rollback the block to  make it appear as it did at time =  T1. The query will read the value  $100 from this block     T7    Commits transaction  T8  Presents $1250 as the answer    At time T6, Oracle is effectively ʹreading throughʹ the lock placed on row 4 by our  transaction. This is how non‐blocking reads are implemented ‐ Oracle only looks to see if  the data changed, it does not care if the data is currently locked (which implies that it has  changed). It will simply retrieve the old value from the rollback segment and proceed onto  the next block of data.  This is another clear demonstration of multi‐versioning ‐ there are multiple versions of the  same piece of information, all at different points in time, available in the database. Oracle  is able to make use of these ʹsnapshotsʹ of data at different points in time to provide us  with read‐consistent queries and non‐blocking queries.  This read‐consistent view of data is always performed at the SQL statement level, the  results of any single SQL statement are consistent with respect to the point in time they  began. This quality is what makes a statement like the following insert a predictable set of  data:  Expert one-on-one Oracle 57 for x in (select * from t)  loop      insert into t values (x.username, x.user_id, x.created);  end loop;  The result of the SELECT * FROM T is preordained when the query begins execution. The  SELECT will not see any of the new data generated by the INSERT. Imagine if it did ‐ this  statement might be a never‐ending loop. If, as the INSERT generated more rows in  CUSTOMER, the SELECT could ʹseeʹ those newly inserted rows ‐ the above piece of code  would create some unknown number of rows. If the table T started out with 10 rows, we  might end up with 20, 21, 23, or an infinite number of rows in T when we finished. It  would be totally unpredictable. This consistent read is provided to all statements so that  an INSERT such as the following is predicable as well:   insert into t select * from t;  The INSERT statement will with be provided a read‐consistent view of T ‐ it will not see  the rows that it itself just inserted, it will only insert the rows that existed at the time the  INSERT began. Many databases wonʹt even permit recursive statements such as the above  due to the fact that they cannot tell how many rows might actually be inserted.  So, if you are used to the way other databases work with respect to query consistency and  concurrency, or you have never had to grapple with such concepts (no real database  experience), you can now see how understanding how this works will be important to you.  In order to maximize Oracleʹs potential, you need to understand these issues as they  pertain to Oracle ‐ not how they are implemented in other databases.  Database Independence?  By now, you might be able to see where Iʹm going in this section. I have made references  above to other databases and how features are implemented differently in each. With the  exception of some read‐only applications, it is my contention that building a wholly  database‐independent application that is highly scalable is extremely hard ‐ and is in fact  quite impossible unless you know exactly how each database works in great detail.  For example, letʹs revisit our initial resource scheduler example (prior to adding the FOR  UPDATE clause). Letʹs say this application had been developed on a database with an  entirely different locking/concurrency model from Oracle. What Iʹll show here is that if  you migrate your application from one database to another database you will have to  verify that it still works correctly in these different environments.  Letʹs assume that we had deployed the initial resource scheduler application in a database  that employed page‐level locking with blocking reads (reads are blocked by writes) and  there was an index on the SCHEDULES table:   Expert one-on-one Oracle 58 create index schedules_idx on schedules( resource_name, start_time );  Also consider that the business rule was implemented via a database trigger (after the  INSERT had occurred but before the transaction committed we would verify that only our  row existed in the table for that time slot). In a page‐locking system, due to the update of  the index page by RESOURCE_NAME and START_TIME it is very likely that we would  have serialized these transactions. The system would have processed these inserts  sequentially due to the index page being locked (all of the RESOURCE_NAMEs with  START_TIMEs near each other would be on the same page). In that page level locking  database our application would be apparently well behaved ‐ our checks on overlapping  resource allocations would have happened one after the other, not concurrently..  If we migrated this application to Oracle and simply assumed that it would behave in the  same way, we would be in for a shock. On Oracle, which does row level locking and  supplies non‐blocking reads, it appears to be ill behaved. As we saw previously, we had to  use the FOR UPDATE clause to serialize access. Without this clause, two users could  schedule the same resource for the same times. This is a direct consequence of not  understanding how the database we have works in a multi‐user environment.   I have encountered issues such as this many times when an application is being moved  from database A to database B. When an application that worked flawlessly in database A  does not work, or works in an apparently bizarre fashion, on database B, the first thought  is that database B is a ʹbad databaseʹ. The simple truth is that database B just does it  differently ‐ neither database is wrong or ʹbadʹ, they are just different. Knowing and  understanding how they work will help you immensely in dealing with these issues.  For example, very recently I was helping to convert some Transact SQL (the stored  procedure language for SQL Server) into PL/SQL. The developer doing the conversion was  complaining that the SQL queries in Oracle returned the ʹwrongʹ answer. The queries  looked like this:  declare      l_some_variable   varchar2(25);  begin     if ( some_condition )     then         l_some_variable := f( … );     end if;           for x in ( select * from T where x = l_some_variable )     loop          ...  Expert one-on-one Oracle 59 The goal here was to find all of the rows in T where X was Null if some condition was not  met or where x equaled a specific value if some condition was met.  The complaint was that, in Oracle, this query would return no data when  L_SOME_VARIABLE was not set to a specific value (when it was left as Null). In Sybase  or SQL Server, this was not the case ‐ the query would find the rows where X was set to a  Null value. I see this on almost every conversion from Sybase or SQL Server to Oracle.  SQL is supposed to operate under tri‐valued logic and Oracle implements Null values the  way ANSI SQL requires them to be implemented. Under those rules, comparing X to a  Null is neither True or False ‐ it is, in fact, unknown. The following snippet shows what I  mean:   ops$tkyte@ORA8I.WORLD> select * from dual;        D  ‐  X        ops$tkyte@ORA8I.WORLD> select * from dual where null=null;        no rows selected        ops$tkyte@ORA8I.WORLD> select * from dual where null<>null;        no rows selected  This can be confusing the first time you see it ‐ it proves that, in Oracle, Null is neither  equal to nor not equal to Null. SQL Server, by default, does not do it that way: in SQL  Server and sybase, Null is equal to Null. Neither Oracleʹs, sybase nor SQL Serverʹs SQL  processing is wrong ‐ they are just different. Both databases are in fact ANSI compliant  databases but they still work differently. There are ambiguities, backward compatibility  issues, and so on, to be overcome. For example, SQL Server supports the ANSI method of  Null comparison, just not by default (it would break thousands of existing legacy  applications built on that database).  In this case, one solution to the problem was to write the query like this instead:  select *    from t    where ( x = l_some_variable OR (x is null and l_some_variable is NULL ))  However, this leads to another problem. In SQL Server, this query would have used an  index on x. This is not the case in Oracle since a B*Tree index (more on indexing  Expert one-on-one Oracle 60 techniques in Chapter 7) will not index an entirely Null entry. Hence, if you need to find  Null values, B*Tree indexes are not very useful.  What we did in this case, in order to minimize impact on the code, was to assign X some  value that it could never in reality assume. Here, X, by definition, was a positive number ‐  so we chose the number ‐1. Thus, the query became:   select * from t where nvl(x,‐1) = nvl(l_some_variable,‐1)  And we created a function‐based index:  create index t_idx on t( nvl(x,‐1) );  With minimal change, we achieved the same end result. The important points to recognize  from this are that:  • Databases are different. Experience in one will in part carry over to another but you  must be ready for some fundamental differences as well as some very minor  differences.   • Minor differences (such as treatment of Nulls) can have as big an impact as  fundamental differences (such as concurrency control mechanism).   • Being aware of the database and how it works and how its features are  implemented is the only way to overcome these issues.   Developers frequently ask me (usually more than once a day) how to do something  specific in the database. For example, they will ask the question ʹHow do I create a  temporary table in a stored procedure?ʹ I do not answer such questions with a direct  answer ‐ I always respond with a question: ʹWhy do you want to do that?. Many times, the  answer will come back: ʹIn SQL Server we created temporary tables in our stored  procedures and we need to do this in Oracle.ʹ That is what I expected to hear. My response,  then, is easy ‐ ʹyou do not want to create temporary tables in a stored procedure in Oracle  (you only think you do).ʹ That would, in fact, be a very bad thing to do in Oracle. If you  created the tables in a stored procedure in Oracle you would find that:  • Doing DDL is a scalability inhibitor.   • Doing DDL constantly is not fast.   • Doing DDL commits your transaction.   • You would have to use Dynamic SQL in all of your stored procedures in order to  access this table ‐ no static SQL.   • Dynamic SQL in PL/SQL is not as fast or as optimized as static SQL.   The bottom line is that you donʹt want to do it exactly as you did it in SQL Server (if you  even need the temporary table in Oracle at all). You want to do things as they are best  done in Oracle. Just as if you were going the other way from Oracle to SQL Server, you  Expert one-on-one Oracle 61 would not want to create a single table for all users to share for temporary data (that is  how Oracle does it). That would limit scalability and concurrency in those other databases.  All databases are not created equal ‐ they are all very different.  The Impact of Standards  If all databases are SQL92‐compliant, then they must be the same. At least that is the  assumption made many times. In this section I would like to dispel that myth.  SQL92 is an ANSI/ISO standard for databases. It is the successor to the SQL89 ANSI/ISO  standard. It defines a language (SQL) and behavior (transactions, isolation levels, and so  on) that tell you how a database will behave. Did you know that many commercially  available databases are SQL92‐compliant? Did you know that it means very little as far as  query and application portability goes?  Starting with the standard, we will find that the SQL92 standard has four levels:  • Entry‐level ‐ This is the level to which most vendors have complied. This level is a  minor enhancement of the predecessor standard, SQL89. No database vendors have  been certified higher and in fact the National Institute of Standards and Technology  (NIST), the agency that used to certify for SQL‐compliance, does not even certify  anymore. I was part of the team that got Oracle 7.0 NIST‐certified for SQL92 entry‐ level compliance in 1993. An entry level compliant database has the feature set of  Oracle 7.0.   • Transitional ‐ This is approximately ʹhalfwayʹ between entry‐level and  intermediate‐level as far as a feature set goes.   • Intermediate ‐ this adds many features including (not by any means an exhaustive  list):  Dynamic SQL  Cascade DELETE for referential integrity  DATE and TIME data types  Domains  Variable length character strings  A CASE expression  CAST functions between data types   • Full ‐ Adds provisions for (again, not exhaustive):  Expert one-on-one Oracle 62 Connection management  A BIT string data type  Deferrable integrity constraints  Derived tables in the FROM clause  Subqueries in CHECK clauses  Temporary tables   The entry‐level standard does not include features such as outer joins, the new inner join  syntax, and so on. Transitional does specify outer join syntax and inner join syntax.  Intermediate adds more, and Full is, of course all of SQL92. Most books on SQL92 do not  differentiate between the various levels leading to confusion on the subject. They  demonstrate what a theoretical database implementing SQL92 FULL would look like. It  makes it impossible to pick up a SQL92 book, and apply what you see in the book to just  any SQL92 database. For example, in SQL Server the ʹinner joinʹ syntax is supported in  SQL statements, whereas in Oracle it is not. But, they are both SQL92‐compliant databases.  You can do inner joins and outer joins in Oracle, you will just do it differently than in SQL  Server. The bottom line is that SQL92 will not go very far at the entry‐level and, if you use  any of the features of intermediate or higher, you risk not being able to ʹportʹ your  application.  You should not be afraid to make use of vendor‐specific features ‐ after all, you are paying  a lot of money for them. Every database has its own bag of tricks, and we can always find  a way to perform the operation in each database. Use what is best for your current  database, and re‐implement components as you go to other databases. Use good  programming techniques to isolate yourself from these changes. The same techniques are  employed by people writing OS‐portable applications. The goal is to fully utilize the  facilities available to you, but ensure you can change the implementation on a case‐by‐case  basis.  For example, a common function of many database applications is the generation of a  unique key for each row. When you insert the row, the system should automatically  generate a key for you. Oracle has implemented the database object called a SEQUENCE  for this. Informix has a SERIAL data type. Sybase and SQL Server have an IDENTITY type.  Each database has a way to do this. However, the methods are different, both in how you  do it, and the possible outcomes. So, to the knowledgeable developer, there are two paths  that can be pursued:  • Develop a totally database‐independent method of generating a unique key.   Expert one-on-one Oracle 63 • Accommodate the different implementations and use different techniques when  implementing keys in each database.   The theoretical advantage of the first approach is that to move from database to database  you need not change anything. I call it a ʹtheoreticalʹ advantage because the ʹconʹ side of  this implementation is so huge that it makes this solution totally infeasible. What you  would have to do to develop a totally database‐independent process is to create a table  such as:  create table id_table ( id_name varchar(30), id_value number );  insert into id_table values ( ʹMY_KEYʹ, 0 );  Then, in order to get a new key, you would have to execute the following code:  update id_table set id_value = id_value + 1 where id_name = ʹMY_KEYʹ;  select id_value from id_table where id_name = ʹMY_KEYʹ;  Looks simple enough, but the outcome is that only one user at a time may process a  transaction now. We need to update that row to increment a counter, and this will cause  our program to serialize on that operation. At best, one person at a time will generate a  new value for this key. This issue is compounded by the fact that our transaction is much  larger then we have outlined above. The UPDATE and SELECT we have in the example  are only two statements of potentially many other statements that make up our transaction.  We have yet to insert the row into the table with this key we just generated, and do  whatever other work it takes to complete this transaction. This serialization will be a huge  limiting factor in scaling. Think of the ramifications if this technique was used on web sites  that processed orders, and this was how we generated order numbers. There would be no  multi‐user concurrency, so we would be forced to do everything sequentially.  The correct approach to this problem would be to use the best code for each database. In  Oracle this would be (assuming the table that needs the generated primary key is T):   create table t ( pk number primary key, ... );  create sequence t_seq;  create trigger t_trigger before insert on t for each row  begin     select t_seq.nextval into :new.pk from dual;  end;  This will have the effect of automatically, and transparently, assigning a unique key to  each row inserted. The same effect can be achieved in the other databases using their types  ‐ the create tables syntax will be different, the net results will be the same. Here, we have  gone out of our way to use each databases feature to generate a non‐blocking, highly  Expert one-on-one Oracle 64 concurrent unique key, and have introduced no real changes to the application code ‐ all  of the logic is contained in this case in the DDL.  Another example of defensive programming to allow for portability is, once you  understand that each database will implement features in a different way, to layer your access  to the database when necessary. Letʹs say you are programming using JDBC. If all you use  is straight SQL SELECTs, INSERTs, UPDATEs, and DELETEs, you probably do not need a  layer of abstraction. You may very well be able to code the SQL directly in your  application, as long as you limit the constructs you use to those constructs supported by  each of the databases you intend to support. Another approach that is both more portable  and offers better performance, would be to use stored procedures to return resultsets. You  will discover that every vendorʹs database can return resultsets from stored procedures  but how they are returned is different. The actual source code you must write is different  for different databases.  Your two choices here would be to either not use stored procedures to return resultsets, or  to implement different code for different databases. I would definitely follow the ʹdifferent  code for different vendorsʹ method, and use stored procedures heavily. This apparently  seems to increase the amount of time it would take to implement on a different database.  However, you will find it is actually easier to implement on multiple databases with this  approach. Instead of having to find the perfect SQL that works on all databases (perhaps  better on some than on others), you will implement the SQL that works best on that  database. You can do this outside of the application itself, giving you more flexibility in  tuning the application. We can fix a poorly performing query in the database itself, and  deploy that fix immediately, without having to patch the application. Additionally, you  can take advantage of vendor extensions to SQL using this method freely. For example,  Oracle supports hierarchical queries via the CONNECT BY operation in its SQL. This  unique feature is great for resolving recursive queries. In Oracle you are free to utilize this  extension to SQL since it is ʹoutsideʹ of the application (hidden in the database). In other  databases, you would use a temporary table and procedural code in a stored procedure to  achieve the same results, perhaps. You paid for these features so you might as well use  them.   These are the same techniques developers who implement multi‐platform code utilize.  Oracle Corporation for example uses this technique in the development of its own  database. There is a large amount of code (a small percentage of the database code overall)  called OSD (Operating System Dependent) code that is implemented specifically for each  platform. Using this layer of abstraction, Oracle is able to make use of many native OS  features for performance and integration, without having to rewrite the large majority of  the database itself. The fact that Oracle can run as a multi‐threaded application on  Windows and a multi‐process application on UNIX attests to this feature. The mechanisms  for inter‐process communication are abstracted to such a level that they can be re‐ Expert one-on-one Oracle 65 implemented on an OS‐by‐OS basis, allowing for radically different implementations that  perform as well as an application written directly, and specifically, for that platform.  In addition to SQL syntactic differences, implementation differences, and differences in  performance of the same query in different databases outlined above, there are the issues  of concurrency controls, isolation levels, query consistency, and so on. We cover these  items in some detail in Chapter 3, Locking and Concurrency, and Chapter 4, Transactions of  this book, and see how their differences may affect you. SQL92 attempted to give a  straightforward definition of how a transaction should work, how isolation levels are to be  implemented, but in the end, youʹll get different results from different databases. It is all  due to the implementation. In one database an application will deadlock and block all over  the place. In another database, the same exact application will not ‐ it will run smoothly. In  one database, the fact that you did block (physically serialize) was used to your advantage  and when you go to deploy on another database, and it does not block, you get the wrong  answer. Picking an application up and dropping it on another database takes a lot of hard  work and effort, even if you followed the standard 100 percent.  Features and Functions  A natural extension of the argument that you shouldnʹt necessarily strive for ʹdatabase  independenceʹ is the idea that you should understand exactly what your specific database  has to offer and make full use of it. This is not a section on all of the features that Oracle 8i  has to offer. That would be an extremely large book in itself. The new features of Oracle 8i  themselves fill a book in the Oracle documentation set. With about 10,000 pages of  documentation provided by Oracle, covering each and every feature and function would  be quite an undertaking. Rather, this is a section on why it would benefit you to get at  least a cursory knowledge of what is provided.  As Iʹve said before, I answer questions about Oracle on the web. Iʹd say that 80 percent of  my answers are simply URLs to the documentation. People are asking how they might go  about writing some complex piece of functionality in the database (or outside of it). I just  point them to the place in the documentation that tells them how Oracle has already  implemented it, and how to use it. Replication comes up this way frequently. Iʹll receive  the question ʹI would like to keep a copy of my data elsewhere. I would like this to be a  read‐only copy. I need it to update only once a day at midnight. How can I write the code  to do that?ʹ The answer is as simple as a CREATE SNAPSHOT command. This is what  built‐in functionality in the database.  It is true you can write your own replication, it might even be fun to do so, but at the end  of the day, it would not be the smartest thing to do. The database does a lot of stuff. In  general, it can do it better then we can ourselves. Replication for example is internalized in  the kernel, written in C. Itʹs fast, itʹs fairly easy, and it is robust. It works across versions,  Expert one-on-one Oracle 66 across platforms. It is supported, so if you hit a problem, Oracleʹs support team will be  glad to help. If you upgrade, replication will be supported there as well, probably with  some new features. Now, consider if you had developed your own. You would have to  provide support for all of the versions you wanted to support. Inter‐operability between  7.3 and 8.0 and 8.1 and 9.0, and so on ‐ this would be your job. If it ʹbrokeʹ, you wonʹt be  calling support. At least, not until you can get a test case that is small enough to  demonstrate your basic issue. When the new release of Oracle comes out, it will be up to  you to migrate your replication code to that release.   Not having a full understanding of what is available to you can come back to haunt you in  the long run. I was recently talking with some developers and their management. They  were demonstrating a ʹvery coolʹ piece of software they had developed. It was a message‐ based system that solved the database queue problem. You see this normally in a database  if you wanted many people to use a table as a ʹqueueʹ. You would like many people to be  able to lock the next record in the queue, skipping over any previously locked records  (these queue records are being processed already). The problem you encounter is that  there is no documented syntax in the database for skipping locked rows. So, if you didnʹt  know anything about Oracleʹs features, you would assume that if you wanted queuing  software on top of the database, you would have to build it (or buy it).  That is what these developers did. They built a series of processes, and developed APIs for  doing message queuing on top of the database. They spent quite a bit of time on it, and  used quite a few man‐hours to achieve it. The developers were quite sure it was unique.  Immediately after seeing it, and hearing of its functionality, I had one thing to say ‐  Advanced Queues. This is a native feature of the database. It solves the ʹget the first  unlocked record in the queue table and lock it for meʹ problem. It was right there all along.  Their developers, not knowing that this feature existed, spent a lot of time and energy  writing their own. In addition to spending lots of time in the past on it, they would be  spending lots of time maintaining it in the future. Their manager was less than impressed  upon discovering the unique piece of software in effect emulated a native database feature.  I have seen people in an Oracle 8i database set up daemon processes that reads messages  off of pipes (a database IPC mechanism). These daemon processes execute the SQL  contained within the pipe message, and commit the work. They did this so that they could  execute auditing in a transaction that would not get rolled back if the bigger transaction  did. Usually, if a trigger or something were used to audit an access to some data, but a  statement failed later on, all of the work would be rolled back (see Chapter 4 on  Transactions, we discuss this statement level atomicity in some detail). So, by sending a  message to another process, they could have a separate transaction do the work and  commit it. The audit record would stay around, even if the parent transaction rolled back.  In versions of Oracle before Oracle 8I, this was an appropriate (and pretty much the only)  way to implement this functionality. When I told them of the database feature called  autonomous transactions (we will take a detailed look at these in Chapter 15), they were  Expert one-on-one Oracle 67 quite upset with themselves. Autonomous transactions, implemented with a single line of  code, do exactly what they were doing. On the bright side, this meant they could discard a  lot of code and not have to maintain it. In addition, the system ran faster overall, and was  easier to understand. Still, they were upset at the amount of time they had wasted  reinventing the wheel. In particular the developer who wrote the daemon processes was  quite upset at having just written a bunch of ʹshelf‐wareʹ.  The above list of examples is something I see repeated time, and time again ‐ large  complex solutions to problems that are already solved by the database itself. Unless you  take the time to learn what is available, you are doomed to do the same thing at some  point. In the second section of this book, Database Structures and Utilities, we are going to  take an in‐depth look at a handful of functionality provided by the database. I picked and  chose the features and functions that I see people using frequently, or in other cases,  functionality that should be used more often but is not. It is only the tip of the iceberg  however. There is so much more to Oracle than can be presented in a single book.  Solving Problems Simply  There are always two ways to solve everything: the easy way and the hard way. Time and  time again, I see people choosing the hard way. It is not always done consciously. More  usually, it is done out of ignorance. They never expected the database to be able to do ʹthatʹ.  I, on the other hand, expect the database to be capable of anything and only do it the ʹhardʹ  way (by writing it myself) when I discover it cannot do something.   For example, I am frequently asked ʹHow can I make sure the end user has only one  session in the database?ʹ (There are hundreds of other examples I could have used here).  This must be a requirement of many applications but none that Iʹve ever worked on ‐ Iʹve  not found a good reason for limiting people in this way. However, people want to do it  and when they do, they usually do it the hard way. For example, they will have a batch job  run by the operating system that will look at the V$SESSION table and arbitrarily kill  sessions of users who have more then one session. Alternatively, they will create their own  tables and have the application insert a row when a user logs in, and remove the row  when they log out. This implementation invariably leads to lots of calls to the help desk  because when the application ʹcrashesʹ, the row never gets removed. Iʹve seen lots of other  ʹcreativeʹ ways to do this, but none is as easy as:   ops$tkyte@ORA8I.WORLD> create profile one_session limit sessions_per_user 1;  Profile created.        ops$tkyte@ORA8I.WORLD> alter user scott profile one_session;  User altered.        Expert one-on-one Oracle 68 ops$tkyte@ORA8I.WORLD> alter system set resource_limit=true;  System altered.  Thatʹs it ‐ now any user with the ONE_SESSION profile can log on only once. When I  bring up this solution, I can usually hear the smacking of a hand on the forehead followed  by the statement ʹI never knew it could do thatʹ. Taking the time to familiarize yourself  with what the tools you have to work with are capable of doing can save you lots of time  and energy in your development efforts.  The same ʹkeep in simpleʹ argument applies at the broader architecture level. I would urge  people to think carefully before adopting very complex implementations. The more  moving parts you have in your system, the more things you have that can go wrong and  tracking down exactly where that error is occurring in an overly complex architecture is  not easy. It may be really ʹcoolʹ to implement using umpteen tiers, but it is not the right  choice if a simple stored procedure can do it better, faster and with less resources.  Iʹve worked on a project where the application development had been on going for over a  year. This was a web application, to be rolled out to the entire company. The HTML client  talked to JSPs in the middle tier, which talked to CORBA objects, which talked to the  database. The CORBA objects would maintain ʹstateʹ and a connection to the database in  order to maintain a session. During the testing of this system we found that they would  need many front end application servers and a very large database machine to support the  estimated 10,000 concurrent users. Not only that, but stability was an issue at times given  the complex nature of the interaction between the various components (just exactly where  in that stack is the error coming from and why? ‐ that was a hard question to answer). The  system would scale, it would just take a lot of horsepower to do it. Additionally, since the  implementation used a lot of complex technologies ‐ it would require experienced  developers to not only to develop it but to maintain it. We took a look at that system and  what it was trying to do and realized that the architecture was a little more complex than  it needed to be in order to do the job. We saw that simply by using the PL/SQL module of  Oracle iAS and some stored procedures, we could implement the exact system on a  fraction of the hardware, and using less ʹexperiencedʹ developers. No EJBs, no complex  interaction between JSPs and EJBs ‐ just the simple translation of a URL into a stored  procedure call. This new system is still up and running today, exceeding the estimated  user count and with response times that people do not believe. It uses the most basic of  architectures, has the fewest moving pieces, runs on an inexpensive 4‐CPU workgroup  server and never breaks (well a tablespace filled up once, but thatʹs another issue).  I will always go with the simplest architecture that solves the problem completely over a  complex one any day. The payback can be enormous. Every technology has its place ‐ not  every problem is a nail, we can use more than a hammer in our toolbox.   Expert one-on-one Oracle 69 Openness  There is another reason that I frequently see people doing things the hard way and again it  relates to the idea that one should strive for ʹopennessʹ and ʹdatabase independenceʹ at all  costs. The developers wish to avoid using ʹclosedʹ, ʹproprietaryʹ database features ‐ even  something as simple as ʹstored proceduresʹ or ʹsequencesʹ because that will lock them into  a database system. Well, let me put forth the idea that the instant you develop a read/write  application you are already somewhat locked in. You will find subtle (and sometimes not  so subtle) differences between the databases as soon as you start running queries and  modifications. For example, in one database you might find that your SELECT COUNT(*)  FROM T deadlocks with a simple update of two rows. In Oracle, youʹll find that the  SELECT COUNT(*) never blocks for a writer. Weʹve seen the case where a business rule  appears to get enforced on one database, due to side effects of the databaseʹs s locking  model, and does not get enforced in another database. Youʹll find that, given the same  exact transaction mix, reports come out with different answers in different databases ‐ all  because of fundamental implementation differences. You will find that it is a very rare  application that can simply be picked up and moved from one database to another.  Differences in the way the SQL is interpreted (for example, the NULL=NULL example)  and processed will always be there.  On a recent project, the developers were building a web‐based product using Visual Basic,  ActiveX Controls, IIS Server, and the Oracle 8i database. I was told that the development  folks had expressed concern that since the business logic had been written in PL/SQL, the  product had become database dependent and was asked: ʹHow can we correct this?ʹ  I was a little taken aback by this question. In looking at the list of chosen technologies I  could not figure out how being database dependent was a ʹbadʹ thing:  • They had chosen a language that locked them into a single operating system and is  supplied by a single vendor (they could have opted for Java).   • They had chosen a component technology that locked them into a single operating  system and vendor (they could have opted for EJB or CORBA).   • They had chosen a web server that locked them in to a single vendor and single  platform (why not Apache?).   Every other technology choice they had made locked them into a very specific  configuration ‐ in fact the only technology that offered them any choice as far as operating  systems go was in fact the database.  Regardless of this ‐ they must have had good reasons to choose the technologies they did ‐  we still have a group of developers making a conscious decision to not utilize the  functionality of a critical component in their architecture, and doing it in the name of  ʹopennessʹ. It is my belief that you pick your technologies carefully and then you exploit  Expert one-on-one Oracle 70 them to the fullest possible extent. You have paid a lot for these technologies ‐ would it not  be in your best interest to exploit them fully? I had to assume that they were looking  forward to utilizing the full potential of the other technologies ‐ so why was the database  an exception? An even harder question to answer in light of the fact that it was crucial to  their success.  We can put a slightly different spin on this argument if we consider it from the perspective  of ʹopennessʹ. You put all of your data into the database. The database is a very open tool.  It supports data access via SQL, EJBs, HTTP, FTP, SMB, and many other protocols and  access mechanisms. Sounds great so far, the most open thing in the world.   Then, you put all of your application logic and more importantly, your security outside of  the database. Perhaps in your beans that access the data. Perhaps in the JSPs that access  the data. Perhaps in your Visual Basic code running under Microsoftʹs Transaction Server  (MTS). The end result is that you have just closed off your database ‐ you have made it  ʹnon‐openʹ. No longer can people hook in existing technologies to make use of this data ‐  they must use your access methods (or bypass security altogether). This sounds all well  and fine today, but what you must remember is that the ʹwhiz bangʹ technology of today,  EJBs for example, yesterdayʹs concept, and tomorrowʹs old, tired technology. What has  persevered for over 20 years in the relational world (and probably most of the object  implementations as well) is the database itself. The front ends to the data change almost  yearly, and as they do, the applications that have all of the security built inside themselves,  not in the database, become obstacles, roadblocks to future progress.  The Oracle database provides a feature called Fine Grained Access Control (Chapter 21 is  dedicated to it). In a nutshell, this technology allows the developer to embed procedures in  the database that can modify queries as they are submitted to the database. This query  modification is used to restrict the rows the client will receive or modify. The procedure  can look at who is running the query, when they are running the query, what terminal  they are running the query from, and so on, and can constrain access to the data as  appropriate. With FGAC, we can enforce security such that, for example:  • Any query executed outside of normal business hours by a certain class of users  returned zero records.   • Any data could be returned to a terminal in a secure facility but only non‐sensitive  information to a ʹremoteʹ client terminal.   Basically, it allows us to locate access control in the database, right next to the data. It no  longer matters if the user comes at the data from a Bean, a JSP, a VB application using  ODBC, or SQL*PLUS, the same security protocols will be enforced. You are well situated  for the next technology that comes along.  Expert one-on-one Oracle 71 Now, I ask you ‐ which implementation is more ʹopenʹ? The one that makes all access to  the data possible only through calls to the VB code and ActiveX controls (replace VB with  Java and ActiveX with EJB if you like ‐ Iʹm not picking on a particular technology but an  implementation here) or the solution that allows access from anything that can talk to the  database, over protocols as diverse as SSL, HTTP and Net8 (and others) or using APIs  such as ODBC, JDBC, OCI, and so on? I have yet to see an ad‐hoc reporting tool that will  ʹqueryʹ your VB code. I know of dozens that can do SQL, though.  The decision to strive for database independence and total ʹopennessʹ is one that people  are absolutely free to take, and many try, but I believe that it is the wrong decision. No  matter what database you are using, you should exploit it fully, squeezing every last bit of  functionality you can out of that product. Youʹll find yourself doing that in the tuning  phase (which again always seems to happen right after deployment) anyway. It is  amazing how quickly the database independence requirement can be dropped when you  can make the application run five times faster just by exploiting the softwareʹs capabilities.  How Do I Make it Run Faster?  The question in the heading is one I get asked all the time. Everyone is looking for the fast  = true switch, assuming ʹdatabase tuningʹ means that you tune the database. In fact, it is  my experience that more than 80 percent (frequently much more, 100 percent) of all  performance gains are to be realized at the application level ‐ not the database level. You  cannot tune a database until you have tuned the applications that run on the data.   As time goes on there are some switches we can ʹthrowʹ at the database level to help lessen  the impact of egregious programming blunders. For example, Oracle 8.1.6 adds a new  parameter, CURSOR_SHARING=FORCE. This feature implements an ʹauto binderʹ if you  will. It will silently take a query written as SELECT * FROM EMP WHERE EMPNO = 1234  and rewrite it for us as SELECT * FROM EMP WHERE EMPNO = :x. This can dramatically  decrease the number of hard parses, and decrease the library latch waits we discussed in  the Architecture sections ‐ but (there is always a but) it can have some side effects. You  may hit an issue (a.k.a. ʹbugʹ) with regards to this feature, for example in the first release:   ops$tkyte@ORA8I.WORLD> alter session set cursor_sharing=force;  Session altered.        ops$tkyte@ORA8I.WORLD> select * from dual where dummy=ʹXʹand 1=0;  select * from dual where dummy=ʹXʹand 1=0                                          *  ERROR at line 1:  ORA‐00933: SQL command not properly ended        ops$tkyte@ORA8I.WORLD> alter session set cursor_sharing=exact;  Expert one-on-one Oracle 72 Session altered.        ops$tkyte@ORA8I.WORLD> select * from dual where dummy=ʹXʹand 1=0;  no rows selected  The way they rewrote the query (because of the lack of whitespace between ʹXʹ and the  word AND) didnʹt work in 8.1.6. The query ended up being:   select * from dual where dummy=:SYS_B_0and :SYS_B_1=:SYS_B_2;  The key word AND became part of the bind variable :SYS_B_0. In 8.1.7, however, this  query is rewritten as:   select * from dual where dummy=:ʺSYS_B_0ʺand :ʺSYS_B_1ʺ=:ʺSYS_B_2ʺ;  This works syntactically but might negatively affect your programʹs performance. For  example, in the above, notice how 1=0 (also False) is rewritten to be :ʺSYS_B_1ʺ  = :ʺSYS_B_2ʺ. The optimizer no longer has all of the information at parse time, it can no  longer see that this query returns zero rows (before it even executes it). While I donʹt  expect you to have lots of queries with 1=0 in them, I would expect you to have some  queries that do use literals in them on purpose. You may have a column with very skewed  values in it, for example 90 percent of the values of the column are greater than 100, 10  percent are less then 100. Further, 1 percent is less then 50. You would want the query:    select * from t where x < 50;  to use an index, and the query:   select * from t where x > 100;  to not use an index. If you use CURSOR_SHARING = FORCE, the optimizer will not have  the 50 or 100 values to consider when optimizing ‐ hence it will come up with a generic  plan that probably does not use the index (even if 99.9 percent of your queries are of the  type WHERE x < 50).   Additionally, I have found that while CURSOR_SHARING = FORCE runs much faster  than parsing and optimizing lots of unique queries, I have also found it to be slower than  using queries where the developer did the binding. This arises not from any inefficiency in  the cursor sharing code, but rather in inefficiencies in the program itself. In Chapter 10,  Tuning Strategies and Tools, weʹll discover how parsing of SQL queries can affect our  performance. In many cases, an application that does not use bind variables is not  efficiently parsing and reusing cursors either. Since the application believes each query is  unique (it built them as unique statements) it will never use a cursor more than once. The  fact is that if the programmer had used bind variables in the first place, they could have  Expert one-on-one Oracle 73 parsed a query once and reused it many times. It is this overhead of parsing that decreases  the overall potential performance you could see.  Basically, it is important to keep in mind that simply turning on CURSOR_SHARING =  FORCE will not necessarily fix your problems. It may very well introduce new ones.  CURSOR_SHARING is, in some cases, a very useful tool, but it is not a silver bullet. A  well‐developed application would never need it. In the long term, using bind variables  where appropriate, and constants when needed, is the correct approach.  Even if there are some switches that can be thrown at the database level, and they are truly  few and far between, problems relating to concurrency issues and poorly executing  queries (due to poorly written queries or poorly structured data) cannot be fixed with a  switch. These situations require rewrites (and frequently a re‐architecture). Moving  datafiles around, changing the multi‐block read count, and other ʹdatabaseʹ level switches  frequently have a minor impact on the overall performance of an application. Definitely  not anywhere near the 2, 3, ... N times increase in performance you need to achieve to  make the application acceptable. How many times has your application been 10 percent  too slow? 10 percent too slow, no one complains about. Five times too slow, people get  upset. I repeat: you will not get a 5‐times increase in performance by moving datafiles  around. You will only achieve this by fixing the application ‐ perhaps by making it do  significantly less I/O.  Performance is something you have to design for, to build to, and to test for continuously  throughout the development phase. It should never be something to be considered after  the fact. I am amazed at how many times people wait until the application has been  shipped to their customer, put in place and is actually running before they even start to  tune it. Iʹve seen implementations where applications are shipped with nothing more than  primary keys ‐ no other indexes whatsoever. The queries have never been tuned or stress  tested. The application has never been tried out with more than a handful of users. Tuning  is considered to be part of the installation of the product. To me, that is an unacceptable  approach. Your end users should be presented with a responsive, fully tuned system from  day one. There will be enough ʹproduct issuesʹ to deal with without having poor  performance be the first thing they experience. Users are expecting a few ʹbugsʹ from a  new application, but at least donʹt make them wait a painfully long time for them to  appear on screen.  The DBA‐Developer Relationship  The back cover of this book talks of the importance of a DBA knowing what the  developers are trying to accomplish and of developers knowing how to exploit the DBAʹs  data management strategies. Itʹs certainly true that the most successful information  systems are based on a symbiotic relationship between the DBA and the application  developer. In this section I just want to give a developerʹs perspective on the division of  Expert one-on-one Oracle 74 work between developer and DBA (assuming that every serious development effort has a  DBA team).  As a developer, you should not necessarily have to know how to install and configure the  software. That should be the role of the DBA and perhaps the SA (System Administrator).  Setting up Net8, getting the listener going, configuring MTS, enabling connection pooling,  installing the database, creating the database, and so on ‐ these are functions I place in the  hands of the DBA/SA.   In general, a developer should not have to know how to tune the operating system. I  myself generally leave this task to the SAs for the system. As a software developer for  database applications you will need to be competent in use of your operating system of  choice, but you shouldnʹt expect to have to tune it.  Perhaps one of the biggest concerns of the DBA is how to back up and restore a database,  and I would say that this is the sole responsibility of the DBA. Understanding how  rollback and redo work ‐ yes, that is something a developer has to know. Knowing how to  perform a tablespace point in time recovery is something a developer can skip over.  Knowing that you can do it might come in handy, but actually having to do it ‐ no.  Tuning at the database instance level, figuring out what the optimum SORT_AREA_SIZE  should be ‐ thatʹs typically the job of the DBA. There are exceptional cases where a  developer might need to change some setting for a session, but at the database level, the  DBA is responsible for that. A typical database supports more than just a single  developerʹs application. Only the DBA who supports all of the applications can make the  right decision.  Allocating space and managing the files is the job of the DBA. Developers will contribute  their estimations for space (how much they feel they will need) but the DBA/SA will take  care of the rest.  Basically, developers do not need to know how to run the database. They need to know  how to run in the database. The developer and the DBA will work together on different  pieces of the same puzzle. The DBA will be visiting you, the developer, when your queries  are consuming too many resources, and you will be visiting them when you cannot figure  out how to make the system go any faster (thatʹs when instance tuning can be done, when  the application is fully tuned).  This will all vary by environment, but I would like to think that there is a division. A good  developer is usually a very bad DBA, and vice versa. They are two different skillsets, two  different mindsets, and two different personalities in my opinion.  Summary  Expert one-on-one Oracle 75 Here we have taken a somewhat anecdotal look at why you need to know the database.  The examples I have given are not isolated ‐ they happen every day, day in and day out. I  observe a continuous cycle of this happening over and over again and again. Letʹs quickly  recap the key points. If you are developing with Oracle:  • You need to understand the Oracle architecture. You donʹt have to know it so well  that you are able to rewrite the server if you wanted but you should know it well  enough that you are aware of the implications of using a particular feature.   • You need to understand locking and concurrency control and that every database  implements this differently. If you donʹt, your database will give ʹwrongʹ answers  and you will have large contention issues ‐ leading to poor performance.   • Do not treat the database as a black box, something you need not understand. The  database is the most critical piece of most applications. To try to ignore it would be  fatal.   • Do not re‐invent the wheel. Iʹve seen more then one development team get in  trouble, not only technically but on a personal level, due to a lack of awareness  what Oracle provides for free. This will happen when it is pointed out that the  feature they just spent the last couple of months implementing was actually a core  feature of the database all along.   • Solve problems as simply as possible, using as much of Oracleʹs built‐in  functionality as possible. You paid a lot for it.   • Software projects come and go, programming languages and frameworks come and  go. We developers are expected to have systems up and running in weeks, maybe  months, and then move on to the next problem. If we re‐invent the wheel over and  over, we will never come close to keeping up with the frantic pace of development.  Just as you would never build your own hash table class in Java ‐ since it comes  with one ‐ you should use the database functionality you have at your disposal. The  first step to being able to do that, of course, is to understand what it is you have at  your disposal. Read on.   Expert one-on-one Oracle 76 Chapter 2: Architecture  Overview  Oracle is designed to be a very portable database ‐ it is available on every platform of  relevance. For this reason, the physical architecture of Oracle looks different on different  operating systems. For example, on a UNIX operating system, you will see Oracle  implemented as many different operating system processes, virtually a process per major  function. On UNIX, this is the correct implementation, as it works on a multi‐process  foundation. On Windows however, this architecture would be inappropriate and would  not work very well (it would be slow and non‐scaleable). On this platform, Oracle is  implemented as a single, threaded process, which is the appropriate implementation  mechanism on this platform. On IBM mainframe systems running OS/390 and zOS, the  Oracle operating system‐specific architecture exploits multiple OS/390 address spaces, all  operating as a single Oracle instance. Up to 255 address spaces can be configured for a  single database instance. Moreover, Oracle works together with OS/390 WorkLoad  Manager (WLM) to establish execution priority of specific Oracle workloads relative to  each other, and relative to all other work in the OS/390 system. On Netware we are back to  a threaded model again. Even though the physical mechanisms used to implement Oracle  from platform to platform vary, the architecture is sufficiently generalized enough that  you can get a good understanding of how Oracle works on all platforms.  In this chapter, we will look at the three major components of the Oracle architecture:  • Files ‐ we will go through the set of five files that make up the database and the  instance. These are the parameter, data, temp, and redo log Files.   • Memory structures, referred to as the System Global Area (SGA) ‐ we will go  through the relationships between the SGA, PGA, and UGA. Here we also go  through the Java pool, shared pool, and large pool parts of the SGA.   • Physical processes or threads ‐ we will go through the three different types of  processes that will be running on the database: server processes, background  processes, and slave processes.   The Server  It is hard to decide which of these components to cover first. The processes use the SGA,  so discussing the SGA before the processes might not make sense. On the other hand, by  discussing the processes and what they do, Iʹll be making references to the SGA. The two  are very tied together. The files are acted on by the processes and would not make sense  without understanding what the processes do. What I will do is define some terms and  Expert one-on-one Oracle 77 give a general overview of what Oracle looks like (if you were to draw it on a whiteboard)  and then weʹll get into some of the details.  There are two terms that, when used in an Oracle context, seem to cause a great deal of  confusion. These terms are ʹinstanceʹ and ʹdatabaseʹ. In Oracle terminology, the definitions  would be:  • Database ‐ A collection of physical operating system files   • Instance ‐ A set of Oracle processes and an SGA   The two are sometimes used interchangeably, but they embrace very different concepts.  The relationship between the two is that a database may be mounted and opened by  many instances. An instance may mount and open a single database at any point in time.  The database that an instance opens and mounts does not have to be the same every time  it is started.  Confused even more? Here are some examples that should help clear it up. An instance is  simply a set of operating system processes and some memory. They can operate on a  database, a database just being a collection of files (data files, temporary files, redo log files,  control files). At any time, an instance will have only one set of files associated with it. In  most cases, the opposite is true as well; a database will have only one instance working on  it. In the special case of Oracle Parallel Server (OPS), an option of Oracle that allows it to  function on many computers in a clustered environment, we may have many instances  simultaneously mounting and opening this one database. This gives us access to this  single database from many different computers at the same time. Oracle Parallel Server  provides for extremely highly available systems and, when implemented correctly,  extremely scalable solutions. In general, OPS will be considered out of scope for this book,  as it would take an entire book to describe how to implement it.  So, in most cases, there is a one‐to‐one relationship between an instance and a database.  This is how the confusion surrounding the terms probably arises. In most peoplesʹ  experience, a database is an instance, and an instance is a database.  In many test environments, however, this is not the case. On my disk, I might have, five  separate databases. On the test machine, I have Oracle installed once. At any point in time  there is only one instance running, but the database it is accessing may be different from  day to day or hour to hour, depending on my needs. By simply having many different  configuration files, I can mount and open any one of these databases. Here, I have one  ʹinstanceʹ but many databases, only one of which is accessible at any point in time.  So now when someone talks of the instance, you know they mean the processes and  memory of Oracle. When they mention the database, they are talking of the physical files  that hold the data. A database may be accessible from many instances, but an instance will  provide access to exactly one database at a time.   Expert one-on-one Oracle 78 Now we might be ready for an abstract picture of what Oracle looks like:    In its simplest form, this is it. Oracle has a large chunk of memory call the SGA where it  will: store many internal data structures that all processes need access to; cache data from  disk; cache redo data before writing it to disk; hold parsed SQL plans, and so on. Oracle  has a set of processes that are ʹattachedʹ to this SGA and the mechanism by which they  attach differs by operating system. In a UNIX environment, they will physically attach to a  large shared memory segment ‐ a chunk of memory allocated in the OS that may be  accessed by many processes concurrently. Under Windows, they simply use the C call  malloc() to allocate the memory, since they are really threads in one big process. Oracle  will also have a set of files that the database processes/threads read and write (and Oracle  processes are the only ones allowed to read or write these files). These files will hold all of  our table data, indexes, temporary space, redo logs, and so on.  If you were to start up Oracle on a UNIX‐based system and execute a ps (process status)  command, you would see that many physical processes are running, with various names.  For example:   $ /bin/ps ‐aef | grep ora816    ora816 20827     1  0   Feb 09 ?        0:00 ora_d000_ora816dev    ora816 20821     1  0   Feb 09 ?        0:06 ora_smon_ora816dev    ora816 20817     1  0   Feb 09 ?        0:57 ora_lgwr_ora816dev    ora816 20813     1  0   Feb 09 ?        0:00 ora_pmon_ora816dev    ora816 20819     1  0   Feb 09 ?        0:45 ora_ckpt_ora816dev    ora816 20815     1  0   Feb 09 ?        0:27 ora_dbw0_ora816dev    ora816 20825     1  0   Feb 09 ?        0:00 ora_s000_ora816dev    ora816 20823     1  0   Feb 09 ?        0:00 ora_reco_ora816dev  I will cover what each of these processes are, but they are commonly referred to as the  Oracle background processes. They are persistent processes that make up the instance  and you will see them from the time you start the database, until the time you shut it  Expert one-on-one Oracle 79 down. It is interesting to note that these are processes, not programs. There is only one  Oracle program on UNIX; it has many personalities. The same ʹprogramʹ that was run to  get ora_lgwr_ora816dev, was used to get the process ora_ckpt_ora816dev. There is only  one binary, named simply oracle. It is just executed many times with different names. On  Windows, using the tlist tool from the Windows resource toolkit, I will find only one  process, Oracle.exe. Again, on NT there is only one binary program. Within this process,  weʹll find many threads representing the Oracle background processes. Using tlist (or any  of a number of tools) we can see these threads:   C:\Documents and Settings\Thomas Kyte\Desktop>tlist 1072  1072 ORACLE.EXE     CWD:     C:\oracle\DATABASE\     CmdLine: c:\oracle\bin\ORACLE.EXE TKYTE816     VirtualSize:   144780 KB   PeakVirtualSize:   154616 KB     WorkingSetSize: 69424 KB   PeakWorkingSetSize: 71208 KB     NumberOfThreads: 11        0 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized        5 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized        5 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized        5 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized        5 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized        5 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized        5 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized        5 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized        5 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized        5 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized        5 Win32StartAddr:0x00000000 LastErr:0x00000000 State:Initialized          0.0.0.0 shp  0x00400000  ORACLE.EXE       5.0.2163.1 shp  0x77f80000  ntdll.dll          0.0.0.0 shp  0x60400000  oraclient8.dll          0.0.0.0 shp  0x60600000  oracore8.dll          0.0.0.0 shp  0x60800000  oranls8.dll  ...  Here, there are eleven threads executing inside the single process Oracle. If I were to log  into this database, I would see the thread count jump to twelve. On UNIX, I would  probably see another process get added to the list of oracle processes running. This brings  us to the next iteration of the diagram. The previous diagram gave a conceptual depiction  of what Oracle would look like immediately after starting. Now, if we were to connect to  Oracle in its most commonly used configuration, we would see something like:  Expert one-on-one Oracle 80   Typically, Oracle will create a new process for me when I log in. This is commonly  referred to as the dedicated server configuration, since a server process will be dedicated  to me for the life of my session. For each session, a new dedicated server will appear in a  one‐to‐one mapping. My client process (whatever program is trying to connect to the  database) will be in direct contact with this dedicated server over some networking  conduit, such as a TCP/IP socket. It is this server that will receive my SQL and execute it  for me. It will read data files, it will look in the databaseʹs cache for my data. It will  perform my update statements. It will run my PL/SQL code. Its only goal is to respond to  the SQL calls that I submit to it.   Oracle may also be executing in a mode called multi‐threaded server (MTS) in which we  would not see an additional thread created, or a new UNIX process appear. In MTS mode,  Oracle uses a pool of ʹshared serversʹ for a large community of users. Shared servers are  simply a connection pooling mechanism. Instead of having 10000 dedicated servers (thatʹs  a lot of processes or threads) for 10000 database sessions, MTS would allow me to have a  small percentage of this number of shared servers, which would be (as their name implies)  shared by all sessions. This allows Oracle to connect many more users to the database than  would otherwise be possible. Our machine might crumble under the load of managing  10000 processes, but managing 100 or 1000 processes would be doable. In MTS mode, the  shared server processes are generally started up with the database, and would just appear  in the ps list (in fact in my previous ps list, the process ora_s000_ora816dev is a shared  server process).  A big difference between MTS mode and dedicated server mode is that the client process  connected to the database never talks directly to a shared server, as it would to a dedicated  server. It cannot talk to a shared server since that process is in fact shared. In order to  share these processes we need another mechanism through which to ʹtalkʹ. Oracle employs  a process (or set of processes) called dispatchers for this purpose. The client process will  talk to a dispatcher process over the network. The dispatcher process will put the clientʹs  request into a request queue in the SGA (one of the many things the SGA is used for). The  first shared server that is not busy will pick up this request, and process it (for example,  the request could be UPDATE T SET X = X+5 WHERE Y = 2. Upon completion of this  command, the shared server will place the response in a response queue. The dispatcher  Expert one-on-one Oracle 81 process is monitoring this queue and upon seeing a result, will transmit it to the client.  Conceptually, the flow of an MTS request looks like this:    The client connection will send a request to the dispatcher. The dispatcher will first place  this request onto the request queue in the SGA (1). The first available shared server will  dequeue this request (2) and process it. When the shared server completes, the response  (return codes, data, and so on) is placed into the response queue (3) and subsequently  picked up by the dispatcher (4), and transmitted back to the client.  As far as the developer is concerned, there is no difference between a MTS connection and  a dedicated server connection. So, now that we understand what dedicated server and  shared server connections are, this begs the questions, ʹHow do we get connected in the  first place?ʹ and ʹWhat is it that would start this dedicated server?ʹ and ʹHow might we get  in touch with a dispatcher?ʹ The answers depend on our specific platform, but in general,  it happens as described below.  We will investigate the most common case ‐ a network based connection request over a  TCP/IP connection. In this case, the client is situated on one machine, and the server  resides on another machine, the two being connected on a TCP/IP network. It all starts  with the client. It makes a request to the Oracle client software to connect to database. For  example, you issue:  C:\> sqlplus scott/tiger@ora816.us.oracle.com  Here the client is the program SQL*PLUS, scott/tiger is my username and password, and  ora816.us.oracle.com is a TNS service name. TNS stands for Transparent Network  Substrate and is ʹfoundationʹ software built into the Oracle client that handles our remote  connections ‐ allowing for peer‐to‐peer communication. The TNS connection string tells  the Oracle software how to connect to the remote database. Generally, the client software  running on your machine will read a file called TNSNAMES.ORA. This is a plain text  configuration file commonly found in the [ORACLE_HOME]\network\admin directory  that will have entries that look like:   ORA816.US.ORACLE.COM =    (DESCRIPTION =      (ADDRESS_LIST =  Expert one-on-one Oracle 82       (ADDRESS = (PROTOCOL = TCP)(HOST = aria.us.oracle.com)(PORT = 1521))      )      (CONNECT_DATA =        (ORACLE_SID = ora816)      )    )  It is this configuration information that allows the Oracle client software to turn  ora816.us.oracle.com into something useful ‐ a hostname, a port on that host that a  ʹlistenerʹ process will accept connections on, the SID (Site IDentifier) of the database on  the host to which we wish to connect, and so on. There are other ways that this string,  ora816.us.oracle.com, could have been resolved. For example it could have been resolved  using Oracle Names, which is a distributed name server for the database, similar in  purpose to DNS for hostname resolution. However, use of the TNSNAMES.ORA is  common in most small to medium installations where the number of copies of such a  configuration file is manageable.  Now that the client software knows where to connect to, it will open a TCP/IP socket  connection to the machine aria.us.oracle.com on the port 1521. If the DBA for our server  has setup Net8, and has the listener running, this connection may be accepted. In a  network environment, we will be running a process called the TNS Listener on our server.  This listener process is what will get us physically connected to our database. When it  receives the inbound connection request, it inspects the request and, using its own  configuration files, either rejects the request (no such database for example, or perhaps our  IP address has been disallowed connections to this host) or accepts it and goes about  getting us connected.   If we are making a dedicated server connection, the listener process will create a dedicated  server for us. On UNIX, this is achieved via a fork() and exec() system call (the only way to  create a new process after initialization in UNIX is fork()). We are now physically  connected to the database. On Windows, the listener process requests the database process  to create a new thread for a connection. Once this thread is created, the client is ʹredirectedʹ  to it, and we are physically connected. Diagrammatically in UNIX, it would look like this:    Expert one-on-one Oracle 83 On the other hand, if we are making a MTS connection request, the listener will behave  differently. This listener process knows the dispatcher(s) we have running on the database.  As connection requests are received, the listener will choose a dispatcher process from the  pool of available dispatchers. The listener will send back to the client the connection  information describing how the client can connect to the dispatcher process. This must be  done because the listener is running on a well‐known hostname and port on that host, but  the dispatchers will be accepting connections on ʹrandomly assignedʹ ports on that server.  The listener is aware of these random port assignments and picks a dispatcher for us. The  client then disconnects from the listener and connects directly to the dispatcher. We now  have a physical connection to the database. Graphically this would look like this:    So, that is an overview of the Oracle architecture. Here, we have looked at what an Oracle  instance is, what a database is, and how you can get connected to the database either  through a dedicated server, or a shared server. The following diagram sums up what  weʹve seen so far; showing the interaction between the a client using a shared server (MTS)  connection and a client using a dedicated server connection. It also shows that an Oracle  instance may use both connection types simultaneously:    Now we are ready to take a more in‐depth look at the processes behind the server, what  they do, and how they interact with each other. We are also ready to look inside the SGA  to see what is in there, and what its purpose it. We will start by looking at the types of files  Oracle uses to manage the data and the role of each file type.  Expert one-on-one Oracle 84 The Files  We will start with the five types of file that make up a database and instance. The files  associated with an instance are simply:  • Parameter files ‐ These files tell the Oracle instance where to find the control files.  For example, how big certain memory structures are, and so on.   The files that make up the database are:  • Data files ‐ For the database (these hold your tables, indexes and all other  segments).   • Redo log files ‐ Our transaction logs.   • Control files ‐ Which tell us where these data files are, and other relevant  information about their state.   • Temp files ‐ Used for disk‐based sorts and temporary storage.   • Password files ‐ Used to authenticate users performing administrative activities  over the network. We will not discuss these files in any detail.   The most important files are the first two, because they contain the data you worked so  hard to accumulate. I can lose any and all of the remaining files and still get to my data. If I  lose my redo log files, I may start to lose some data. If I lose my data files and all of their  backups, Iʹve definitely lost that data forever.  We will now take a look at the types of files and what we might expect to find in them.  Parameter Files  There are many different parameter files associated with an Oracle database, from a  TNSNAMES.ORA file on a client workstation (used to ʹfindʹ a server as shown above), to a  LISTENER.ORA file on the server (for the Net8 listener startup), to the SQLNET.ORA,  PROTOCOL.ORA, NAMES.ORA, CMAN.ORA, and LDAP.ORA files. The most important  parameter file however, is the databases parameter file for without this, we cannot even  get a database started. The remaining files are important; all of them are related to  networking and getting connected to the database. However, they are out of the scope for  our discussion. For information on their configuration and setup, I would refer you to the  Oracle Net8 Administrators Guide. Typically as a developer, these files would be set up for  you, not by you.  The parameter file for a database is commonly known as an init file, or an init.ora file.  This is due to its default name, which is init.ora. For example, a database  with a SID of tkyte816 will have an init file named, inittkyte816.ora. Without a parameter  file, you cannot start an Oracle database. This makes this a fairly important file. However,  Expert one-on-one Oracle 85 since it is simply a plain text file, which you can create with any text editor, it is not a file  you have to guard with your life.  For those of you unfamiliar with the term SID or ORACLE_SID, a full definition is called  for. The SID is a site identifier. It and ORACLE_HOME (where the Oracle software is  installed) are hashed together in UNIX to create a unique key name for attaching an SGA.  If your ORACLE_SID or ORACLE_HOME is not set correctly, youʹll get ORACLE NOT  AVAILABLE error, since we cannot attach to a shared memory segment that is identified  by magic key. On Windows, we donʹt use shared memory in the same fashion as UNIX,  but the SID is still important. We can have more than one database on the same  ORACLE_HOME, so we need a way to uniquely identify each one, along with their  configuration files.   The Oracle init.ora file is a very simple file in its construction. It is a series of variable  name/value pairs. A sample init.ora file might look like this:   db_name = ʺtkyte816ʺ        db_block_size = 8192        control_files = (ʺC:\oradata\control01.ctlʺ, ʺC:\oradata\control02.ctlʺ)  In fact, this is pretty close to the minimum init.ora file you could get away with. If I had a  block size that was the default on my platform (the default block size varies by platform), I  could remove that. The parameter file is used at the very least to get the name of the  database, and the location of the control files. The control files tell Oracle the location  every other file, so they are very important to the ʹbootstrapʹ process of starting the  instance.  The parameter file typically has many other configuration settings in it. The number of  parameters and their names vary from release to release. For example in Oracle 8.1.5, there  was a parameter plsql_load_without_compile. It was not in any prior release, and is not to  be found in any subsequent release. On my 8.1.5, 8.1.6, and 8.1.7 databases I have 199, 201,  and 203 different parameters, respectively, that I may set. Most parameters like  db_block_size, are very long lived (they wonʹt go away from release to release) but over  time many other parameters become obsolete as implementations change. If you would  like to review these parameters and get a feeling for what is available and what they do,  you should refer to the Oracle8i Reference manual. In the first chapter of this document, it  goes over each and every documented parameter in detail.  Notice I said ʹdocumentedʹ in the preceding paragraph. There are undocumented  parameters as well. You can tell an undocumented parameter from a documented one in  that all undocumented parameters begin with an underscore. There is a great deal of  Expert one-on-one Oracle 86 speculation about these parameters ‐ since they are undocumented, they must be ʹmagicʹ.  Many people feel that these undocumented parameters are well known and used by  Oracle insiders. I find the opposite to be true. They are not well known and are hardly  ever used. Most of these undocumented parameters are rather boring actually,  representing deprecated functionality and backwards compatibility flags. Others help in  the recovery of data, not of the database itself, they enable the database to start up in  certain extreme circumstances, but only long enough to get data out, you have to rebuild  after that. Unless directed to by support, there is no reason to have an undocumented  init.ora parameter in your configuration. Many have side effects that could be devastating.  In my development database, I use only one undocumented setting:   _TRACE_FILES_PUBLIC = TRUE  This makes trace files readable by all, not just the DBA group. On my development  database, I want my developers to use SQL_TRACE, TIMED_STATISTICS, and the  TKPROF utility frequently (well, I demand it actually); hence they need to be able to read  the trace files. In my real database, I donʹt use any undocumented settings.  In general, you will only use these undocumented parameters at the request of Oracle  Support. The use of them can be damaging to a database and their implementation can,  and will, change from release to release.  Now that we know what database parameter files are and where to get more details about  the valid parameters that we can set, the last thing we need to know is where to find them  on disk. The naming convention for this file by default is:   init$ORACLE_SID.ora    (Unix environment variable)  init%ORACLE_SID%.ora   (Windows environment variable)  And by default they will be found in  $ORACLE_HOME/dbs       (Unix)  %ORACLE_HOME%\DATABASE (Windows)  It is interesting to note that, in many cases, you will find the entire contents of this  parameter file to be something like:  IFILE=ʹC:\oracle\admin\tkyte816\pfile\init.oraʹ  The IFILE directive works in a fashion similar to a #include in C. It includes in the current  file, the contents of the named file. The above includes an init.ora file from a non‐default  location.  Expert one-on-one Oracle 87 It should be noted that the parameter file does not have to be in any particular location.  When starting an instance, you may use the startup pfile = filename. This is most useful  when you would like to try out different init.ora parameters on your database to see the  effects of having different settings.  Data Files  Data files, along with redo log files, are the most important set of files in the database. This  is where all of your data will ultimately be stored. Every database has at least one data file  associated with it, and typically will have many more than one. Only the most simple ʹtestʹ  database will have one file. Any real database will have at least two ‐ one for the SYSTEM  data, and one for USER data. What we will discuss in this section is how Oracle organizes  these files, and how data is organized within them. In order to understand this we will  have to understand what a tablespace, segment, extent, and block are. These are the units  of allocation that Oracle uses to hold objects in the database.  We will start with segments. Segments are simply your database objects that consume  storage ‐ objects such as tables, indexes, rollback segments, and so on. When you create a  table, you are creating a table segment. When you create a partitioned table ‐ you create a  segment per partition. When you create an index, you create an index segment, and so on.  Every object that consumes storage is ultimately stored in a single segment. There are  rollback segments, temporary segments, cluster segments, index segments, and so on.  Segments themselves consist of one or more extent. An extent is a contiguous allocation of  space in a file. Every segment starts with at least one extent and some objects may require  at least two (rollback segments are an example of a segment that require at least two  extents). In order for an object to grow beyond its initial extent, it will request another  extent be allocated to it. This second extent will not necessarily be right next to the first  extent on disk, it may very well not even be allocated in the same file as the first extent. It  may be located very far away from it, but the space within an extent is always contiguous  in a file. Extents vary in size from one block to 2 GB in size.   Extents, in turn, consist of blocks. A block is the smallest unit of space allocation in Oracle.  Blocks are where your rows of data, or index entries, or temporary sort results will be  stored. A block is what Oracle generally reads and writes from and to disk. Blocks in  Oracle are generally one of three common sizes ‐ 2 KB, 4 KB, or 8 KB (although 16 KB and  32 KB are also permissible). The relationship between segments, extents, and blocks looks  like this:  Expert one-on-one Oracle 88   A segment is made up of one or more extents ‐ an extent is a contiguous allocation of  blocks.  The block size for a database is a constant once the database is created ‐ each and every  block in the database will be the same size. All blocks have the same general format, which  looks something like this:    The block header contains information about the type of block (a table block, index block,  and so on), transaction information regarding active and past transactions on the block,  and the address (location) of the block on the disk. The table directory, if present, contains  information about the tables that store rows in this block (data from more than one table  may be stored on the same block). The row directory contains information describing the  rows that are to be found on the block. This is an array of pointers to where the rows are to  be found in the data portion of the block. These three pieces of the block are collectively  known as the block overhead ‐ space used on the block that is not available for your data,  but rather is used by Oracle to manage the block itself. The remaining two pieces of the  block are rather straightforward ‐ there will possibly be free space on a block and then  there will generally be used space that is currently storing data.   Now that we have a cursory understanding of segments, which consist of extents, which  consist of blocks, we are ready to see what a tablespace is, and then how files fit into the  picture. A tablespace is a container ‐ it holds segments. Each and every segment belongs to  exactly one tablespace. A tablespace may have many segments within it. All of the extents  for a given segment will be found in the tablespace associated with that segment.  Segments never cross tablespace boundaries. A tablespace itself has one or more data files  Expert one-on-one Oracle 89 associated with it. An extent for any given segment in a tablespace will be contained  entirely within one data file. However, a segment may have extents from many different  data files. Graphically it might look like this:    So, here we see tablespace named USER_DATA. It consists of two data files, user_data01,  and user_data02. It has three segments allocated it, T1, T2, and I1 (probably two tables and  an index). The tablespace has four extents allocated in it and each extent is depicted as a  contiguous set of database blocks. Segment T1 consists of two extents, one extent in each  file. Segment T2 and I1 each have one extent depicted. If we needed more space in this  tablespace, we could either resize the data files already allocated to the tablespace, or we  could add a third data file to it.  Tablespaces are a logical storage container in Oracle. As developers we will create  segments in tablespaces. We will never get down to the raw ʹfile levelʹ ‐ we do not specify  that we want our extents to be allocated in a specific file. Rather, we create objects in  tablespaces and Oracle takes care of the rest. If at some point in the future, the DBA  decides to move our data files around on disk to more evenly distribute I/O ‐ that is OK  with us. It will not affect our processing at all.  In summary, the hierarchy of storage in Oracle is as follows:  1. A database is made up of one or more tablespaces.   2. A tablespace is made up of one or more data files. A tablespace contains segments.   3. A segment (TABLE, INDEX, and so on) is made up of one or more extents. A  segment exists in a tablespace, but may have data in many data files within that  tablespace.   4. An extent is a contiguous set of blocks on disk. An extent is in a single tablespace  and furthermore, is always in a single file within that tablespace.   5. A block is the smallest unit of allocation in the database. A block is the smallest unit  of I/O used by a database.   Before we leave this topic of data files, we will look at one more topic to do with  tablespaces. We will look at how extents are managed in a tablespace. Prior to Oracle 8.1.5,  there was only one method to manage the allocation of extents within a tablespace. This  method is called a dictionary‐managed tablespace. That is, the space within a tablespace  was managed in data dictionary tables, much in the same way you would manage  Expert one-on-one Oracle 90 accounting data, perhaps with a DEBIT and CREDIT table. On the debit side, we have all  of the extents allocated to objects. On the credit side, we have all of the free extents  available for use. When an object needed another extent, it would ask the system to get  one. Oracle would then go to its data dictionary tables, run some queries, find the space  (or not), and then update a row in one table (or remove it all together), and insert a row  into another. Oracle managed space in very much the same way you will write your  applications by modifying data, and moving it around.  This SQL, executed on your behalf in the background to get the additional space, is  referred to as recursive SQL. Your SQL INSERT statement caused other recursive SQL to  be executed to get more space. This recursive SQL could be quite expensive, if it is done  frequently. Such updates to the data dictionary must be serialized; they cannot be done  simultaneously. They are something to be avoided.   In earlier releases of Oracle, we would see this space management issue, this recursive  SQL overhead, most often occurred in temporary tablespaces (this is before the  introduction of ʹrealʹ temporary tablespaces). Space would frequently be allocated (we  have to delete from one dictionary table and insert into another) and de‐allocated (put the  rows we just moved back where they were initially). These operations would tend to  serialize, dramatically decreasing concurrency, and increasing wait times. In version 7.3,  Oracle introduced the concept of a temporary tablespace to help alleviate this issue. A  temporary tablespace was one in which you could create no permanent objects of your  own. This was fundamentally the only difference; the space was still managed in the data  dictionary tables. However, once an extent was allocated in a temporary tablespace, the  system would hold onto it (would not give the space back). The next time someone  requested space in the temporary tablespace for any purpose, Oracle would look for an  already allocated extent in its memory set of allocated extents. If it found one there, it  would simply reuse it, else it would allocate one the old fashioned way. In this fashion,  once the database had been up and running for a while, the temporary segment would  appear full but would actually just be ʹallocatedʹ. The free extents were all there; they were  just being managed differently. When someone needed temporary space, Oracle would  just look for that space in an in‐memory data structure, instead of executing expensive,  recursive SQL.  In Oracle 8.1.5 and later, Oracle goes a step further in reducing this space management  overhead. They introduced the concept of a locally managed tablespace as opposed to a  dictionary managed one. This effectively does for all tablespaces, what Oracle7.3 did for  temporary tablespaces ‐ it removes the need to use the data dictionary to manage space in  a tablespace. With a locally managed tablespace, a bitmap stored in each data file is used  to manage the extents. Now, to get an extent all the system needs to do is set a bit to 1 in  the bitmap. To free space ‐ set it back to 0. Compared to using dictionary‐managed  tablespaces, this is incredibly fast. We no longer serialize for a long running operation at  the database level for space requests across all tablespaces. Rather, we serialize at the  Expert one-on-one Oracle 91 tablespace level for a very fast operation. Locally managed tablespaces have other nice  attributes as well, such as the enforcement of a uniform extent size, but that is starting to  get heavily into the role of the DBA.  Temp Files  Temporary data files (temp files) in Oracle are a special type of data file. Oracle will use  temporary files to store the intermediate results of a large sort operation, or result set,  when there is insufficient memory to hold it all in RAM. Permanent data objects, such as a  table or an index, will never be stored in a temporary file, but the contents of a temporary  table or index would be. So, youʹll never create your application tables in a temporary data  file, but you might store data there when you use a temporary table.  Temporary files are treated in a special way by Oracle. Normally, each and every change  you make to an object will be recorded in the redo logs ‐ these transaction logs can be  replayed at a later date in order to ʹredo a transactionʹ. We might do this during recovery  from failure for example. Temporary files are excluded from this process. Temporary files  never have redo generated for them, although they do have UNDO generated, when used  for global temporary tables, in the event you decide to rollback some work you have done  in your session. Your DBA never needs to back up a temporary data file, and in fact if they  do they are only wasting their time, as you can never restore a temporary data file.  It is recommended that your database be configured with locally managed temporary  tablespaces. Youʹll want to make sure your DBA uses a CREATE TEMPORARY  TABLESPACE command. You do not want them to just alter a permanent tablespace to a  temporary one, as you do not get the benefits of temp files that way. Additionally, youʹll  want them to use a locally managed tablespace with uniform extent sizes that reflect your  sort_area_size setting. Something such as:   tkyte@TKYTE816> create temporary tablespace temp    2  tempfile ʹc:\oracle\oradata\tkyte816\temp.dbfʹ    3  size 5m    4  extent management local    5  uniform size 64k;        Tablespace created.  As we are starting to get deep into DBA‐related activities once again, weʹll move onto the  next topic.  Control Files  Expert one-on-one Oracle 92 The control file is a fairly small file (it can grow up to 64 MB or so in extreme cases) that  contains a directory of the other files Oracle needs. The parameter file (init.ora file) tells  the instance where the control files are, the control files tell the instance where the  database and online redo log files are. The control files also tell Oracle other things, such  as information about checkpoints that have taken place, the name of the database (which  should match the db_name init.ora parameter), the timestamp of the database as it was  created, an archive redo log history (this can make a control file large in some cases), and  so on.  Control files should be multiplexed either by hardware (RAID) or by Oracle when RAID  or mirroring is not available ‐ more than one copy of them should exist and they should be  stored on separate disks, to avoid losing them in the event you have a disk failure. It is not  fatal to lose your control files, it just makes recovery that much harder.  Control files are something a developer will probably never have to actually deal with. To  a DBA they are an important part of the database, but to a software developer they are not  extremely relevant.  Redo Log Files  Redo log files are extremely crucial to the Oracle database. These are the transaction logs  for the database. They are used only for recovery purposes ‐ their only purpose in life is to  be used in the event of an instance or media failure, or as a method of maintaining a  standby database for failover. If the power goes off on your database machine, causing an  instance failure, Oracle will use the online redo logs in order to restore the system to  exactly the point it was at immediately prior to the power outage. If your disk drive  containing your datafile fails permanently, Oracle will utilize archived redo logs, as well  as online redo logs, in order to recover a backup of that drive to the correct point in time.  Additionally, if you ʹaccidentallyʹ drop a table or remove some critical information and  commit that operation, you can restore a backup and have Oracle restore it to the point  immediately prior to the ʹaccidentʹ using these online and archive redo log files.  Virtually every operation you perform in Oracle generates some amount of redo to be  written to the online redo log files. When you insert a row into a table, the end result of  that insert is written to the redo logs. When you delete a row, the fact that you deleted that  row is written. When you drop a table, the effects of that drop are written to the redo log.  The data from the table you dropped is not written; however the recursive SQL that Oracle  performs to drop the table, does generate redo. For example, Oracle will delete a row from  the SYS.OBJ$ table and this will generate redo.   Some operations may be performed in a mode that generates as little redo as possible. For  example, I can create an index with the NOLOGGING attribute. This means that the initial  creation of that index will not be logged, but any recursive SQL Oracle performs on my  Expert one-on-one Oracle 93 behalf will be. For example the insert of a row into SYS.OBJ$ representing the existence of  the index will not be logged. All subsequent modifications of the index using SQL inserts,  updates and deletes will however, be logged.  There are two types of redo log files that Iʹve referred to ‐ online and archived. We will  take a look at each. In Chapter 5, Redo and Rollback, we will take another look at redo in  conjunction with rollback segments, to see what impact they have on you the developer.  For now, we will just concentrate on what they are and what purpose they provide.  Online Redo Log  Every Oracle database has at least two online redo log files. These online redo log files are  fixed in size and are used in a circular fashion. Oracle will write to log file 1, and when it  gets to the end of this file, it will switch to log file 2, and rewrite the contents of that file  from start to end. When it has filled log file 2, it will switch back to log file 1 (assuming we  have only two redo log files, if you have three, it would of course proceed onto the third  file):    The act of switching from one log file to the other is called a log switch. It is important to  note that a log switch may cause a temporary ʹhangʹ in a poorly tuned database. Since the  redo logs are used to recover transactions in the event of a failure, we must assure  ourselves that we wonʹt need the contents of a redo log file in the event of a failure before  we reuse it. If Oracle is not sure that it wonʹt need the contents of a log file, it will suspend  operations in the database momentarily, and make sure that the data this redo ʹprotectsʹ is  safely on disk itself. Once it is sure of that, processing will resume and the redo file will be  reused. What weʹve just started to talk about here is a key database concept ‐  checkpointing. In order to understand how online redo logs are used, weʹll need to know  something about checkpointing, how the database buffer cache works, and what a process  called Database Block Writer (DBWn) does. The database buffer cache and DBWn are  covered in more detail a little later on, but we will skip ahead a little anyway and touch on  them now.  Expert one-on-one Oracle 94 The database buffer cache is where database blocks are stored temporarily. This is a  structure in the SGA of Oracle. As blocks are read, they are stored in this cache ‐ hopefully  to allow us to not have to physically re‐read them later. The buffer cache is first and  foremost a performance‐tuning device, it exists solely to make the very slow process of  physical I/O appear to be much faster then it is. When we modify a block by updating a  row on it, these modifications are done in memory, to the blocks in the buffer cache.  Enough information to redo this modification is stored in the redo log buffer, another SGA  data structure. When you COMMIT your modifications, making them permanent, Oracle  does not go to all of the blocks you modified in the SGA and write them to disk. Rather, it  just writes the contents of the redo log buffer out to the online redo logs. As long as that  modified block is in the buffer cache and is not on disk, we need the contents of that online  redo log in the event the database fails. If at the instant after we committed, the power was  turned off, the database buffer cache would be wiped out.   If this happens, the only record of our change is in that redo log file. Upon restart of the  database, Oracle will actually replay our transaction, modifying the block again in the  same way we did, and committing it for us. So, as long as that modified block is cached  and not written to disk, we cannot reuse that redo log file.  This is where DBWn comes into play. It is the Oracle background process that is  responsible for making space in the buffer cache when it fills up and, more importantly,  for performing checkpoints. A checkpoint is the flushing of dirty (modified) blocks from  the buffer cache to disk. Oracle does this in the background for us. Many things can cause  a checkpoint to occur, the most common event being a redo log switch. As we filled up log  file 1 and switched to log file 2, Oracle initiated a checkpoint. At this point in time, DBWn  started flushing to disk all of the dirty blocks that are protected by log file 1. Until DBWn  flushes all of these blocks protected by that log file, Oracle cannot reuse it. If we attempt to  use it before DBWn has finished its checkpoint, we will get a message like this:   ...  Thread 1 cannot allocate new log, sequence 66  Checkpoint not complete    Current log# 2 seq# 65 mem# 0: C:\ORACLE\ORADATA\TKYTE816\REDO02.LOG  ...  In our databases ALERT log (the alert log is a file on the server that contains informative  messages from the server such as startup and shutdown messages, and exceptional events,  such as an incomplete checkpoint). So, at this point in time, when this message appeared,  processing was suspended in the database while DBWn hurriedly finished its checkpoint.  Oracle gave all the processing power it could, to DBWn at that point in the hope it would  finish faster.  Expert one-on-one Oracle 95 This is a message you never want to see in a nicely tuned database instance. If you do see  it, you know for a fact that you have introduced artificial, unnecessary waits for your end  users. This can always be avoided. The goal (and this is for the DBA, not the developer  necessarily) is to have enough online redo log files allocated so that you never attempt to  reuse a log file before the checkpoint initiated by it completes. If you see this message  frequently, it means your DBA has not allocated sufficient online redo logs for your  application, or that DBWn needs to be tuned to work more efficiently. Different  applications will generate different amounts of redo log. A DSS (Decision Support  System, query only) system will naturally generate significantly less online redo log then  an OLTP (transaction processing) system would. A system that does a lot of image  manipulation in BLOBs (Binary Large OBjects) in the database may generate radically  more redo then a simple order entry system. An order entry system with 100 users will  probably generate a tenth the amount of redo 1,000 users would generate. There is no  ʹrightʹ size for your redo logs, although you do want to ensure they are large enough.  There are many things you must take into consideration when setting both the size of, and  the number of, online redo logs. Many of them are out of the scope of this particular book,  but Iʹll list some of them to give you an idea:  • Standby database ‐ If you are utilizing the standby database feature, whereby redo  logs are sent to another machine after they are filled and applied to a copy of your  database, you will most likely want lots of small redo log files. This will help ensure  the standby database is never too far out of sync with the primary database.   • Lots of users modifying the same blocks ‐ Here you might want large redo log  files. Since everyone is modifying the same blocks, we would like to update them as  many times as possible before writing them out to disk. Each log switch will fire a  checkpoint so we would like to switch logs infrequently. This may, however, affect  your recovery time.   • Mean time to recover ‐ If you must ensure that a recovery takes as little time as  possible, you may be swayed towards smaller redo log files, even if the previous  point is true. It will take less time to process one or two small redo log files than one  gargantuan one upon recovery. The overall system will run slower than it  absolutely could day‐to‐day perhaps (due to excessive checkpointing), but the  amount of time spent in recovery will be smaller. There are other database  parameters that may also be used to reduce this recovery time, as an alternative to  the use of small redo log files.   Archived Redo Log  The Oracle database can run in one of two modes ‐ NOARCHIVELOG mode and  ARCHIVELOG mode. I believe that a system is not a production system unless it is in  ARCHIVELOG mode. A database that is not in ARCHIVELOG mode will, some day, lose  Expert one-on-one Oracle 96 data. It is inevitable; you will lose data if you are not in ARCHIVELOG mode. Only a test  or development system should execute in NOARCHIVELOG mode.  The difference between these two modes is simply what happens to a redo log file when  Oracle goes to reuse it. ʹWill we keep a copy of that redo or should Oracle just overwrite it,  losing it forever?ʹ It is an important question to answer. Unless you keep this file, we  cannot recover data from a backup to the current point in time. Say you take a backup  once a week on Saturday. Now, on Friday afternoon, after you have generated hundreds  of redo logs over the week, your hard disk fails. If you have not been running in  ARCHIVELOG mode, the only choices you have right now are:  • Drop the tablespace(s) associated with the failed disk. Any tablespace that had a file  on that disk must be dropped, including the contents of that tablespace. If the  SYSTEM tablespace (Oracleʹs data dictionary) is affected, you cannot do this.   • Restore last Saturdayʹs data and lose all of the work you did that week.   Neither option is very appealing. Both imply that you lose data. If you had been executing  in ARCHIVELOG mode on the other hand, you simply would have found another disk.  You would have restored the affected files from Saturdayʹs backup onto it. Lastly, you  would have applied the archived redo logs, and ultimately, the online redo logs to them  (in effect replay the weeks worth of transactions in fast forward mode). You lose nothing.  The data is restored to the point in time of the failure.  People frequently tell me they donʹt need ARCHIVELOG mode for their production  systems. I have yet to meet anyone who was correct in that statement. Unless they are  willing to lose data some day, they must be in ARCHIVELOG mode. ʹWe are using RAID‐ 5, we are totally protectedʹ is a common excuse. Iʹve seen cases where, due to a  manufacturing error, all five disks in a raid set froze, all at the same time. Iʹve seen cases  where the hardware controller introduced corruption into the data files, so they safely  protected corrupt data. If we had the backups from before the hardware failure, and the  archives were not affected, we could have recovered. The bottom line is that there is no  excuse for not being in ARCHIVELOG mode on a system where the data is of any value.  Performance is no excuse ‐ properly configured archiving adds little overhead. This and  the fact that a ʹfast systemʹ that ʹloses dataʹ is useless, would make it so that even if  archiving added 100 percent overhead, you would need to do it.  Donʹt let anyone talk you out of being in ARCHIVELOG mode. You spent a long time  developing your application, so you want people to trust it. Losing their data will not  instill confidence in them.   Files Wrap‐Up  Expert one-on-one Oracle 97 Here we have explored the important types of files used by the Oracle database, from  lowly parameter files (without which you wonʹt even be able to get started), to the all  important redo log and data files. We have explored the storage structures of Oracle from  tablespaces to segments, and then extents, and finally down to database blocks, the  smallest unit of storage. We have reviewed how checkpointing works in the database, and  even started to look ahead at what some of the physical processes or threads of Oracle do.  In later components of this chapter, weʹll take a much more in depth look at these  processes and memory structures.  The Memory Structures  Now we are ready to look at Oracleʹs major memory structures. There are three major  memory structures to be considered:  • SGA, System Global Area ‐ This is a large, shared memory segment that virtually  all Oracle processes will access at one point or another.   • PGA, Process Global Area ‐ This is memory, which is private to a single process or  thread, and is not accessible from other processes/threads.   • UGA, User Global Area ‐ This is memory associated with your session. It will be  found either in the SGA or the PGA depending on whether you are running in MTS  mode (then itʹll be in the SGA), or dedicated server (itʹll be in the PGA).   We will briefly discuss the PGA and UGA, and then move onto the really big structure,  the SGA.  PGA and UGA  As stated earlier, the PGA is a process piece of memory. This is memory specific to a single  operating system process or thread. This memory is not accessible by any other  process/thread in the system. It is typically allocated via the C run‐time call malloc(), and  may grow (and shrink even) at run‐time. The PGA is never allocated out of Oracleʹs SGA ‐  it is always allocated locally by the process or thread.  The UGA is in effect, your sessionʹs state. It is memory that your session must always be  able to get to. The location of the UGA is wholly dependent on how Oracle has been  configured to accept connections. If you have configured MTS, then the UGA must be  stored in a memory structure that everyone has access to ‐ and that would be the SGA. In  this way, your session can use any one of the shared servers, since any one of them can  read and write your sessions data. On the other hand, if you are using a dedicated server  connection, this need for universal access to your session state goes away, and the UGA  becomes virtually synonymous with the PGA ‐ it will in fact be contained in the PGA.  When you look at the system statistics, youʹll find the UGA reported in the PGA in  Expert one-on-one Oracle 98 dedicated server mode (the PGA will be greater than, or equal to, the UGA memory used;  the PGA memory size will include the UGA size as well).   One of the largest impacts on the size of your PGA/UGA will be the init.ora or session‐ level parameters, SORT_AREA_SIZE and SORT_AREA_RETAINED_SIZE. These two  parameters control the amount of space Oracle will use to sort data before writing it to  disk, and how much of that memory segment will be retained after the sort is done. The  SORT_AREA_SIZE is generally allocated out of your PGA and the  SORT_AREA_RETAINED_SIZE will be in your UGA. We can monitor the size of the  UGA/PGA by querying a special Oracle V$ table, also referred to as a dynamic  performance table. Weʹll find out more about these V$ tables in Chapter 10, Tuning  Strategies and Tools. Using these V$ tables, we can discover our current usage of PGA and  UGA memory. For example, Iʹll run a small test that will involve sorting lots of data. Weʹll  look at the first couple of rows and then discard the result set. We can look at the ʹbeforeʹ  and ʹafterʹ memory usage:   tkyte@TKYTE816> select a.name, b.value    2  from v$statname a, v$mystat b    3  where a.statistic# = b.statistic#    4  and a.name like ʹ%ga %ʹ    5  /        NAME                                VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  session uga memory                  67532  session uga memory max              71972  session pga memory                 144688  session pga memory max             144688        4 rows selected.  So, before we begin we can see that we have about 70 KB of data in the UGA and 140 KB  of data in the PGA. The first question is, how much memory are we using between the  PGA and UGA? It is a trick question, and one that you cannot answer unless you know if  we are connected via a dedicated server or a shared server over MTS, and even then it  might be hard to figure out. In dedicated server mode, the UGA is totally contained within  the PGA. There, we would be consuming 140 KB of memory in our process or thread. In  MTS mode, the UGA is allocated from the SGA, and the PGA is in the shared server. So, in  MTS mode, by the time we get the last row from the above query, our process may be in  use by someone else. That PGA isnʹt ʹoursʹ, so technically we are using 70 KB of memory  (except when we are actually running the query at which point we are using 210 KB of  memory between the combined PGA and UGA).  Expert one-on-one Oracle 99 Now we will do a little bit of work and see what happens with our PGA/UGA:   tkyte@TKYTE816> show parameter sort_area        NAME                                 TYPE    VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  sort_area_retained_size              integer 65536  sort_area_size                       integer 65536        tkyte@TKYTE816> set pagesize 10  tkyte@TKYTE816> set pause on  tkyte@TKYTE816> select * from all_objects order by 1, 2, 3, 4;              ...(control C after first page of data) ...        tkyte@TKYTE816> set pause off        tkyte@TKYTE816> select a.name, b.value    2  from v$statname a, v$mystat b    3  where a.statistic# = b.statistic#    4  and a.name like ʹ%ga %ʹ    5  /        NAME                                VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  session uga memory                  67524  session uga memory max             174968  session pga memory                 291336  session pga memory max             291336        4 rows selected.  As you can see, our memory usage went up ‐ weʹve done some sorting of data. Our UGA  temporarily increased by about the size of SORT_AREA_RETAINED_SIZE while our PGA  went up a little more. In order to perform the query and the sort (and so on), Oracle  allocated some additional structures that our session will keep around for other queries.  Now, letʹs retry that operation but play around with the size of our SORT_AREA:   tkyte@TKYTE816> alter session set sort_area_size=1000000;        Session altered.        Expert one-on-one Oracle 100 tkyte@TKYTE816> select a.name, b.value    2  from v$statname a, v$mystat b    3  where a.statistic# = b.statistic#    4  and a.name like ʹ%ga %ʹ    5  /        NAME                                VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  session uga memory                  63288  session uga memory max             174968  session pga memory                 291336  session pga memory max             291336        4 rows selected.        tkyte@TKYTE816> show parameter sort_area        NAME                                 TYPE    VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  sort_area_retained_size              integer 65536  sort_area_size                       integer 1000000        tkyte@TKYTE816> select * from all_objects order by 1, 2, 3, 4;        ...(control C after first page of data) ...        tkyte@TKYTE816> set pause off        tkyte@TKYTE816> select a.name, b.value    2  from v$statname a, v$mystat b    3  where a.statistic# = b.statistic#    4  and a.name like ʹ%ga %ʹ    5  /  NAME                                VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  session uga memory                  67528  session uga memory max             174968  session pga memory                1307580  session pga memory max            1307580        4 rows selected.  Expert one-on-one Oracle 101 As you can see, our PGA has grown considerably this time. By about the 1,000,000 bytes of  SORT_AREA_SIZE we are using. It is interesting to note that the UGA did not move at all  in this case. We can change that by altering the SORT_AREA_RETAINED_SIZE as follows:   tkyte@TKYTE816> alter session set sort_area_retained_size=1000000;  Session altered.        tkyte@TKYTE816> select a.name, b.value    2  from v$statname a, v$mystat b    3  where a.statistic# = b.statistic#    4  and a.name like ʹ%ga %ʹ    5  /        NAME                                VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  session uga memory                  63288  session uga memory max             174968  session pga memory                1307580  session pga memory max            1307580        4 rows selected.        tkyte@TKYTE816> show parameter sort_area        NAME                                 TYPE    VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  sort_area_retained_size              integer 1000000  sort_area_size                       integer 1000000        tkyte@TKYTE816> select * from all_objects order by 1, 2, 3, 4;              ...(control C after first page of data) ...        tkyte@TKYTE816> select a.name, b.value    2  from v$statname a, v$mystat b    3  where a.statistic# = b.statistic#    4  and a.name like ʹ%ga %ʹ    5  /        NAME                                VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  session uga memory                  66344  Expert one-on-one Oracle 102 session uga memory max            1086120  session pga memory                1469192  session pga memory max            1469192        4 rows selected.  Here, we see that our UGA memory max shot way up ‐ to include the  SORT_AREA_RETAINED_SIZE amount of data, in fact. During the processing of our  query, we had a 1 MB of sort data ʹin memory cacheʹ. The rest of the data was on disk in a  temporary segment somewhere. After our query completed execution, this memory was  given back for use elsewhere. Notice how PGA memory does not shrink back. This is to be  expected as the PGA is managed as a heap and is created via malloc()ʹed memory. Some  processes within Oracle will explicitly free PGA memory ‐ others allow it to remain in the  heap (sort space for example stays in the heap). The shrinking of a heap like this typically  does nothing (processes tend to grow in size, not to shrink). Since the UGA is a ʹsub‐heapʹ  (the ʹparentʹ heap being either the PGA or SGA) it is made to shrink. If we wish, we can  force the PGA to shrink:   tkyte@TKYTE816> exec dbms_session.free_unused_user_memory;        PL/SQL procedure successfully completed.              tkyte@TKYTE816> select a.name, b.value    2  from v$statname a, v$mystat b    3  where a.statistic# = b.statistic#    4  and a.name like ʹ%ga %ʹ    5  /        NAME                                VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  session uga memory                  73748  session uga memory max            1086120  session pga memory                 183360  session pga memory max            1469192  However, you should be aware that on most systems, this is somewhat a waste of time.  You may have shrunk the size of the PGA heap as far as Oracle is concerned, but you  havenʹt really given the OS any memory back in most cases. In fact, depending on the OS  method of managing memory, you may actually be using more memory in total according  to the OS. It will all depend on how malloc(), free(), realloc(), brk(), and sbrk() (the C  memory management routines) are implemented on your platform.  Expert one-on-one Oracle 103 So, here we have looked at the two memory structures, the PGA and the UGA. We  understand now that the PGA is private to a process. It is the set of variables that an  Oracle dedicated or shared server needs to have independent of a session. The PGA is a  ʹheapʹ of memory in which other structures may be allocated. The UGA on the other hand,  is also a heap of memory in which various session‐specific structures may be defined. The  UGA is allocated from the PGA when you use the dedicated server mode to connect to  Oracle, and from the SGA in MTS mode. This implies that when using MTS, you must size  your SGA to have enough UGA space in it to cater for every possible user that will ever  connect to your database concurrently. So, the SGA of a database running MTS is  generally much larger than the SGA for a similarly configured, dedicated server mode  only, database.  SGA  Every Oracle instance has one big memory structure collectively referred to as the SGA,  the System Global Area. This is a large, shared memory structure that every Oracle  process will access at one point or another. It will vary from a couple of MBs on small test  systems to hundreds of MBs on medium to large systems, and into many GBs in size for  really big systems.   On a UNIX operating system, the SGA is a physical entity that you can ʹseeʹ from the  operating system command line. It is physically implemented as a shared memory  segment ‐ a standalone piece of memory that processes may attach to. It is possible to have  an SGA on a system without having any Oracle processes; the memory stands alone. It  should be noted however that if you have an SGA without any Oracle processes, it  indicates that the database crashed in some fashion. It is not a normal situation but it can  happen. This is what an SGA ʹlooksʹ like on UNIX:  $ ipcs ‐mb  IPC status from  as of Mon Feb 19 14:48:26 EST 2001  T         ID      KEY        MODE        OWNER    GROUP      SEGSZ  Shared Memory:  m        105   0xf223dfc8 ‐‐rw‐r‐‐‐‐‐   ora816      dba  186802176  On Windows, you really cannot see the SGA as you can in UNIX. Since Oracle executes as  a single process with a single address space on that platform, the SGA is allocated as  private memory to the ORACLE.EXE process. If you use the Windows Task Manager or  some other performance tool, you can see how much memory ORACLE.EXE has allocated,  but you cannot see what is the SGA versus any other piece of allocated memory.  Within Oracle itself we can see the SGA regardless of platform. There is another magic V$  table called V$SGASTAT. It might look like this:   Expert one-on-one Oracle 104 tkyte@TKYTE816> compute sum of bytes on pool  tkyte@TKYTE816> break on pool skip 1  tkyte@TKYTE816> select pool, name, bytes    2  from v$sgastat    3  order by pool, name;        POOL        NAME                                BYTES  ‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  java pool   free memory                      18366464              memory in use                     2605056  ***********                                ‐‐‐‐‐‐‐‐‐‐  sum                                          20971520        large pool  free memory                       6079520              session heap                        64480  ***********                                ‐‐‐‐‐‐‐‐‐‐  sum                                           6144000        shared pool Checkpoint queue                    73764              KGFF heap                            5900              KGK heap                            17556              KQLS heap                          554560              PL/SQL DIANA                       364292              PL/SQL MPCODE                      138396              PLS non‐lib hp                       2096              SYSTEM PARAMETERS                   61856              State objects                      125464              VIRTUAL CIRCUITS                    97752              character set object                58936              db_block_buffers                   408000              db_block_hash_buckets              179128              db_files                           370988              dictionary cache                   319604              distributed_transactions‐          180152              dlo fib struct                      40980              enqueue_resources                   94176              event statistics per sess          201600              file # translation table            65572              fixed allocation callback             320              free memory                       9973964              joxlod: in ehe                      52556              joxlod: in phe                       4144              joxs heap init                        356  Expert one-on-one Oracle 105             library cache                     1403012              message pool freequeue             231152              miscellaneous                      562744              processes                           40000              sessions                           127920              sql area                          2115092              table columns                       19812              transaction_branches               368000              transactions                        58872              trigger defini                       2792              trigger inform                        520  ***********                                ‐‐‐‐‐‐‐‐‐‐  sum                                          18322028                    db_block_buffers                 24576000              fixed_sga                           70924              log_buffer                          66560  ***********                                ‐‐‐‐‐‐‐‐‐‐  sum                                          24713484              43 rows selected.  The SGA is broken up into various pools. They are:  • Java pool ‐ The Java pool is a fixed amount of memory allocated for the JVM  running in the database.   • Large pool ‐ The large pool is used by the MTS for session memory, by Parallel  Execution for message buffers, and by RMAN Backup for disk I/O buffers.   • Shared pool ‐ The shared pool contains shared cursors, stored procedures, state  objects, dictionary caches, and many dozens of other bits of data.   • The ʹNullʹ pool ‐ This one doesnʹt really have a name. It is the memory dedicated to  block buffers (cached database blocks), the redo log buffer and a ʹfixed SGAʹ area.   So, an SGA might look like this:    Expert one-on-one Oracle 106 The init.ora parameters that have the most effect on the overall size of the SGA are:  • JAVA_POOL_SIZE ‐ controls the size of the Java pool.   • SHARED_POOL_SIZE ‐ controls the size of the shared pool, to some degree.   • LARGE_POOL_SIZE ‐ controls the size of the large pool.   • DB_BLOCK_BUFFERS ‐ controls the size of the block buffer cache.   • LOG_BUFFER ‐ controls the size of the redo buffer to some degree.   With the exception of the SHARED_POOL_SIZE and LOG_BUFFER, there is a one‐to‐one  correspondence between the init.ora parameters, and the amount of memory allocated in  the SGA. For example, if you multiply DB_BLOCK_BUFFERS by your databases block size,  youʹll typically get the size of the DB_BLOCK_BUFFERS row from the NULL pool in  V$SGASTAT (there is some overhead added for latches). If you look at the sum of the  bytes from V$SGASTAT for the large pool, this will be the same as the  LARGE_POOL_SIZE parameter.  Fixed SGA  The fixed SGA is a component of the SGA that varies in size from platform to platform  and release to release. It is ʹcompiledʹ into the Oracle binary itself at installation time  (hence the name ʹfixedʹ). The fixed SGA contains a set of variables that point to the other  components of the SGA, and variables that contain the values of various parameters. The  size of the fixed SGA is something over which we have no control, and it is generally very  small. Think of this area as a ʹbootstrapʹ section of the SGA, something Oracle uses  internally to find the other bits and pieces of the SGA.  Redo Buffer  The redo buffer is where data that needs to be written to the online redo logs will be  cached temporarily before it is written to disk. Since a memory‐to‐memory transfer is  much faster then a memory to disk transfer, use of the redo log buffer can speed up  operation of the database. The data will not reside in the redo buffer for a very long time.  In fact, the contents of this area are flushed:  • Every three seconds, or   • Whenever someone commits, or   • When it gets one third full or contains 1 MB of cached redo log data.   For these reasons, having a redo buffer in the order of many tens of MB in size is typically  just a waste of good memory. In order to use a redo buffer cache of 6 MB for example, you  would have to have very long running transactions that generate 2 MB of redo log every  three seconds or less. If anyone in your system commits during that three‐second period of  Expert one-on-one Oracle 107 time, you will never use the 2MB of redo log space; itʹll continuously be flushed. It will be  a very rare system that will benefit from a redo buffer of more than a couple of megabytes  in size.  The default size of the redo buffer, as controlled by the LOG_BUFFER init.ora parameter,  is the greater of 512 KB and (128 * number of CPUs) KB. The minimum size of this area is  four times the size of the largest database block size supported on that platform. If you  would like to find out what that is, just set your LOG_BUFFERS to 1 byte and restart your  database. For example, on my Windows 2000 instance I see:   SVRMGR> show parameter log_buffer  NAME                                TYPE    VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  log_buffer                          integer 1        SVRMGR> select * from v$sgastat where name = ʹlog_bufferʹ;  POOL        NAME                       BYTES  ‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐              log_buffer                      66560  The smallest log buffer I can really have, regardless of my init.ora settings, is going to be  65 KB. In actuality ‐ it is a little larger then that:   tkyte@TKYTE816> select * from v$sga where name = ʹRedo Buffersʹ;        NAME                                VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  Redo Buffers                        77824  It is 76 KB in size. This extra space is allocated as a safety device ‐ as ʹguardʹ pages to  protect the redo log buffer pages themselves.  Block Buffer Cache  So far, we have looked at relatively small components of the SGA. Now we are going to  look at one that is possibly huge in size. The block buffer cache is where Oracle will store  database blocks before writing them to disk, and after reading them in from disk. This is a  crucial area of the SGA for us. Make it too small and our queries will take forever to run.  Make it too big and weʹll starve other processes (for example, you will not leave enough  room for a dedicated server to create its PGA and you wonʹt even get started).  The blocks in the buffer cache are basically managed in two different lists. There is a ʹdirtyʹ  list of blocks that need to be written by the database block writer (DBWn ‐ weʹll be taking a  Expert one-on-one Oracle 108 look at that process a little later). Then there is a list of ʹnot dirtyʹ blocks. This used to be a  LRU (Least Recently Used) list in Oracle 8.0 and before. Blocks were listed in order of use.  The algorithm has been modified slightly in Oracle 8i and in later versions. Instead of  maintaining the list of blocks in some physical order, Oracle employs a ʹtouch countʹ  schema. This effectively increments a counter associated with a block every time you hit it  in the cache. We can see this in one of the truly magic tables, the X$ tables. These X$ tables  are wholly undocumented by Oracle, but information about them leaks out from time to  time.   The X$BH table shows information about the blocks in the block buffer cache. Here, we  can see the ʹtouch countʹ get incremented as we hit blocks. First, we need to find a block.  Weʹll use the one in the DUAL table, a special table with one row and one column that is  found in all Oracle databases. We need to know the file number and block number for that  block:   tkyte@TKYTE816> select file_id, block_id    2  from dba_extents    3  where segment_name = ʹDUALʹ and owner = ʹSYSʹ;           FILE_ID   BLOCK_ID  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐           1        465  Now we can use that information to see the ʹtouch countʹ on that block:  sys@TKYTE816> select tch from x$bh where file# = 1 and dbablk = 465;               TCH  ‐‐‐‐‐‐‐‐‐‐          10        sys@TKYTE816> select * from dual;        D  ‐  X        sys@TKYTE816> select tch from x$bh where file# = 1 and dbablk = 465;               TCH  ‐‐‐‐‐‐‐‐‐‐          11        Expert one-on-one Oracle 109 sys@TKYTE816> select * from dual;        D  ‐  X        sys@TKYTE816> select tch from x$bh where file# = 1 and dbablk = 465;               TCH  ‐‐‐‐‐‐‐‐‐‐          12  Every time I touch that block, the counter goes up. A buffer no longer moves to the head  of the list as it is used, rather it stays where it is in the list, and has its ʹtouch countʹ  incremented. Blocks will naturally tend to move in the list over time however, because  blocks are taken out of the list and put into the dirty list (to be written to disk by DBWn).  Also, as they are reused, when the buffer cache is effectively full, and some block with a  small ʹtouch countʹ is taken out of the list, it will be placed back into approximately the  middle of the list with the new data. The whole algorithm used to manage these lists is  fairly complex and changes subtly from release to release of Oracle as improvements are  made. The actual full details are not relevant to us as developers, beyond the fact that  heavily used blocks will be cached and blocks that are not used heavily will not be cached  for long.   The block buffer cache, in versions prior to Oracle 8.0, was one big buffer cache. Every  block was cached alongside every other block ‐ there was no way to segment or divide up  the space in the block buffer cache. Oracle 8.0 added a feature called multiple buffer pools  to give us this ability. Using the multiple buffer pool feature, we can set aside a given  amount of space in the block buffer cache for a specific segment or segments (segments as  you recall, are indexes, tables, and so on). Now, we could carve out a space, a buffer pool,  large enough to hold our ʹlookupʹ tables in memory, for example. When Oracle reads  blocks from these tables, they always get cached in this special pool. They will compete for  space in this pool only with other segments targeted towards this pool. The rest of the  segments in the system will compete for space in the default buffer pool. This will increase  the likelihood of their staying in the cache and not getting aged out by other unrelated  blocks. A buffer pool that is set up to cache blocks like this is known as a KEEP pool. The  blocks in the KEEP pool are managed much like blocks in the generic block buffer cache  described above ‐ if you use a block frequently, itʹll stay cached. If you do not touch a  block for a while, and the buffer pool runs out of room, that block will be aged out of the  pool.  We also have the ability to carve out a space for segments in the buffer pool. This space is  called a RECYCLE pool. Here, the aging of blocks is done differently to the KEEP pool. In  Expert one-on-one Oracle 110 the KEEP pool, the goal is to keep ʹwarmʹ and ʹhotʹ blocks cached for as long as possible. In  the recycle pool, the goal is to age out a block as soon as it is no longer needed. This is  advantageous for ʹlargeʹ tables (remember, large is always relative, there is no absolute  size we can put on ʹlargeʹ) that are read in a very random fashion. If the probability that a  block will ever be re‐read in a reasonable amount of time, there is no use in keeping this  block cached for very long. Therefore, in a RECYCLE pool, things move in and out on a  more regular basis.  So, taking the diagram of the SGA a step further, we can break it down to:    Shared Pool  The shared pool is one of the most critical pieces of memory in the SGA, especially in  regards to performance and scalability. A shared pool that is too small can kill  performance to the point where the system appears to hang. A shared pool that is too  large can do the same thing. A shared pool that is used incorrectly will be a disaster as  well.  So, what exactly is the shared pool? The shared pool is where Oracle caches many bits of  ʹprogramʹ data. When we parse a query, the results of that are cached here. Before we go  through the job of parsing an entire query, Oracle searches here to see if the work has  already been done. PL/SQL code that you run is cached here, so the next time you run it,  Oracle doesnʹt have to read it in from disk again. PL/SQL code is not only cached here, it is  shared here as well. If you have 1000 sessions all executing the same code, only one copy  of the code is loaded and shared amongst all sessions. Oracle stores the system parameters  in the shared pool. The data dictionary cache, cached information about database objects,  is stored here. In short, everything but the kitchen sink is stored in the shared pool.   The shared pool is characterized by lots of small (4 KB or thereabouts) chunks of memory.  The memory in the shared pool is managed on a LRU basis. It is similar to the buffer cache  in that respect ‐ if you donʹt use it, youʹll lose it. There is a supplied package,  DBMS_SHARED_POOL, which may be used to change this behavior ‐ to forcibly pin  objects in the shared pool. You can use this procedure to load up your frequently used  Expert one-on-one Oracle 111 procedures and packages at database startup time, and make it so they are not subject to  aging out. Normally though, if over time a piece of memory in the shared pool is not  reused, it will become subject to aging out. Even PL/SQL code, which can be rather large,  is managed in a paging mechanism so that when you execute code in a very large package,  only the code that is needed is loaded into the shared pool in small chunks. If you donʹt  use it for an extended period of time, it will be aged out if the shared pool fills up and  space is needed for other objects.  The easiest way to break Oracleʹs shared pool is to not use bind variables. As we saw in  Chapter 1 on Developing Successful Oracle Applications, not using bind variables can bring a  system to its knees for two reasons:  • The system spends an exorbitant amount of CPU time parsing queries.   • The system expends an extremely large amount of resources managing the objects  in the shared pool as a result of never reusing queries.   If every query submitted to Oracle is a unique query with the values hard‐coded, the  concept of the shared pool is substantially defeated. The shared pool was designed so that  query plans would be used over and over again. If every query is a brand new, never  before seen query, then caching only adds overhead. The shared pool becomes something  that inhibits performance. A common, misguided technique that many try in order to solve  this issue is to add more space to the shared pool, but this typically only makes things  worse than before. As the shared pool inevitably fills up once again, it gets to be even more  of an overhead than the smaller shared pool, for the simple reason that managing a big,  full‐shared pool takes more work than managing a smaller full‐shared pool.  The only true solution to this problem is to utilize shared SQL ‐ to reuse queries. Later, in  Chapter 10 on Tuning Strategies and Tools, we will take a look at the init.ora parameter  CURSOR_SHARING that can work as a short term ʹcrutchʹ in this area, but the only real  way to solve this issue is to use reusable SQL in the first place. Even on the largest of large  systems, I find that there are at most 10,000 to 20,000 unique SQL statements. Most  systems execute only a few hundred unique queries.  The following real‐world example demonstrates just how bad things can get if you use of  the shared pool poorly. I was asked to work on a system where the standard operating  procedure was to shut down the database each and every night, in order to wipe out the  SGA, and restart it clean. The reason for doing this was that the system was having issues  during the day whereby it was totally CPU‐bound and, if the database were left to run for  more than a day, performance would really start to decline. This was solely due to the fact  that, in the time period from 9am to 5pm, they would fill a 1 GB shared pool inside of a 1.1  GB SGA. This is true ‐ 0.1 GB dedicated to block buffer cache and other elements, and 1 GB  dedicated to caching unique queries that would never be executed again. The reason for  the cold start was that if they left the system running for more than a day, they would run  Expert one-on-one Oracle 112 out of free memory in the shared pool. At that point, the overhead of aging structures out  (especially from a structure so large) was such that it overwhelmed the system and  performance was massively degraded (not that performance was that great anyway, since  they were managing a 1 GB shared pool). Additionally, the people working on this system  constantly wanted to add more and more CPUs to the machine, due to the fact that hard  parsing SQL is so CPU‐intensive. By correcting the application, allowing it to use bind  variables, not only did the physical machine requirements drop (they then had many  times more CPU power then they needed), the memory utilization was reversed. Instead  of a 1 GB shared pool, they had less then 100 MB allocated ‐ and never used it all over  many weeks of continuous uptime.   One last comment about the shared pool and the init.ora parameter,  SHARED_POOL_SIZE. There is no relationship between the outcome of the query:   sys@TKYTE816> select sum(bytes) from v$sgastat where pool = ʹshared poolʹ;  SUM(BYTES)  ‐‐‐‐‐‐‐‐‐‐    18322028  1 row selected.  and the SHARED_POOL_SIZE init.ora parameter:   sys@TKYTE816> show parameter shared_pool_size  NAME                                TYPE    VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  shared_pool_size                    string  15360000  SVRMGR>  other than the fact that the SUM(BYTES) FROM V$SGASTAT will always be larger than  the SHARED_POOL_SIZE. The shared pool holds many other structures that are outside  the scope of the corresponding init.ora parameter. The SHARED_POOL_SIZE is typically  the largest contributor to the shared pool as reported by the SUM(BYTES), but it is not the  only contributor. For example, the init.ora parameter, CONTROL_FILES, contributes 264  bytes per file to the ʹmiscellaneousʹ section of the shared pool. It is unfortunate that the  ʹshared poolʹ in V$SGASTAT and the init.ora parameter SHARED_POOL_SIZE are named  as they are, since the init.ora parameter contributes to the size of the shared pool, but it is  not the only contributor.  Large Pool  The large pool is not so named because it is a ʹlargeʹ structure (although it may very well  be large in size). It is so named because it is used for allocations of large pieces of memory,  bigger than the shared pool is designed to handle. Prior to its introduction in Oracle 8.0, all  Expert one-on-one Oracle 113 memory allocation took place in the shared pool. This was unfortunate if you were using  features that made use of ʹlargeʹ memory allocations such as MTS. This issue was further  confounded by the fact that processing, which tended to need a lot of memory allocation,  would use the memory in a different manner to the way in which the shared pool  managed it. The shared pool manages memory in a LRU basis, which is perfect for caching  and reusing data. Large memory allocations, however, tended to get a chunk of memory,  use it, and then were done with it ‐ there was no need to cache this memory.  What Oracle needed was something similar to the RECYCLE and KEEP buffer pools  implemented for the block buffer cache. This is exactly what the large pool and shared  pool are now. The large pool is a RECYCLE‐style memory space whereas the shared pool  is more like the KEEP buffer pool ‐ if people appear to be using something frequently, then  you keep it cached.  Memory allocated in the large pool is managed in a heap, much in the way C manages  memory via malloc() and free(). As soon as you ʹfreeʹ a chunk of memory, it can be used by  other processes. In the shared pool, there really was no concept of ʹfreeingʹ a chunk of  memory. You would allocate memory, use it, and then stop using it. After a while, if that  memory needed to be reused, Oracle would age out your chunk of memory. The problem  with using just a shared pool is that one size doesnʹt always fit all.   The large pool is used specifically by:  • MTS ‐ to allocate the UGA region in the SGA.   • Parallel Execution of statements ‐ to allow for the allocation of inter‐process  message buffers, used to coordinate the parallel query servers.   • Backup ‐ for RMAN disk I/O buffers.   As you can see, none of the above memory allocations should be managed in an LRU  buffer pool designed to manage small chunks of memory. With MTS memory, for example,  once a session logs out, this memory is never going to be reused so it should be  immediately returned to the pool. Also, MTS memory tends to be ʹlargeʹ. If you review our  earlier examples, with the SORT_AREA_RETAINED_SIZE, the UGA can grow very large,  and is definitely bigger than 4 KB chunks. Putting MTS memory into the shared pool  causes it to fragment into odd sized pieces of memory and, furthermore you will find that  large pieces of memory that will never be reused will age out memory that could be  reused. This forces the database to do more work to rebuild that memory structure later.  The same is true for parallel query message buffers. Once they have delivered their  message, they are no longer needed. Backup buffers, even more so ‐ they are large, and  once Oracle is done using them, they should just ʹdisappearʹ.  The large pool is not mandatory when using MTS, but it is highly recommended. If you do  not have a large pool and use MTS, the allocations come out of the shared pool as they  Expert one-on-one Oracle 114 always did in Oracle 7.3 and before. This will definitely lead to degraded performance  over some period of time, and should be avoided. The large pool will default to some size  if either one of two init.ora parameters, DBWn_IO_SLAVES or  PARALLEL_AUTOMATIC_TUNING are set. It is recommended that you set the size of  the large pool manually yourself. The default mechanism is typically not the appropriate  value for your situation.  Java Pool  The Java pool is the newest pool in the Oracle 8i database. It was added in version 8.1.5 to  support the running of Java in the database. If you code a stored procedure in Java or put  an EJB (Enterprise JavaBean) into the database, Oracle will make use of this chunk of  memory when processing that code. An early quirk of the Java pool, in Oracle 8.1.5, was  that it did not show up in the SHOW SGA command, and was not visible in V$SGASTAT  view. This was particularly confusing at the time, since the JAVA_POOL_SIZE init.ora  parameter that controls the size of this structure defaults to 20 MB. This oversight had  people wondering why their SGA was taking an extra 20 MB of RAM for the database.  As of version 8.1.6, however, the Java pool is visible in the V$SGASTAT view, as well as  the variable size in the SHOW SGA command. The init.ora parameter JAVA_POOL_SIZE  is used to fix the amount of memory allocated to the Java pool for all session‐specific Java  code and data. In Oracle 8.1.5, this parameter could take values from 1 MB to 1 GB. In  Oracle 8.1.6 and later versions, the valid range of values is 32 KB to 1 GB in size. This is  contrary to the documentation, which stills refers to the old minimum of 1 MB.  The Java pool is used in different ways, depending on the mode in which the Oracle server  is running. In dedicated server mode, the Java pool includes the shared part of each Java  class, which is actually used per session. These are basically the read‐only parts (execution  vectors, methods, and so on) and are about 4 to 8 KB per class.   Thus, in dedicated server mode (which will most likely be the case for applications using  purely Java stored procedures), the total memory required for the Java pool is quite  modest and can be determined based on the number of Java classes you will be using. It  should be noted that none of the per‐session state is stored in the SGA in dedicated server  mode, as this information is stored in the UGA and, as you will recall, the UGA is  included in the PGA in dedicated server mode.  When executing in MTS mode, the Java pool includes:  • The shared part of each Java class, and   • Some of the UGA used for per‐session state of each session, which is allocated from  the java_pool within the SGA. The remainder of the UGA will be located as normal  in the shared pool, or if the large pool is configured it will be allocated there instead.   Expert one-on-one Oracle 115 As the total size of the Java pool is fixed, application developers will need to estimate the  total requirement of their applications, and multiply this by the number of concurrent  sessions they need to support. This number will dictate the overall size of the Java pool.  Each Java UGA will grow or shrink as needed, but bear in mind that the pool must be  sized such that all UGAs combined must be able to fit in at the same time.  In MTS mode, which will be the case for applications using CORBA or EJBs (as pointed  out in Chapter 1 on Developing Successful Oracle applications), the Java pool may need to be  very large. Instead of being a function of the number of classes you use in your  applications, it will be a function of the number of concurrent users. Just as the large pool  could become very large under MTS, the Java pool may also become very large.  Memory Structures Wrap‐Up  In this section, we have taken a look at the Oracle memory structure. We started at the  process and session level, taking a look at the PGA (Process Global Area) and UGA (User  Global Area), and their relationship to each other. We have seen how the mode in which  you connect to Oracle will dictate how memory is organized. A dedicated server  connection implies more memory used in the server process than under MTS, but that  MTS implies there will be the need for a significantly larger SGA. Then, we discussed the  components of the SGA itself, looking at its six main structures. We discovered the  differences between the shared pool and the large pool, and looked at why you might  want a large pool in order to ʹsaveʹ your shared pool. We covered the Java pool and how it  is used under various conditions. We looked at the block buffer cache and how that can be  subdivided into smaller, more focused pools.  Now we are ready to move onto the physical processes that make up the rest of an Oracle  instance.  The Processes  We have reached the last piece of the puzzle. Weʹve investigated the database and the set  of physical files that constitute a database. In covering the memory used by Oracle, we  have looked at one half of an instance. The last remaining architectural issue is the set of  processes that constitute the other half of the instance. Some of these processes, such as the  database block writer (DBWn), and the log writer (LGWR) have cropped up already. Here,  we will take a closer look at the function of each, what they do and why they do it. When  we talk of ʹprocessʹ here, it will be synonymous with ʹthreadʹ on operating systems where  Oracle is implemented with threads. So, for example, when we talk about the DBWn  process, the equivalent on windows is the DBWn thread.   There are three classes of processes in an Oracle instance:  Expert one-on-one Oracle 116 • Server Processes ‐ These perform work based on a clientʹs request. We have already  looked at dedicated, and shared servers to some degree. These are the server  processes.   • Background Processes ‐ These are the processes that start up with the database,  and perform various maintenance tasks, such as writing blocks to disk, maintaining  the online redo log, cleaning up aborted processes, and so on.   • Slave Processes ‐ These are similar to background processes but they are processes  that perform extra work on behalf of either a background or a server process.   We will take a look at the processes in each of these three classes to see how they fit into  the picture.  Server Processes  We briefly touched on these processes earlier in this section when we discussed dedicated  and shared servers. Here, we will revisit the two server processes and review their  architectures in more detail.  Both dedicated and shared servers have the same jobs ‐ they process all of the SQL you  give to them. When you submit a SELECT * FROM EMP query to the database, it is an  Oracle dedicated/shared server that will parse the query, and place it into the shared pool  (or find it in the shared pool already, hopefully). It is this process that will come up with  the query plan. It is this process that will execute the query plan, perhaps finding the  necessary data in the buffer cache, or reading the data from disk into the buffer cache.  These server processes are the ʹwork horseʹ processes. Many times, you will find these  processes to be the highest consumers of CPU time on your system, as they are the ones  that do your sorting, your summing, your joining ‐ pretty much everything.   In dedicated server mode, there will be a one‐to‐one mapping between a client session and  a server process (or thread, as the case may be). If you have 100 sessions on a UNIX  machine, there will be 100 processes executing on their behalf. Graphically it would look  like this:    Expert one-on-one Oracle 117 Your client application will have Oracle libraries linked into it. These libraries provide the  Application Program Interface (API) that you need in order to talk to the database. These  APIs know how to submit a query to the database, and process the cursor that is returned.  They know how to bundle your requests into network calls that the dedicated server will  know how to un‐bundle. This piece of the picture is called Net8. This is the networking  software/protocol that Oracle employs to allow for client server processing (even in a n‐ tier architecture, there is a client server program lurking). Oracle employs this same  architecture even if Net8 is not technically involved in the picture. That is, when the client  and server are on the same machine this two‐process (also known as two‐task) architecture  is employed. This architecture provides two benefits:  • Remote Execution ‐ It is very natural for the client application to be executing on a  machine other than the database itself.   • Address Space Isolation ‐ The server process has read‐write access to the SGA. An  errant pointer in a client process could easily corrupt data structures in the SGA, if  the client process and server process were physically linked together.   Earlier in this chapter we saw how these dedicated servers are ʹspawnedʹ or created by the  Oracle Net8 Listener process. We wonʹt cover that process again, but rather weʹll quickly  look at what happens when the listener is not involved. The mechanism is much the same  as it was with the listener, but instead of the listener creating the dedicated server via a  fork()/exec() in UNIX and an IPC (Inter Process Communication) call in Windows, it is the  client process itself that creates it. We can see this clearly on UNIX:   ops$tkyte@ORA8I.WORLD> select a.spid dedicated_server,    2             b.process clientpid    3    from v$process a, v$session b    4   where a.addr = b.paddr    5     and b.audsid = userenv(ʹsessionidʹ)    6  /        DEDICATED CLIENTPID  ‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐  7055      7054        ops$tkyte@ORA8I.WORLD> !/bin/ps ‐lp 7055   F S   UID   PID  PPID  C PRI NI     ADDR     SZ    WCHAN TTY   TIME CMD   8 S 30174  7055  7054  0  41 20 61ac4230  36815 639b1998 ?     0:00 oracle        ops$tkyte@ORA8I.WORLD> !/bin/ps ‐lp 7054   F S   UID   PID  PPID  C PRI NI     ADDR     SZ    WCHAN TTY   TIME CMD   8 S 12997  7054  6783  0  51 20 63eece30   1087 63eecea0 pts/7 0:00 sqlplus  Expert one-on-one Oracle 118 Here, I used a query to discover the Process ID (PID) associated with my dedicated server  (the SPID from V$PROCESS is the operating system process ID of the process that was  being used during the execution of that query). Also, by looking at the PROCESS column  in V$SESSION, I discovered the process ID of the client accessing the database. Using a  simple ps command, I can clearly see that the PPID (Parent Process ID) of my dedicated  server is in fact SQL*PLUS. In this case, it was SQL*PLUS that created my dedicated server,  via fork() / exec() commands.   Letʹs now take a look at the other type of Server process, the shared server process, in  more detail. These types of connections mandate the use of Net8 even if the client and  server are on the same machine ‐ you cannot use MTS without using the Net8 listener. As  we described earlier in this section, the client application will connect to the Net8 listener  and will be redirected to a dispatcher. The dispatcher will act as the conduit between the  client application and the shared server process. Following is a diagram of the architecture  of a shared server connection to the database:    Here, we can see that the client applications, with the Oracle libraries linked in, will be  physically connected to a MTS dispatcher. We may have many MTS dispatchers  configured for any given instance but it is not uncommon to have just one dispatcher for  many hundreds, to thousands, of users. The dispatcher is simply responsible for receiving  inbound requests from the client applications, and putting them into the SGA in a request  queue. The first available shared server process, which is basically the same as a dedicated  server process, will pick up the request from the queue, and attach the UGA of the  associated session (the ʹSʹ boxes depicted in the above diagram). The shared server will  process that request and place any output from it into the response queue. The dispatcher  is constantly monitoring the response queue for results, and transmits them back to the  client application. As far as the client is concerned, it cannot really tell if it is connected via  a dedicated server, or a MTS connection ‐ they appear to be the same. Only at the database  level is the difference apparent.  Expert one-on-one Oracle 119 Dedicated Server versus Shared Server  Before we continue onto the rest of the processes, weʹll discuss why there are two modes  of connections, and when one might be more appropriate over the other. Dedicated server  mode is by far the most common method of connection to the Oracle database for all SQL‐ based applications. It is the easiest to set up and provides the easiest way to establish  connections. It requires little to no configuration. MTS setup and configuration, while not  difficult, is an extra step. The main difference between the two is not, however, in their set  up. It is in their mode of operation. With dedicated server, there is a one‐to‐one mapping  between client session, and server process. With MTS there is a many‐to‐one relationship ‐  many clients to a shared server. As the name implies, shared server is a shared resource,  whereas the dedicated server is not. When using a shared resource, you must be careful  not to monopolize it for long periods of time. As we saw in Chapter 1, Developing  Successful Oracle Applications, with the EJB example running a long running stored  procedure, monopolizing this resource can lead to a system that appears to be hanging. In  the above picture, I have two shared servers. If I have three clients, and all of them attempt  to run a 45‐second process more or less at the same time, two of them will get their  response in 45 seconds; the third will get its response in 90 seconds. This is rule number  one for MTS ‐ make sure your transactions are short in duration. They can be frequent, but  they should be short (as characterized by OLTP systems). If they are not, you will get what  appears to be a total system slowdown due to shared resources being monopolized by a  few processes. In extreme cases, if all of the shared servers are busy, the system will  appear to be hang.   So, MTS is highly appropriate for an OLTP system characterized by short, frequent  transactions. In an OLTP system, transactions are executed in milliseconds ‐ nothing ever  takes more then a fraction of a second. MTS on the other hand is highly inappropriate for a  data warehouse. Here, you might execute a query that takes one, two, five, or more  minutes. Under MTS, this would be deadly. If you have a system that is 90 percent OLTP  and 10 percent ʹnot quite OLTPʹ, then you can mix and match dedicated servers and MTS  on the same instance. In this fashion, you can reduce the number of processes on the  machine dramatically for the OLTP users, and make it so that the ʹnot quite OLTPʹ users  do not monopolize their shared servers.  So, what are the benefits of MTS, bearing in mind that you have to be somewhat careful  about the transaction types you let use it? MTS does three things for us, mainly:  Reduces the number of OS processes/threads:   On a system with thousands of users, the OS may quickly become overwhelmed in trying  to manage thousands of processes. In a typical system, only a fraction of the thousands of  users are concurrently active at any point in time. For example, Iʹve worked on systems  Expert one-on-one Oracle 120 recently with 5000 concurrent users. At any one point in time, at most 50 were active. This  system would work effectively with 50 shared server processes, reducing the number of  processes the operating system has to manage by two orders of magnitude (100 times).  The operating system can now, to a large degree, avoid context switching.  Allows you to artificially limit the degree of concurrency:   As a person who has been involved in lots of benchmarks, the benefits of this are obvious  to me. When running benchmarks, people frequently ask to run as many users as possible  until the system breaks. One of the outputs of these benchmarks is always a chart that  shows the number of concurrent users versus number of transactions:    Initially, as you add concurrent users, the number of transactions goes up. At some point  however, adding additional users does not increase the number of transactions you can  perform per second ‐ it tends to go flat. The throughput has peaked and now response  time starts to go up (we are doing the same number of TPS, but the end users are  observing slower response times). As you continue adding users, you will find that the  throughput will actually start to decline. The concurrent user count before this drop off is  the maximum degree of concurrency you want to allow on the system. Beyond this point,  the system is becoming flooded and the queues are forming to perform work. Much like a  backup at a tollbooth, the system can no longer keep up. Not only does response time rise  dramatically at this point, but throughput from the system falls as well. If we limit the  maximum concurrency to the point right before this drop, we can sustain maximum  throughput, and minimize the increase in the response time for most users. MTS allows us  to limit the maximum degree of concurrency on our system to this number.  Reduces the memory needed on the system:   This is one of the most highly touted reasons for using MTS ‐ it reduces the amount of  required memory. It does, but not as significantly as you might think. Remember that  Expert one-on-one Oracle 121 when we use MTS, the UGA is located in the SGA. This means that when switching over  to MTS, you must be able to accurately determine your expected UGA memory needs, and  allocate appropriately in the SGA, via the LARGE_POOL. So, the SGA requirements for  the MTS configuration are typically very large. This memory must be pre‐allocated and  thus, can only be used by the database. Contrast this with dedicated server, where anyone  can use any memory not allocated to the SGA. So, if the SGA is much larger due to the  UGA being located in it, where does the memory savings come from? It comes from  having that many less PGAs allocated. Each dedicated/shared server has a PGA. This is  process information. It is sort areas, hash areas, and other process related structures. It is  this memory need that you are removing from the system by using MTS. If you go from  using 5000 dedicated servers to 100 shared servers, it is the cumulative sizes of the 4900  PGAs you no longer need, that you are saving with MTS.  Of course, the final reason to use MTS is when you have no choice. If you want to talk to  an EJB in the database, you must use MTS. There are many other advanced connection  features that require the use of MTS. If you want to use database link concentration  between databases, for example, then you must be using MTS for those connections.  A Recommendation  Unless your system is overloaded, or you need to use MTS for a specific feature, a  dedicated server will probably serve you best. A dedicated server is simple to set up, and  makes tuning easier. There are certain operations that must be done in a dedicated server  mode so every database will have either both, or just a dedicated server set up.  On the other hand, if you have a very large user community and know that you will be  deploying with MTS, I would urge you to develop and test with MTS. It will increase your  likelihood of failure if you develop under just a dedicated server and never test on MTS.  Stress the system, benchmark it, make sure that your application is well behaved under  MTS. That is, make sure it does not monopolize shared servers for too long. If you find  that it does so during development, it is much easier to fix than during deployment. You  can utilize features such as the Advanced Queues (AQ) to turn a long running process into  an apparently short one, but you have to design that into your application. These sorts of  things are best done when you are developing.  Note If you are already using a connection‐pooling feature in your application (for  example, you are using the J2EE connection pool), and you have sized your  connection pool appropriately, using MTS will only be a performance inhibitor. You  already sized your connection pool to cater for the number of concurrent connections  that you will get at any point in time ‐ you want each of those connections to be a  direct dedicated server connection. Otherwise, you just have a connection pooling  feature connecting to yet another connection pooling feature.   Expert one-on-one Oracle 122 Background Processes  The Oracle instance is made up of two things: the SGA and a set of background processes.  The background processes perform the mundane maintenance tasks needed to keep the  database running. For example, there is a process that maintains the block buffer cache for  us, writing blocks out to the data files as needed. There is another process that is  responsible for copying an online redo log file to an archive destination as it fills up. There  is another process responsible for cleaning up after aborted processes, and so on. Each of  these processes is pretty focused on its job, but works in concert with all of the others. For  example, when the process responsible for writing to the log files fills one log and goes to  the next, it will notify the process responsible for archiving that full log file that there is  work to be done.  There are two classes of background processes: those that have a focused job to do (as we  have just described), and those that do a variety of other jobs. For example, there is a  background process for the internal job queues in Oracle. This process monitors the job  queues and runs whatever is inside of them. In many respects, it resembles a dedicated  server process, but without a client connection. We will look at each of these background  processes now, starting with the ones that have a focused job, and then going into the ʹall‐ purposeʹ processes.  Focused Background Processes  The following diagram depicts the Oracle background processes that have a focused  purpose:    You may not see all of these processes when you start your instance, but the majority of  them will be present. You will only see ARCn (the archiver) if you are in Archive Log  Mode and have enabled automatic archiving. You will only see the LMD0, LCKn, LMON,  and BSP (more details on those processes below) processes if you are running Oracle  Parallel Server (a configuration of Oracle that allows many instances on different machines  Expert one-on-one Oracle 123 in a cluster mount), and open the same physical database. For the sake of clarity, missing  from the above picture are the MTS dispatcher (Dnnn) and shared server (Snnn) processes.  As we just covered them in some detail, I left them out in order to make the diagram a  little more readable. The previous figure depicts what you might ʹseeʹ if you started an  Oracle instance, and mounted and opened a database. For example, on my UNIX system,  after starting the instance up, I have the following processes:   $ /bin/ps ‐aef | grep ʹora_.*_ora8i$ʹ    ora816 20642     1  0   Jan 17 ?        5:02 ora_arc0_ora8i    ora816 20636     1  0   Jan 17 ?      265:44 ora_snp0_ora8i    ora816 20628     1  0   Jan 17 ?       92:17 ora_lgwr_ora8i    ora816 20626     1  0   Jan 17 ?        9:23 ora_dbw0_ora8i    ora816 20638     1  0   Jan 17 ?        0:00 ora_s000_ora8i    ora816 20634     1  0   Jan 17 ?        0:04 ora_reco_ora8i    ora816 20630     1  0   Jan 17 ?        6:56 ora_ckpt_ora8i    ora816 20632     1  0   Jan 17 ?      186:44 ora_smon_ora8i    ora816 20640     1  0   Jan 17 ?        0:00 ora_d000_ora8i    ora816 20624     1  0   Jan 17 ?        0:05 ora_pmon_ora8i  They correspond to the processes depicted above, with the exception of the SNPn process  listed (which we will cover shortly; it is not a ʹfocusedʹ background process). It is  interesting to note the naming convention used by these processes. The process name  starts with ora_. It is followed by four characters representing the actual name of the  process, and then by _ora8i. As it happens, my ORACLE_SID (site identifier) is ora8i. On  UNIX, this makes it very easy to identify the Oracle background processes and associate  them with a particular instance (on Windows, there is no easy way to do this, as the  backgrounds are threads in a larger, single process). What is perhaps most interesting, but  not readily apparent from the above, is that they are all really the same exact binary. Search as  hard as you like but you will not find the arc0 binary executable on disk anywhere. You  will not find LGWR or DBW0. These processes are all really oracle (thatʹs the name of the  binary executable that is run). They just alias themselves upon start‐up in order to make it  easier to identify which process is which. This enables a great deal of object code to be  efficiently shared on the UNIX platform. On Windows, this is not nearly as interesting, as  they are just threads within the process ‐ so of course they are one big binary.  letʹs now take a look at the function performed by each process.  PMON ‐ The Process Monitor  This process is responsible for cleaning up after abnormally terminated connections. For  example, if your dedicated server ʹfailsʹ or is killed for some reason, PMON is the process  responsible for releasing your resources. PMON will rollback uncommitted work, release  locks, and free SGA resources allocated to the failed process.  Expert one-on-one Oracle 124 In addition to cleaning up after aborted connections, PMON is responsible for monitoring  the other Oracle background processes and restarting them if necessary (and if possible). If  a shared server or a dispatcher fails (crashes) PMON will step in, and restart another one  (after cleaning up for the failed process). PMON will watch all of the Oracle processes, and  either restart them or terminate the instance as appropriate. For example, it is appropriate  to restart the instance in the event the database log writer process, LGWR (), fails. This is a  serious error and the safest path of action is to terminate the instance immediately and let  normal recovery fix up the data. This is a very rare occurrence and should be reported to  Oracle support immediately.  The other thing PMON does for the instance, in Oracle 8i, is to register it with the Net8  listener. When an instance starts up, the PMON process polls the well‐known port address  (unless directed otherwise) to see whether or not a listener is up and running. The well‐ known/default port used by Oracle is 1521. Now, what happens if the listener is started on  some different port? In this case the mechanism is the same, except that the listener  address needs to be explicitly mentioned in the init.ora parameter via the  LOCAL_LISTENER setting. If the listener is started, PMON communicates with the  listener and passes to it relevant parameters, such as the service name.   SMON ‐ The System Monitor  SMON is the process that gets to do all of the jobs no one else wants to do. It is a sort of  ʹgarbage collectorʹ for the database. Some of the jobs it is responsible for include:  • Temporary space clean up ‐ With the advent of ʹtrueʹ temporary tablespaces, this  job has lessened, but has not gone away. For example, when building an index, the  extents allocated for the index during the creation are marked as TEMPORARY. If  the CREATE INDEX session is aborted for some reason, SMON is responsible for  cleaning them up. There are other operations that create temporary extents that  SMON would be responsible for as well.   • Crash recovery ‐ SMON is responsible for performing crash recovery of a failed  instance, upon restart.   • Coalescing free space ‐ If you are using dictionary‐managed tablespaces, SMON is  responsible for taking extents that are free in a tablespace and contiguous with  respect to each other, and coalescing them into one ʹbiggerʹ free extent. This occurs  only on dictionary managed tablespace with a default storage clause that has  pctincrease set to a non‐zero value.   • Recovering transactions active against unavailable files ‐ This is similar to its role  during database startup. Here SMON recovers failed transactions that were  skipped during instance/crash recovery due to a file(s) not being available to  recover. For example, the file may have been on a disk that was unavailable or not  mounted. When the file does become available, SMON will recover it.   Expert one-on-one Oracle 125 • Instance recovery of failed node in OPS ‐ In an Oracle Parallel Server  configuration, when a node in the cluster goes down (the machine fails), some other  node in the instance will open that failed nodeʹs redo log files, and perform a  recovery of all data for that failed node.   • Cleans up OBJ$ ‐ OBJ$ is a low‐level data dictionary table that contains an entry for  almost every object (table, index, trigger, view, and so on) in the database. There are  many times entries in here that represent deleted objects, or objects that represent  ʹnot thereʹ objects, used in Oracleʹs dependency mechanism. SMON is the process  that removes these no longer needed rows.   • Shrinks rollback segments ‐ SMON is the process that will perform the automatic  shrinking of a rollback segment to its optimal size, if it is set.   • ʹOfflinesʹ rollback segments ‐ It is possible for the DBA to ʹofflineʹ or make  unavailable, a rollback segment that has active transactions. It may be possible that  active transactions are using this off lined rollback segment. In this case, the  rollback is not really off lined; it is marked as ʹpending offlineʹ. SMON will  periodically try to ʹreallyʹ offline it in the background until it can.   That should give you a flavor of what SMON does. As evidenced by the ps listing of  processes I introduced above, SMON can accumulate quite a lot of CPU over time (the  instance from which ps was taken was an active instance that had been up for well over a  month). SMON periodically wakes up (or is woken up by the other backgrounds) to  perform these housekeeping chores.   RECO ‐ Distributed Database Recovery  RECO has a very focused job; it recovers transactions that are left in a prepared state  because of a crash or loss of connection during a two‐phase commit (2PC). A 2PC is a  distributed protocol that allows for a modification that affects many disparate databases to  be committed atomically. It attempts to close the window for distributed failure as much  as possible before committing. In a 2PC between N databases, one of the databases,  typically (but not always) the one the client logged in to initially, will be the coordinator.  This one site will ask the other N‐1 sites if they are ready to commit. In effect, this one site  will go to the N‐1 sites, and ask them to be prepared to commit. Each of the N‐1 sites  reports back their ʹprepared stateʹ as YES or NO. If any one of the sites votes NO, the entire  transaction is rolled back. If all sites vote YES, then the site coordinator broadcasts a  message to make the commit permanent on each of the N‐1 sites.  If after some site votes YES, they are prepared to commit, but before they get the directive  from the coordinator to actually commit, the network fails or some other error occurs, the  transaction becomes an in‐doubt distributed transaction. The 2PC tries to limit the  window of time in which this can occur, but cannot remove it. If we have a failure right  then and there, the transaction will become the responsibility of RECO. RECO will try to  contact the coordinator of the transaction to discover its outcome. Until it does that, the  Expert one-on-one Oracle 126 transaction will remain in its uncommitted state. When the transaction coordinator can be  reached again, RECO will either commit the transaction or roll it back.  It should be noted that if the outage is to persist for an extended period of time, and you  have some outstanding transactions, you can commit/roll them back manually yourself.  You might want to do this since an in doubt distributed transaction can cause writers to  block readers ‐ this is the one time this can happen in Oracle. Your DBA could call the  DBA of the other database and ask them to query up the status of those in‐doubt  transactions. Your DBA can then commit or roll them back, relieving RECO of this task.  CKPT ‐ Checkpoint Process  The checkpoint process doesnʹt, as its name implies, do a checkpoint (thatʹs mostly the job  of DBWn). It simply assists with the checkpointing process by updating the file headers of  the data files. It used to be that CKPT was an optional process, but starting with version  8.0 of the database it is always started, so if you do a ps on UNIX, youʹll always see it there.  The job of updating data filesʹ headers with checkpoint information used to belong to the  LGWR (Log Writer) however, as the number of files increased along with the size of a  database over time, this additional task for LGWR became too much of a burden. If LGWR  had to update dozens, or hundreds, or even thousands of files, there would be a good  chance sessions waiting to commit these transactions would have to wait far too long.  CKPT removes this responsibility from LGWR.  DBWn ‐ Database Block Writer  The Database Block Writer (DBWn) is the background process responsible for writing  dirty blocks to disk. DBWn will write dirty blocks from the buffer cache, usually in order  to make more room in the cache (to free buffers for reads of other data), or to advance a  checkpoint (to move forward the position in an online redo log file from which Oracle  would have to start reading, in order to recover the instance in the event of failure). As we  discussed previously, when Oracle switches log files, a checkpoint is signaled. Oracle  needs to advance the checkpoint so that it no longer needs the online redo log file it just  filled up. If it hasnʹt been able to do that by the time we need to reuse that redo log file, we  get the ʹcheckpoint not completeʹ message and we must wait.  As you can see, the performance of DBWn can be crucial. If it does not write out blocks  fast enough to free buffers for us, we will see waits for FREE_BUFFER_WAITS and ʹWrite  Complete Waitsʹ start to grow.   We can configure more then one DBWn, up to ten in fact (DBW0 ... DBW9). Most systems  run with one database block writer but larger, multi‐CPU systems can make use of more  than one. If you do configure more then one DBWn, be sure to also increase the init.ora  parameter, DB_BLOCK_LRU_LATCHES. This controls the number of LRU list latches  (now called touch lists in 8i) ‐ in effect, you want each DBWn to have their own list. If each  Expert one-on-one Oracle 127 DBWn shares the same list of blocks to write out to disk then they would only end up  contending with other in order to access this list.  Normally, the DBWn uses asynchronous I/O to write blocks to disk. With asynchronous  I/O, DBWn gathers up a batch of blocks to be written, and gives them to the operating  system. DBWn does not wait for the OS to actually write the blocks out, rather it goes back  and collects the next batch to be written. As the OS completes the writes, it  asynchronously notifies DBWn that it completed the write. This allows DBWn to work  much faster than if it had to do everything serially. Weʹll see later, in the Slave Processes  section, how we can use I/O slaves to simulate asynchronous I/O on platforms or  configurations that do not support it.  I would like to make one final point about DBWn. It will, almost by definition, write out  blocks scattered all over disk ‐ DBWn does lots of scattered writes. When you do an  update, youʹll be modifying index blocks that are stored here and there and data blocks  that are randomly distributed on disk as well. LGWR, on the other hand, does lots of  sequential writes to the redo log. This is an important distinction, and one of the reasons  that Oracle has a redo log and a LGWR process. Scattered writes are significantly slower  then sequential writes. By having the SGA buffer dirty blocks and the LGWR process do  large sequential writes that can recreate these dirty buffers, we achieve an increase in  performance. The fact that DBWn does its slow job in the background while LGWR does  its faster job while the user waits, gives us overall better performance. This is true even  though Oracle may technically be doing more I/O then it needs to (writes to the log and to  the datafile) ‐ the writes to the online redo log could be skipped if, during a commit,  Oracle physically wrote the modified blocks out to disk instead.  LGWR ‐ Log Writer  The LGWR process is responsible for flushing to disk the contents of the redo log buffer,  located in the SGA. It does this:  • Every three seconds, or   • Whenever you commit, or   • When the redo log buffer is a third full or contains 1 MB of buffered data.   For these reasons, having an enormous redo log buffer is not practical ‐ Oracle will never  be able to use it all. The logs are written to with sequential writes as compared to the  scattered I/O DBWn must perform. Doing large batch writes like this is much more  efficient than doing many scattered writes to various parts of a file. This is one of the main  reasons for having a LGWR and redo logs in the first place. The efficiency in just writing  out the changed bytes using sequential I/O outweighs the additional I/O incurred. Oracle  could just write database blocks directly to disk when you commit but that would entail a  lot of scattered I/O of full blocks ‐ this would be significantly slower than letting LGWR  write the changes out sequentially.   Expert one-on-one Oracle 128 ARCn ‐ Archive Process  The job of the ARCn process is to copy an online redo log file to another location when  LGWR fills it up. These archived redo log files can then be used to perform media  recovery. Whereas online redo log is used to ʹfixʹ the data files in the event of a power  failure (when the instance is terminated), archive redo logs are used to ʹfixʹ data files in the  event of a hard disk failure. If you lose the disk drive containing the data file,  /d01/oradata/ora8i/system.dbf, we can go to our backups from last week, restore that old  copy of the file, and ask the database to apply all of the archived and online redo log  generated since that backup took place. This will ʹcatch upʹ that file with the rest of the  data files in our database, and we can continue processing with no loss of data.  ARCn typically copies online redo log files to at least two other locations (redundancy  being a key to not losing data!). These other locations may be disks on the local machine or,  more appropriately, at least one will be located on another machine altogether, in the  event of a catastrophic failure. In many cases, these archived redo log files are copied off  by some other process to some tertiary storage device, such as tape. They may also be sent  to another machine to be applied to a ʹstandby databaseʹ, a failover option offered by  Oracle.  BSP ‐ Block Server Process  This process is used exclusively in an Oracle Parallel Server (OPS) environment. An OPS is  a configuration of Oracle whereby more then one instance mounts and opens the same  database. Each instance of Oracle in this case is running on a different machine in a cluster,  and they all access in a read‐write fashion the same exact set of database files.  In order to achieve this, the SGA block buffer caches must be kept consistent with respect  to each other. This is the main goal of the BSP. In earlier releases of OPS this was  accomplished via a ʹpingʹ. That is, if a node in the cluster needed a read consistent view of  a block that was locked in exclusive mode by another node, the exchange of data was done  via a disk flush (the block was pinged). This was a very expensive operation just to read  data. Now, with the BSP, this exchange is done via very fast cache‐to‐cache exchange over  the clusters high‐speed connection.  LMON ‐ Lock Monitor Process  This process is used exclusively in an OPS environment. The LMON process monitors all  instances in a cluster to detect the failure of an instance. It then facilitates the recovery of  the global locks held by the failed instance, in conjunction with the distributed lock  manager (DLM) employed by the cluster hardware.  LMD ‐ Lock Manager Daemon  Expert one-on-one Oracle 129 This process is used exclusively in an OPS environment. The LMD process controls the  global locks and global resources for the block buffer cache in a clustered environment.  Other instances will make requests to the local LMD, in order to request it to release a lock  or help find out who has a lock. The LMD also handles global deadlock detection and  resolution.  LCKn ‐ Lock Process  The LCKn process is used exclusively in an OPS environment. This process is very similar  in functionality to the LMD described above, but handles requests for all global resources  other than database block buffers.  Utility Background Processes  These background processes are totally optional, based on your need for them. They  provide facilities not necessary to run the database day‐to‐day, unless you are using them  yourself, or are making use of a feature that uses them.   There are two of these utilitarian background processes. One deals with the running of  submitted jobs. Oracle has a batch job queue built into the database that allows you to  schedule either one‐off or recurring jobs to be executed. The other process manages and  processes the queue tables associated with the Advanced Queuing (AQ) software. The AQ  software provides a message‐oriented middle‐ware solution built into the database.  They will be visible in UNIX as any other background process would be ‐ if you do a ps  you will see them. In my ps listing above, you can see that I am running one job queue  process (ora_snp0_ora8I) and no queue processes on my instance.  SNPn ‐ Snapshot Processes (Job Queues)  The SNPn process is poorly named these days. In the first 7.0 release, Oracle provided  replication. This was done in the form of a database object known as a snapshot. The  internal mechanism for refreshing, or making current, these snapshots was the SNPn  process, the snapshot process. This process monitored a job table that told it when it  needed to refresh various snapshots in the system. In Oracle 7.1, Oracle Corporation  exposed this facility for all to use via a database package called DBMS_JOB. What was  solely the domain of the snapshot in 7.0 become the ʹjob queueʹ in 7.1 and later versions.  Over time, the parameters for controlling the behavior of the queue (how frequently it  should be checked and how many queue processes there should be) changed their name  from SNAPSHOT_REFRESH_INTERVAL and SNAPSHOT_REFRESH_PROCESSES to  JOB_QUEUE_INTERVAL and JOB_QUEUE_PROCESSES. The name of the operating  system process however was not changed.  Expert one-on-one Oracle 130 You may have up to 36 job queue processes. Their names will be SNP0, SNP1, … , SNP9,  SNPA, … , SNPZ. These job queuesʹ processes are used heavily in replication as part of the  snapshot or materialized view refresh process. Developers also frequently use them in  order to schedule one‐off (background) jobs or recurring jobs. For example, later in this  book we will show how to use the job queues to make things ʹapparently fasterʹ ‐ by doing  a little extra work in one place, we can make the end‐user experience much more pleasant  (similar to what Oracle does with LGWR and DBWn).  The SNPn processes are very much like a shared server, but with aspects of a dedicated  server. They are shared ‐ they process one job after the other, but they manage memory  more like a dedicated server would (UGA in the PGA issue). Each job queue process will  run exactly one job at a time, one after the other, to completion. That is why we may need  multiple processes if we wish to run jobs at the same time. There is no threading or pre‐ empting of a job. Once it is running, it will run to completion (or failure). Later on in  Appendix A on Necessary Supplied Packages, we will be taking a more in‐depth look at the  DBMS_JOB package and creative uses of this job queue.  QMNn ‐ Queue Monitor Processes  The QMNn process is to the AQ tables what the SNPn process is to the job table. They  monitor the Advanced Queues and alert waiting ʹdequeuersʹ of messages, that a message  has become available. They are also responsible for queue propagation ‐ the ability of a  message enqueued (added) in one database to be moved to a queue in another database  for dequeueing.  The queue monitor is an optional background process. The init.ora parameter  AQ_TM_PROCESS specifies creation of up to ten of these processes named QMN0, ... ,  QMN9. By default, there will be no QMNn processes.  EMNn ‐ Event Monitor Processes  The EMNn is part of the Advanced Queue architecture. It is a process that is used to notify  queue subscribers of messages they would be interested in. This notification is performed  asynchronously. There are Oracle Call Interface (OCI) functions available to register a  callback for message notification. The callback is a function in the OCI program that will  be invoked automatically whenever a message of interest is available in the queue. The  EMNn background process is used to notify the subscriber. The EMNn process is started  automatically when the first notification is issued for the instance. The application may  then issue an explicit message_receive(dequeue) to retrieve the message.   Slave Processes  Now we are ready to look at the last class of Oracle processes, the ʹslaveʹ processes. There  are two types of slave processes with Oracle ‐ I/O slaves and Parallel Query slaves.  Expert one-on-one Oracle 131 I/O Slaves  I/O slaves are used to emulate asynchronous I/O for systems, or devices, that do not  support it. For example, tape devices (notoriously slow) do not support asynchronous I/O.  By utilizing I/O slaves, we can mimic for tape drives what the OS normally provides for  disk drives. Just as with true asynchronous I/O, the process writing to the device, batches  up a large amount of data and hands it off to be written. When it is successfully written,  the writer (our I/O slave this time, not the OS) signals the original invoker, who removes  this batch of data from their list of data that needs to be written. In this fashion, we can  achieve a much higher throughput, since the I/O slaves are the ones spent waiting for the  slow device, while their caller is off doing other important work getting the data together  for the next write.  I/O slaves are used in a couple of places in Oracle 8i ‐DBWn and LGWR can make use of  them to simulate asynchronous I/O and the RMAN (Recovery MANager) will make use of  them when writing to tape.  There are two init.ora parameters controlling the use of I/O slaves:  • BACKUP_TAPE_IO_SLAVES ‐ This specifies whether I/O slaves are used by the  RMAN to backup, copy, or restore data to tape. Since this is designed for tape  devices, and tape devices may be accessed by only one process at any time, this  parameter is a Boolean, not a number of slaves to use as you would expect. RMAN  will start up as many slaves as is necessary for the number of physical devices  being used. When BACKUP_TAPE_IO_SLAVES = TRUE, an I/O slave process is  used to write to, or read from a tape device. If this parameter is FALSE (the default),  then I/O slaves are not used for backups. Instead, the shadow process engaged in  the backup will access the tape device.   • DBWn_IO_SLAVES ‐ This specifies the number of I/O slaves used by the DBWn  process. The DBWn process and its slaves always perform the writing to disk of  dirty blocks in the buffer cache. By default, the value is 0 and I/O slaves are not  used.   Parallel Query Slaves  Oracle 7.1 introduced parallel query capabilities into the database. This is the ability to  take a SQL statement such as a SELECT, CREATE TABLE, CREATE INDEX, UPDATE,  and so on and create an execution plan that consists of many execution plans that can be  done simultaneously. The outputs of each of these plans are merged together into one  larger result. The goal is to do an operation in a fraction of the time it would take if you  did it serially. For example, if you have a really large table spread across ten different files,  16 CPUs at your disposal, and you needed to execute an ad‐hoc query on this table, it  Expert one-on-one Oracle 132 might be advantageous to break the query plan into 32 little pieces, and really make use of  that machine. This is as opposed to just using one process to read and process all of that  data serially.  Summary  Thatʹs it ‐ the three pieces to Oracle. Weʹve covered the files used by Oracle, from the lowly,  but important, init.ora, to data files, redo log files and so on. Weʹve taken a look inside the  memory structures used by Oracle, both in the server processes and the SGA. Weʹve seen  how different server configurations such as MTS versus dedicated server mode for  connections will have a dramatic impact on how memory is used by the system. Lastly we  looked at the processes (or threads depending on the operating system) that make Oracle  do what it does. Now we are ready to look at the implementation of some other features in  Oracle such as Locking and Concurrency controls, and Transactions in the following chapters.  Expert one-on-one Oracle 133 Chapter 3: Locking and Concurrency  Overview  One of the key challenges in developing multi‐user, database‐driven applications is to  maximize concurrent access but, at the same time, to ensure that each user is able to read  and modify the data in a consistent fashion. The locking and concurrency controls that  allow this to happen are key features of any database, and Oracle excels in providing them.  However, Oracleʹs implementation of these features is unique and it is up to you, the  application developer, to ensure that when your application performs data manipulation,  it uses these mechanisms correctly. If you fail to do so, your application will behave in an  unexpected way and, inevitably, the integrity of your data will be compromised (as was  demonstrated in Chapter 1 on Developing successful Oracle Applications).  In this chapter weʹre going to take a detailed look at how Oracle locks data, and the  implications of this model for writing multi‐user applications. We will investigate the  granularity to which Oracle locks data, how Oracle achieves ʹmulti‐version read  consistencyʹ, and what that all means to you, the developer. When appropriate, Iʹll contrast  Oracleʹs locking scheme with other popular implementations, mostly to dispel the myth  that ʹrow level locking adds overheadʹ. It only adds overhead if the implementation adds  overhead.  What are Locks?  A lock is a mechanism used to regulate concurrent access to a shared resource. Note how I  used the term ʹshared resourceʹ, not ʹdatabase rowʹ. It is true that Oracle locks table data at  the row level, but it also uses locks at many other levels to provide concurrent access to  various resources. For example, while a stored procedure is executing, the procedure itself  is locked in a mode that allows others to execute it, but will not permit another user to  alter it in any way. Locks are used in the database to permit concurrent access to these  shared resources, while at the same time providing data integrity and consistency.  In a single‐user database, locks are not necessary. There is, by definition, only one user  modifying the information. However, when multiple users are accessing and modifying  data or data structures, it is crucial to have a mechanism in place to prevent concurrent  modifications to the same piece of information. This is what locking is all about.  It is very important to understand that there are as many ways to implement locking in a  database as there are databases. Just because you are experienced with the locking model  of one particular RDBMS does not mean you know everything about locking. For example,  before I got heavily involved with Oracle, I used other databases such as Sybase and  Informix. All three of these databases provide locking mechanisms for concurrency control,  Expert one-on-one Oracle 134 but there are deep and fundamental differences in the way locking is implemented in each  one. In order to demonstrate this, Iʹll outline my progression from a Sybase developer to  an Informix user and finally an Oracle developer. This happened many years ago, and the  Sybase fans out there will tell me ʹbut we have row‐level locking nowʹ. It is true: Sybase  now uses row‐level locking, but the way in which it is implemented is totally different to  the way in which it is done in Oracle. It is a comparison between apples and oranges, and  that is the key point.  As a Sybase programmer, I would hardly ever consider the possibility of multiple users  inserting data into a table concurrently ‐ it was something that just didnʹt often happen in  that database. At that time, Sybase provided only for page‐level locking and, since all the  data tended to be inserted into the last page of non‐clustered tables, concurrent inserts by  two users was simply not going to happen. Exactly the same issue affected concurrent  updates (since an UPDATE is really a DELETE followed by an INSERT). Perhaps this is  why Sybase, by default, commits or rolls back immediately after execution of each and  every statement.  Compounding the fact that, in most cases, multiple users could not simultaneously modify  the same table, was the fact that while a table modification was in progress, many queries  were also effectively blocked against that table. If I tried to query a table and needed a  page that was locked by an update, I waited (and waited and waited). The locking  mechanism was so poor that providing support for transactions that took more than a  microsecond was deadly ‐ the entire database would appear to ʹfreezeʹ if you did. I learned  a lot of bad habits here. I learned that transactions were ʹbadʹ, that you ought to commit  rapidly and never hold locks on data. Concurrency came at the expense of consistency.  You either wanted to get it right or get it fast. I came to believe that you couldnʹt have both.   When I moved on to Informix, things were better, but not by much. As long as I  remembered to create a table with row‐level locking enabled, then I could actually have  two people simultaneously insert data into that table. Unfortunately, this concurrency  came at a high price. Row‐level locks in the Informix implementation were expensive,  both in terms of time and memory. It took time to acquire and ʹunacquireʹ or release them,  and each lock consumed real memory. Also, the total number of locks available to the  system had to be computed prior to starting the database. If you exceeded that number  then you were just out of luck. Consequently, most tables were created with page‐level  locking anyway, and, as with Sybase, both row and page‐level locks would stop a query in  its tracks. As a result of all this, I found that once again I would want to commit as fast as I  could. The bad habits I picked up using Sybase were simply re‐enforced and, furthermore,  I learned to treat a lock as a very scarce resource, something to be coveted. I learned that  you should manually escalate locks from row‐level to table‐level to try to avoid acquiring  too many of them and bringing the system down.  Expert one-on-one Oracle 135 When I started using Oracle I didnʹt really bother reading the manuals to find out how  locking worked here. After all, I had been using databases for quite a while and was  considered something of an expert in this field (in addition to Sybase and Informix, I had  used Ingress, DB2, Gupta SQLBase, and a variety of other databases). I had fallen into the  trap of believing that I knew how things should work, so of course they will work in that  way. I was wrong in a big way.  It was during a benchmark that I discovered just how wrong I was. In the early days of  these databases, it was common for the vendors to ʹbenchmarkʹ for really large  procurements ‐ to see who could do the work the fastest, the easiest, with the most  features. It was set up between Informix, Sybase and Oracle. Oracle was first. Their  technical people came on‐site, read through the benchmark specs, and started setting it up.  The first thing I noticed was that they were going to use a database table to record their  timings, even though we were going to have many dozens of connections doing work,  each of which would frequently need to insert and update data in this log table. Not only  that, but they were going to read the log table during the benchmark as well! I, being a nice  guy, pulled one of them aside to ask him if he was crazy ‐ why would they purposely  introduce another point of contention into the system? Wouldnʹt the benchmark processes  all tend to serialize around their operations on this single table? Would we jam up the  benchmark by trying to read from this table as others were heavily modifying it? Why  would you want to introduce all of these extra locks you need to manage? I had dozens of  ʹwhy would you even consider thatʹ ‐ type of questions. The technical folks from Oracle  thought I was a little daft at that point. That is until I pulled up a window into Sybase or  Informix, and showed them the effects of two people inserting into a table, or someone  trying to query a table with others inserting rows (the query returns zero rows per second).  The differences between the way Oracle does it, and the way almost every other database  does it, are phenomenal ‐ they are night and day. Needless to say, neither Informix nor  Sybase were too keen on the database log table approach during their attempts. They  preferred to record their timings to flat files in the operating system.  The moral to this story is twofold; all databases are fundamentally different and, when  designing your application, you must approach each as if you never used a database before.  Things you would do in one database are either not necessary, or simply wonʹt work in  another database.  In Oracle you will learn that:  • Transactions are what databases are all about; they are good.   • You should defer committing as long as you have to. You should not do it quickly  to avoid stressing the system, as it does not stress the system to have long or large  transactions. The rule is commit when you must, and not before. Your transactions  should only be as small or large as your business logic dictates.   Expert one-on-one Oracle 136 • You should hold locks on data as long as you need to. They are tools for you to use,  not things to be avoided. Locks are not a scarce resource.   • There is no overhead involved with row level locking in Oracle, none.   • You should never escalate a lock (for example, use a table lock instead of row locks)  because it would be ʹbetter on the systemʹ. In Oracle it wonʹt be better for the system  ‐ it will save no resources.   • Concurrency and consistency can be achieved. You can get it fast and correct, every  time.   As we go through the remaining components in this chapter, weʹll be reinforcing the  above points.  Locking Issues  Before we discuss the various types of locks that Oracle uses, it is useful to look at some  locking issues ‐ many of which arise from badly designed applications that do not make  correct use (or make no use) of the databaseʹs locking mechanisms.  Lost Updates  A lost update is a classic database problem. Simply put, a lost update occurs when the  following events occur, in the order presented:  1. User 1 retrieves (queries) a row of data.   2. User 2 retrieves that same row.   3. User 1 modifies that row, updates the database and commits.   4. User 2 modifies that row, updates the database and commits.   This is called a lost update because all of the changes made in step three above will be lost.  Consider, for example, an employee update screen ‐ one that allows a user to change an  address, work number and so on. The application itself is very simple ‐ a small search  screen to generate a list of employees and then the ability to drill down into the details of  each employee. This should be a piece of cake. So, we write the application with no  locking on our part, just simple SELECT and UPDATE commands.  So, an end user (user 1) navigates to the details screen, changes an address on the screen,  hits Save, and receives confirmation that the update was successful. Fine, except that when  user 1 checks the record the next day, in order to send out a tax form, the old address is  still listed. How could that have happened? Unfortunately it can happen all too easily. In  this case another end user (USER2) had queried the same record about 5 minutes before  user 1 and still had the old data displayed on their screen. User 1 came along, queried up  the data on his terminal, performed his update, received confirmation, and even re‐ queried to see the change for himself. However, user 2 then updated the work telephone  Expert one-on-one Oracle 137 number field and hit save ‐ blissfully unaware of the fact that he has just overwritten user  1ʹs changes to the address field with the old data! The reason this can happen is that the  application developer, finding it easier to update all columns instead of figuring out  exactly which columns changed, wrote the program such that when one particular field is  updated, all fields for that record are ʹrefreshedʹ.  Notice that for this to happen, user 1 and user 2 didnʹt even need to be working on the  record at the exact same time. All it needed was for them to be working on the record at  about the same time  This is a database issue that Iʹve seen crop up time and time again when GUI  programmers, with little or no database training, are given the task of writing a database  application. They get a working knowledge of SELECT, INSERT, UPDATE and DELETE  and then set about writing the application. When the resulting application behaves in the  manner described above, it completely destroys peopleʹs confidence in it, especially since  it seems so random, so sporadic, and is totally irreproducible in a controlled environment  (leading the developer to believe it must be user error).  Many tools, such as Oracle Forms, transparently protect you from this behavior by  ensuring the record is unchanged from when you queried it and locked before you make  any changes to it ‐ but many others (such as a handwritten VB or Java program) do not.  What the tools that protect you do behind the scenes, or what the developers must do  themselves, is to use one of two types of locking.   Pessimistic Locking  This locking method would be put into action in the instant before we modify a value on  screen ‐ for example when the user selects a specific row and indicates their intention to  perform an update (by hitting a button on the screen, say). So, a user queries the data out  without locking:  scott@TKYTE816> SELECT EMPNO, ENAME, SAL FROM EMP WHERE DEPTNO = 10;             EMPNO ENAME             SAL  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐        7782 CLARK            2450        7839 KING             5000        7934 MILLER           1300  Eventually, the user picks a row they would like to update. Letʹs say in this case, they  choose to update the MILLER row. Our application will at that point in time (before they  make any changes on screen) issue the following command:   Expert one-on-one Oracle 138 scott@TKYTE816> SELECT EMPNO, ENAME, SAL    2    FROM EMP    3   WHERE EMPNO = :EMPNO    4     AND ENAME = :ENAME    5     AND SAL = :SAL    6     FOR UPDATE NOWAIT    7  /             EMPNO ENAME             SAL  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐        7934 MILLER           1300  What the application does is supply values for the bind variables from the data on the  screen (in this case 7934, MILLER and 1300) and re‐queries up this same row from the  database ‐ this time locking the row against updates by other sessions. This is why this  approach is called pessimistic locking. We lock the row before we attempt to update  because we doubt that the row will remain unchanged otherwise.  Since all tables have a primary key (the above SELECT will retrieve at most one record  since it includes the primary key, EMPNO) and primary keys should be immutable (we  should never update them) weʹll get one of three outcomes from this statement:  • If the underlying data has not changed, we will get our MILLER row back and this  row will be locked from updates by others (but not read).   • If another user is in the process of modifying that row, we will get an ORA‐00054  Resource Busy error. We are blocked and must wait for the other user to finish with  it.   • If, in the time between selecting the data and indicating our intention to update,  someone has already changed the row then we will get zero rows back. The data on  our screen is stale. The application needs to requery and lock the data before  allowing the end user to modify any of the data in order to avoid the lost update  scenario described above. In this case, with pessimistic locking in place, when user  2 attempts to update the telephone field the application would now recognize that  the address field had been changed and would re‐query the data. Thus user 2  would not overwrite user 1ʹs change with the old data in that field.   Once we have locked the row successfully, the application will issue some update and  commit the changes:  scott@TKYTE816> UPDATE EMP    2  SET ENAME = :ENAME, SAL = :SAL    3  WHERE EMPNO = :EMPNO;  1 row updated.  Expert one-on-one Oracle 139       scott@TKYTE816> commit;  Commit complete.  We have now very safely changed that row. It is not possible for us to overwrite someone  elseʹs changes as we verified the data did not change between the time we initially read it  out and when we locked it.  Optimistic Locking  The second method, referred to as optimistic locking, is to keep the old and new values in  the application and upon updating the data use an update like this:  Update table     Set column1 = :new_column1, column2 = :new_column2, ….   Where column1 = :old_column1     And column2 = :old_column2      …  Here, we are optimistically hoping that the data doesnʹt get changed. In this case, if our  update updates one row ‐ we got lucky, the data didnʹt change between the time we read  it out and the time we got around to submitting the update. If we update zero rows, we  lose ‐ someone else changed the data and now we must figure out what we want to do in  order to avoid the lost update. Should we make the end user re‐key the transaction after  querying up the new values for the row (potentially frustrating them no end as there is a  chance the row will change yet again on them)? Should we try to merge the values of the  two updates, performing update conflict resolution based on business rules (lots of code)?  Of course, for disconnected users, the last option is the only one available.  It should be noted that you can use a SELECT FOR UPDATE NOWAIT here as well. The  UPDATE above will in fact avoid a lost update, but it does stand a chance of blocking ‐  hanging while it waits for an UPDATE of that row by another session to complete. If all of  your applications use optimistic locking then using a straight UPDATE is generally OK,  rows are locked for a very short duration as updates are applied and committed. If some  of your applications use pessimistic locking, however, which will hold locks on rows for  relatively long periods, then you would want to consider using a SELECT FOR UPDATE  NOWAIT immediately prior to the UPDATE to avoid getting blocked by another session.  So, which method is best? In my experience, pessimistic locking works very well in Oracle  (but perhaps not other databases) and has many advantages over optimistic locking.   With pessimistic locking the user can have confidence that the data they are modifying on  the screen is currently ʹownedʹ by them ‐ they in effect have the record checked out and  Expert one-on-one Oracle 140 nobody else can modify it. Some would argue that if you lock the row before changes are  made, other users would be locked out from that row and the scalability of the application  would be decreased. The fact is that, however you do it, only one user will ultimately be  able to update the row (if we want to avoid the lost update). If you lock the row first, and  then update it, your end user has a better experience. If you donʹt lock it and try to update  it later your end user may put time and energy into making changes only to be told ʹsorry,  the data has changed, please try againʹ. To limit the time that a row is locked before  updating, you could have the application release the lock if the user walks away and  doesnʹt actually use the record for some period of time or use Resource Profiles in the  database to time out idle sessions.  Furthermore, locking the row in Oracle does not prevent reads of that record as in other  databases; the locking of the row does not prevent any normal activity from taking place.  This is due 100% to Oracleʹs concurrency and locking implementation. In other databases,  the converse is true. If I tried to do pessimistic locking in them, no application would work.  The fact that a locked row in those databases block queries prevents this approach from  even being considered. So, it may be necessary to unlearn ʹrulesʹ you have learned in one  database in order to be successful in a different database.  Blocking  Blocking occurs when one session holds a lock on a resource that another session is  requesting. As a result, the requesting session will be blocked, it will ʹhangʹ until the  holding session gives up the locked resource. In almost every case, blocking is avoidable.  In fact, if you do find yourself blocking in an interactive application you are most likely  suffering from the lost update bug described above (your logic is flawed and that is the  cause of the blocking).  There are four common DML statements that will block in the database ‐ INSERT,  UPDATE, DELETE, and SELECT FOR UPDATE. The solution to a blocked SELECT FOR  UPDATE is trivial: simply add the NOWAIT clause and it will no longer block. Instead,  your application would report back to the end user that the row is already locked. The  interesting cases are the remaining three DML statements. Weʹll look at each of them and  see why they should not block, and when they do ‐ how to correct that.  Blocked Inserts  The only time an INSERT will block is when you have a table with a primary key or  unique constraint placed on it and two sessions simultaneously attempt to insert a row  with the same value. One of the sessions will block until the other session either commits  (in which case the blocked session will receive an error about a duplicate value) or rolls  back (in which case the blocked session succeeds). This typically happens with  Expert one-on-one Oracle 141 applications that allow the end user to generate the primary key/unique column value. It  is most easily avoided via the use of Oracle sequences in the generation of primary keys as  they are a highly concurrent method of generating unique keys in a multi‐user  environment. In the event that you cannot use a sequence, you can use the technique  outlined in Appendix A, on the DBMS_LOCK package, where I demonstrate how to use  manual locks to avoid this issue.  Blocked Updates and Deletes  In an interactive application ‐ one where you query some data out of the database, allow  an end user to manipulate it and then ʹput it backʹ into the database, a blocked UPDATE or  DELETE indicates that you probably have a lost update problem in your code. You are  attempting to UPDATE a row that someone else is already updating, in other words that  someone else already has locked. You can avoid the blocking issue by using the SELECT  FOR UPDATE NOWAIT query to:  • Verify the data has not changed since you queried it out (lost update prevention)   • Lock the row (prevents the update or delete from blocking)   As discussed earlier, you can do this regardless of the locking approach you take ‐ both  pessimistic and optimistic locking may employ the SELECT FOR UPDATE NOWAIT to  verify the row has not changed. Pessimistic locking would use that statement the instant  the user indicated their intention to modify the data. Optimistic locking would use that  statement immediately prior to updating the data in the database. Not only will this  resolve the blocking issue in your application, it will also correct the data integrity issue.   Deadlocks  Deadlocks occur when two people hold a resource that the other wants. For example, if I  have two tables, A and B in my database, and each has a single row in it, I can  demonstrate a deadlock easily. All I need to do is open two sessions (two SQL*PLUS  sessions, for example), and in Session A, I update table A. In session B, I update table B.  Now, if I attempt to update table A in session B, I will become blocked. Session A has this  row locked already. This is not a deadlock, this is just blocking. We have not yet  deadlocked since there is a chance that the session A will commit or rollback, and session  B will simply continue at that point.  If we go back to session A, and then try to update table B, we will cause a deadlock. One of  the two sessions will be chosen as a ʹvictimʹ, and will have its statement rolled back. For  example, the attempt by session B to update table A may be rolled back, with an error such  as:   update a set x = x+1  Expert one-on-one Oracle 142        *  ERROR at line 1:  ORA‐00060: deadlock detected while waiting for resource  Session Aʹs attempt to update table B will remain blocked ‐ Oracle will not rollback the  entire transaction. Only one of the statements that contributed to the deadlock is rolled  back. Session B still has the row in table B locked, and session A is patiently waiting for the  row to become available. After receiving the deadlock message, session B must decide  whether to commit the outstanding work on table B, roll it back, or continue down an  alternate path and commit later. As soon as this session does commit or rollback, the other  blocked session will continue on as if nothing ever happened.  Oracle considers deadlocks to be so rare, so unusual, that it creates a trace file on the  server each and every time one does occur. The contents of the trace file will look  something like this:  *** 2001‐02‐23 14:03:35.041  *** SESSION ID:(8.82) 2001‐02‐23 14:03:35.001  DEADLOCK DETECTED  Current SQL statement for this session:  update a set x = x+1  The following deadlock is not an ORACLE error. It is a  deadlock due to user error in the design of an application  or from issuing incorrect ad‐hoc SQL. The following...  Obviously, Oracle considers deadlocks a self‐induced error on part of the application and,  for the most part, they are correct. Unlike in many other RDBMSs, deadlocks are so rare in  Oracle they can be considered almost non‐existent. Typically, you must come up with  artificial conditions to get one.  The number one cause of deadlocks in the Oracle database, in my experience, is un‐ indexed foreign keys. There are two cases where Oracle will place a full table lock on a  child table after modification of the parent table:  • If I update the parent tableʹs primary key (a very rare occurrence if you follow the  rules of relational databases that primary keys should be immutable), the child  table will be locked in the absence of an index.   • If I delete a parent table row, the entire child table will be locked (in the absence of  an index) as well.   So, as a demonstration of the first point, if I have a pair of tables set up such as:  tkyte@TKYTE816> create table p ( x int primary key );  Table created.  Expert one-on-one Oracle 143       tkyte@TKYTE816> create table c ( y references p );  Table created.        tkyte@TKYTE816> insert into p values ( 1 );  tkyte@TKYTE816> insert into p values ( 2 );        tkyte@TKYTE816> commit;  And then I execute:  tkyte@TKYTE816> update p set x = 3 where x = 1;  1 row updated.  I will find that my session has locked the table C. No other session can delete, insert or  update any rows in C. Again, updating a primary key is a huge ʹno‐noʹ in a relational  database, so this is generally not really an issue. Where I have seen this updating of the  primary key become a serious issue is when you use tools that generate your SQL for you  and those tools update every single column ‐ regardless of whether the end user actually  modified that column or not. For example, if you use Oracle Forms and create a default  layout on any table Oracle Forms by default will generate an update that modifies every  single column in the table you choose to display. If you build a default layout on the DEPT  table and include all three fields Oracle Forms will execute the following command  whenever you modify any of the columns of the DEPT table:   update dept set deptno=:1,dname=:2,loc=:3 where rowid=:4  In this case, if the EMP table has a foreign key to DEPT and there is no index on the  DEPTNO column in the EMP table ‐ the entire EMP table will be locked after an update to  DEPT. This is something to watch out for carefully if you are using any tools that generate  SQL for you. Even though the value of the primary key does not change, the child table  EMP will be locked after the execution of the above SQL statement. In the case of Oracle  Forms, the solution is to mark that tableʹs property update changed columns only to Yes.  Oracle Forms will generate an update statement that includes only the changed columns  (not the primary key).  Problems arising from deletion of a row in a parent table are far more common. If I delete  a row in table P, then the child table, C, will become locked ‐ preventing other updates  against C from taking place for the duration of my transaction (assuming no one else was  modifying C, of course; in which case my delete will wait). This is where the blocking and  deadlock issues come in. By locking the entire table C, I have seriously decreased the  concurrency in my database ‐ no one will be able to modify anything in C. In addition, I  have increased the probability of a deadlock, since I now ʹownʹ lots of data until I commit.  Expert one-on-one Oracle 144 The probability that some other session will become blocked on C is now much higher;  any session that tries to modify C will get blocked. Therefore, Iʹll start seeing lots of  sessions that hold some pre‐existing locks getting blocked in the database. If any of these  blocked sessions are, in fact, holding a lock that my session needs ‐ we will have a  deadlock. The deadlock in this case is caused by my session obtaining many more locks  then it ever needed. When someone complains of deadlocks in the database, I have them  run a script that finds un‐indexed foreign keys and ninety‐nine percent of the time we  locate an offending table. By simply indexing that foreign key, the deadlocks, and lots of  other contention issues, go away. Here is an example of how to automatically find these  un‐indexed foreign keys:   tkyte@TKYTE816> column columns format a30 word_wrapped  tkyte@TKYTE816> column tablename format a15 word_wrapped  tkyte@TKYTE816> column constraint_name format a15 word_wrapped        tkyte@TKYTE816> select table_name, constraint_name,    2       cname1 || nvl2(cname2,ʹ,ʹ||cname2,null) ||    3       nvl2(cname3,ʹ,ʹ||cname3,null) || nvl2(cname4,ʹ,ʹ||cname4,null) ||    4       nvl2(cname5,ʹ,ʹ||cname5,null) || nvl2(cname6,ʹ,ʹ||cname6,null) ||    5       nvl2(cname7,ʹ,ʹ||cname7,null) || nvl2(cname8,ʹ,ʹ||cname8,null)    6              columns    7    from ( select b.table_name,    8                  b.constraint_name,    9                  max(decode( position, 1, column_name, null )) cname1,   10                  max(decode( position, 2, column_name, null )) cname2,   11                  max(decode( position, 3, column_name, null )) cname3,   12                  max(decode( position, 4, column_name, null )) cname4,   13                  max(decode( position, 5, column_name, null )) cname5,   14                  max(decode( position, 6, column_name, null )) cname6,   15                  max(decode( position, 7, column_name, null )) cname7,   16                  max(decode( position, 8, column_name, null )) cname8,   17                  count(*) col_cnt   18             from (select substr(table_name,1,30) table_name,   19                          substr(constraint_name,1,30) constraint_name,   20                          substr(column_name,1,30) column_name,   21                          position   22                     from user_cons_columns ) a,   23                  user_constraints b   24            where a.constraint_name = b.constraint_name   25              and b.constraint_type = ʹRʹ   26            group by b.table_name, b.constraint_name   27         ) cons   28   where col_cnt > ALL  Expert one-on-one Oracle 145  29           ( select count(*)   30               from user_ind_columns i   31              where i.table_name = cons.table_name   32                and i.column_name in (cname1, cname2, cname3, cname4,   33                                      cname5, cname6, cname7, cname8 )   34                and i.column_position <= cons.col_cnt   35              group by i.index_name   36           )   37  /        TABLE_NAME                     CONSTRAINT_NAME COLUMNS  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  C                              SYS_C004710     Y  This script works on foreign key constraints that have up to 8 columns in them (if you  have more than that, you probably want to rethink your design). It starts by building an  inline view, named CONS in the above query. This inline view transposes the appropriate  column names in the constraint from rows into columns, the result being a row per  constraint and up to 8 columns that have the names of the columns in the constraint.  Additionally there is a column, COL_CNT, which contains the number of columns in the  foreign key constraint itself. For each row returned from the inline view we execute a  correlated subquery that checks all of the indexes on the table currently being processed. It  counts the columns in that index that match columns in the foreign key constraint and  then groups them by index name. So, it generates a set of numbers, each of which is a  count of matching columns in some index on that table. If the original COL_CNT is greater  than all of these numbers then there is no index on that table that supports that constraint.  If COL_CNT is less than all of these numbers then there is at least one index that supports  that constraint. Note the use of the NVL2 function (new to Oracle 8.15), which we used to  ʹglueʹ the list of column names into a comma‐separated list. This function takes three  arguments A, B, C. If argument A is not null then it returns argument B, else it returns  argument C. This query assumes that the owner of the constraint is the owner of the table  and index as well. If another user indexed the table, or the table is in another schema, it  will not work correctly (both rare events).  So, this script shows us that table C has a foreign key on the column Y, but no index. By  indexing Y, we can remove this locking issue all together. In addition to this table lock, an  un‐indexed foreign key can also be problematic in the following cases:  • When you have an ON DELETE CASCADE and have not indexed the child table.  For example, EMP is child of DEPT. DELETE DEPTNO = 10 should CASCADE to  EMP. If DEPTNO in EMP is not indexed, you will get a full table scan of EMP. This  full scan is probably undesirable, and if you delete many rows from the parent table,  the child table will be scanned once for each parent row deleted.   Expert one-on-one Oracle 146 • When you query from the parent to the child. Consider the EMP/DEPT example  again. It is very common to query the EMP table in the context of a DEPTNO. If you  frequently run the following query, say to generate a report, youʹll find that not  having the index in place will slow down the queries:  • select * from dept, emp  •   where emp.deptno = dept.deptno and dept.deptno = :X;  So, when do you not need to index a foreign key? The answer is, in general, when the  following conditions are met:  • You do not delete from the parent table.   • You do not update the parent tableʹs unique/primary key value (watch for  unintended updates to the primary key by tools!   • You do not join from the parent to the child (like DEPT to EMP)   If you satisfy all three above, feel free to skip the index ‐ it is not needed. If you do any of  the above, be aware of the consequences. This is the one very rare time when Oracle tends  to ʹover‐lockʹ data.  Lock Escalation  When lock escalation occurs, the system is decreasing the granularity of your locks. An  example would be the database system turning your 100 row‐level locks against a table  into a single table‐level lock. You are now using ʹone lock to lock everythingʹ and, typically,  you are also locking a whole lot more data than you were before. Lock escalation is used  frequently in databases that consider a lock to be a scarce resource, overhead to be avoided.  Important  Oracle will never escalate a lock. Never. Oracle never escalates locks, but it does practice lock conversion, or lock promotion ‐  terms that are often confused with lock escalation.   Note The terms ʹlock conversionʹ and ʹlock promotionʹ are synonymous ‐ Oracle typically  refers to the process as conversion.  It will take a lock at the lowest level possible (the least restrictive lock possible) and will  convert that lock to a more restrictive level. For example, if you select a row from a table  with the FOR UPDATE clause, two locks will be created. One lock is placed on the row(s)  you selected (and this will be an exclusive lock, no one else can lock that specific row in  exclusive mode). The other lock, a ROW SHARE TABLE lock, is placed on the table itself.  This will prevent other sessions from placing an exclusive lock on the table and thus will  prevent them from altering the structure of the table, for example. All other statements  against the table are permitted. Another session can even come in and make the table  Expert one-on-one Oracle 147 read‐only using LOCK TABLE X IN SHARE MODE, preventing modifications. In reality,  however, this other session cannot be allowed to prevent the modification that is already  taking place. So, as soon as the command to actually update that row is issued, Oracle will  convert the ROW SHARE TABLE lock into the more restrictive ROW EXCLUSIVE TABLE  lock and the modification will proceed. This lock conversion happens transparently.  Lock escalation is not a database ʹfeatureʹ. It is not a desired attribute. The fact that a  database supports lock escalation implies there is some inherent overhead in its locking  mechanism, that there is significant work performed to managed hundreds of locks. In  Oracle the overhead to have one lock or a million locks is the same ‐ none.  Types of Lock  The five general classes of locks in Oracle are listed below. The first three are common  (used in every Oracle database) and the last two are unique to OPS (Oracle Parallel Server).  We will introduce the OPS‐specific locks, but will concentrate on the common locks:  • DML locks ‐ DML stands for Data Manipulation Language, in general SELECT,  INSERT, UPDATE, and DELETE. DML locks will be, for example, locks on a  specific row of data, or a lock at the table level, which locks every row in the table.   • DDL locks ‐ DDL stands for Data Definition Language, in general CREATE,  ALTER, and so on. DDL locks protect the definition of the structure of objects.   • Internal locks and latches ‐ These are the locks Oracle uses to protect its internal  data structures. For example, when Oracle parses a query and generates an  optimized query plan, it will ʹlatchʹ the library cache in order to put that plan in  there for other sessions to use. A latch is a lightweight low‐level serialization device  employed by Oracle ‐ similar in function to a lock.   • Distributed locks ‐ These are used by OPS to ensure that resources on the various  nodes remain consistent with respect to each other. Distributed locks are held by a  database instance, not by individual transactions.   • PCM (Parallel Cache Management) Locks ‐ These are locks that protect one or  more cached data blocks in the buffer cache across multiple instances.   We will now take a more detailed look at the specific types of locks within each of these  general classes and implications of their use. There are more lock types than I can cover  here. The ones I am covering are the most common ones and are the ones that are held for  a long duration. The other types of lock are generally held for very short periods of time.   DML Locks  DML locks are used to ensure only one person at a time modifies a row, and that no one  can drop a table upon which you are working. Oracle will place these locks for you, more  or less transparently, as you do work.  Expert one-on-one Oracle 148 TX ‐ (Transaction) Locks  A TX lock is acquired when a transaction initiates its first change, and is held until the  transaction performs a COMMIT or ROLLBACK. It is used as a queueing mechanism so  that other sessions can wait for the transaction to complete. Each and every row you  modify or SELECT FOR UPDATE will ʹpointʹ to an associated TX lock. While this sounds  expensive, it is not. To understand why, we need a conceptual understanding of where  locks ʹliveʹ and how they are managed. In Oracle, locks are stored as an attribute of the  data (see Chapter 2, Architecture, for an overview of the Oracle block format). Oracle does  not have a traditional lock manager that keeps a big long list of every row that is locked in  the system. Many other databases do it that way because, for them, locks are a scarce  resource the use of which needs to be monitored. The more locks in use, the more they  have to manage so it is a concern in these systems if ʹtoo manyʹ locks are being used.  If Oracle had a traditional lock manager, the act of locking a row would resemble:  1. Find the address of the row you want to lock.   2. Get in line at the lock manager (must be serialized, it is a common in‐memory  structure.)   3. Lock the list.   4. Search through the list to see if anyone else has locked this row.   5. Create a new entry in the list to establish the fact that you have locked the row.   6. Unlock the list.   Now that you have the row locked, you can modify it. Later, as you commit your changes  you must:  7. Get in line again.   8. Lock the list of locks.   9. Search through it and release all of your locks.   10. Unlock the list.   As you can see, the more locks acquired, the more time spent on this operation, both  before and after modifying the data. Oracle does not do it that way. Oracle does it more  like this:  1. Find the address of the row you want to lock.   2. Go to the row.   3. Lock it (waiting for it if it is already locked, unless we are using the NOWAIT  option).   Thatʹs it, period. Since the lock is stored as an attribute of the data, Oracle does not need a  traditional lock manager. Our transaction will simply go to the data and lock it (if it is not  Expert one-on-one Oracle 149 locked already). The interesting thing is that the data may appear locked when we get to it,  even if it is not. When we lock rows of data in Oracle a transaction ID is associated with  the bock containing the data and when the lock is released, that transaction ID is left  behind. This transaction ID is unique to our transaction, and represents the rollback  segment number, slot, and sequence number. We leave that on the block that contains our  row to tell other sessions that ʹwe own this dataʹ (not all of the data on the block, just the  one row we are modifying). When another session comes along, it sees the lock ID and,  using the fact that it represents a transaction, it can quickly see if the transaction holding  the lock is still active. If it is not active, the data is theirs. If it is still active, that session will  ask to be notified as soon as it finishes. Hence, we have a queueing mechanism: the session  requesting the lock will be queued up waiting for that transaction to complete and then it  will get the data.  Here is a small example showing how this happens. We will use three V$ tables in order to  see how this works:  • V$TRANSACTION, which contains an entry for every active transaction/   • V$SESSION, which shows us the sessions logged in.   • V$LOCK, which contains an entry for all locks being held as well as for sessions  that are waiting on locks.   First, letʹs start a transaction:  tkyte@TKYTE816> update dept set deptno = deptno+10;  4 rows updated.        Now, letʹs look at the state of the system at this point:  tkyte@TKYTE816> select username,    2         v$lock.sid,    3         trunc(id1/power(2,16)) rbs,    4         bitand(id1,to_number(ʹffffʹ,ʹxxxxʹ))+0 slot,    5         id2 seq,    6         lmode,    7         request    8  from v$lock, v$session    9  where v$lock.type = ʹTXʹ   10    and v$lock.sid = v$session.sid   11    and v$session.username = USER   12  /        USERNAME        SID        RBS       SLOT        SEQ      LMODE    REQUEST  Expert one-on-one Oracle 150 ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  TKYTE             8          2         46        160          6          0        tkyte@TKYTE816> select XIDUSN, XIDSLOT, XIDSQN    2    from v$transaction    3  /            XIDUSN    XIDSLOT     XIDSQN  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐           2         46        160  The interesting points to note here are:   • The LMODE is 6 in the V$LOCK table and the request is 0. If you refer to the  definition of the V$LOCK table in the Oracle Server Reference Manual, you will find  that LMODE=6 is an exclusive lock. A value of 0 in the request means we are not  making a request ‐ we have the lock.   • There is only one row in this table. This V$LOCK table is more of a queueing table  than a lock table. Many people expect there would be four rows in V$LOCK since  we have four rows locked. What you must remember however is that Oracle does  not store a master list of every row locked anywhere. To find out if a row is locked,  we must go to that row.   • I took the ID1 and ID2 columns, and performed some manipulation on them.  Oracle needed to save three 16 bit numbers, but only had two columns in order to  do it. So, the first column ID1 holds two of these numbers. By dividing by 2^16 with  trunc(id1/power(2,16)) rbs and by masking out the high bits with  bitand(id1,to_number(ʹffffʹ,ʹxxxxʹ))+0 slot, I am able to get the two numbers that are  hiding in that one number back out.   • The RBS, SLOT, and SEQ values match the V$TRANSACTION information. This is  my transaction ID.   Now Iʹll start another session using the same user name, update some rows in EMP, and  then try to update DEPT:   tkyte@TKYTE816> update emp set ename = upper(ename);  14 rows updated.        tkyte@TKYTE816> update dept set deptno = deptno‐10;  I am now blocked in this session. If we run the V$ queries again, we see:   tkyte@TKYTE816> select username,    2         v$lock.sid,  Expert one-on-one Oracle 151   3             trunc(id1/power(2,16)) rbs,    4             bitand(id1,to_number(ʹffffʹ,ʹxxxxʹ))+0 slot,    5             id2 seq,    6         lmode,    7             request    8  from v$lock, v$session    9  where v$lock.type = ʹTXʹ   10    and v$lock.sid = v$session.sid   11    and v$session.username = USER   12  /        USERNAME        SID        RBS       SLOT        SEQ      LMODE    REQUEST  ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  TKYTE             8          2         46        160          6          0  TKYTE             9          2         46        160          0          6  TKYTE             9          3         82        163          6          0        tkyte@TKYTE816> select XIDUSN, XIDSLOT, XIDSQN    2    from v$transaction    3  /            XIDUSN    XIDSLOT     XIDSQN  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐           3         82        163           2         46        160  What we see here is that a new transaction has begun, with a transaction ID of (3,82,163).  Our new session, SID=9, has two rows in V$LOCK this time. One row represents the locks  that it owns (where LMODE=6). It also has a row in there that shows a REQUEST with a  value of 6. This is a request for an exclusive lock. The interesting thing to note here is that  the RBS/SLOT/SEQ values of this request row are the transaction ID of the holder of the  lock. The transaction with SID=8 is blocking the transaction with SID=9. We can see this  more explicitly simply by doing a self‐join of V$LOCK:   tkyte@TKYTE816> select             (select username from v$session where sid=a.sid) blocker,    2         a.sid,    3        ʹ is blocking ʹ,    4         (select username from v$session where sid=b.sid) blockee,    5             b.sid    6    from v$lock a, v$lock b    7   where a.block = 1    8     and b.request > 0  Expert one-on-one Oracle 152   9     and a.id1 = b.id1   10     and a.id2 = b.id2   11  /        BLOCKER         SID ʹISBLOCKINGʹ  BLOCKEE         SID  ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  TKYTE             8  is blocking  TKYTE             9  Now, if we commit our original transaction, SID=8, and rerun our query, we find that the  request row has gone:   tkyte@TKYTE816> select username,    2         v$lock.sid,    3             trunc(id1/power(2,16)) rbs,    4             bitand(id1,to_number(ʹffffʹ,ʹxxxxʹ))+0 slot,    5             id2 seq,    6         lmode,    7             request, block    8  from v$lock, v$session    9  where v$lock.type = ʹTXʹ   10    and v$lock.sid = v$session.sid   11    and v$session.username = USER   12  /        USERNAME        SID        RBS       SLOT        SEQ      LMODE    REQUEST  ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  TKYTE             9          3         82        163          6          0        tkyte@TKYTE816> select XIDUSN, XIDSLOT, XIDSQN    2    from v$transaction    3  /            XIDUSN    XIDSLOT     XIDSQN  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐           3         82        163  The request row disappeared the instant the other session gave up its lock. That request  row was the queuing mechanism. The database is able to wake up the blocked sessions the  instant the transaction is completed.  There are infinitely more ʹprettyʹ displays with various GUI tools, but in a pinch, having  knowledge of the tables you need to look at is very useful.  Expert one-on-one Oracle 153 However, before we can say that we have a good understanding of how the row locking in  Oracle works we must look at one last piece, that is, how the locking and transaction  information is managed with the data itself. It is part of the block overhead. In Chapter 2,  Architecture, we discussed how the basic format of a block included some leading  ʹoverheadʹ space in which to store a transaction table for that block. This transaction table  contains an entry for each ʹrealʹ transaction that has locked some data in that block. The  size of this structure is controlled by two physical attribute parameters on the CREATE  statement for an object:  • INITRANS ‐ the initial, pre‐allocated size of this structure. This defaults to 2 for  indexes and 1 for tables.   • MAXTRANS ‐ the maximum size to which this structure may grow. It defaults to  255.   So, each block starts life with, by default, one or two transaction slots. The number of  simultaneous active transactions that a block can ever have is constrained by the value of  MAXTRANS, and by the availability of space on the block. You may not be able to achieve  255 concurrent transactions on the block if there is not sufficient space to grow this  structure.  We can artificially demonstrate how this works by creating a table with a constrained  MAXTRANS. For example:   tkyte@TKYTE816> create table t ( x int ) maxtrans 1;  Table created.        tkyte@TKYTE816> insert into t values ( 1 );  1 row created.        tkyte@TKYTE816> insert into t values ( 2 );  1 row created.        tkyte@TKYTE816> commit;  Commit complete.  Now, in one session we issue:  tkyte@TKYTE816> update t set x = 3 where x = 1;  1 row updated.  and in another:  tkyte@TKYTE816> update t set x = 4 where x = 2;  Expert one-on-one Oracle 154 Now, since those two rows are undoubtedly on the same database block and we set  MAXTRANS (the maximum degree of concurrency for that block) to one, the second  session will be blocked. This demonstrates what happens when more than MAXTRANS  transactions attempt to access the same block simultaneously. Similarly, blocking may also  occur if the INITRANS is set low and there is not enough space on a block to dynamically  expand the transaction. In most cases the defaults of 1 and 2 for INITRANS is sufficient as  the transaction table will dynamically grow (space permitting), but in some environments  you may need to increase this setting to increase concurrency and decrease waits. An  example of when you might need to do this would be a table, or even more frequently, on  an index (since index blocks can get many more rows on them than a table can typically  hold) that is frequently modified. We may need to increase INITRANS to set aside ahead  of time sufficient space on the block for the number of expected concurrent transactions.  This is especially true if the blocks are expected to be nearly full to begin with, meaning  there is no room for the dynamic expansion of the transaction structure on the block.  TM ‐ (DML Enqueue) Locks  These locks are used to ensure that the structure of a table is not altered while you are  modifying its contents. For example, if you have updated a table, you will acquire a TM  lock on that table. This will prevent another user from executing DROP or ALTER  commands on that table. If they attempt to perform DDL on the table while you have a TM  lock on it, they will receive the following error message:   drop table dept             *  ERROR at line 1:  ORA‐00054: resource busy and acquire with NOWAIT specified  This is a confusing messag, at first, since there is no method to specify NOWAIT or WAIT  on a DROP TABLE at all. It is just the generic message you get when you attempt to  perform an operation that would be blocked, but the operation does not permit blocking.  As weʹve seen before, it is the same message that you get if you issue a SELECT FOR  UPDATE NOWAIT against a locked row.  Below, we see how these locks would appear to us in the V$LOCK table:   tkyte@TKYTE816> create table t1 ( x int );  Table created.        tkyte@TKYTE816> create table t2 ( x int );  Table created.        tkyte@TKYTE816> insert into t1 values ( 1 );  Expert one-on-one Oracle 155 1 row created.        tkyte@TKYTE816> insert into t2 values ( 1 );  1 row created.        tkyte@TKYTE816> select username,    2         v$lock.sid,    3             id1, id2,    4         lmode,    5             request, block, v$lock.type    6  from v$lock, v$session    7  where v$lock.sid = v$session.sid    8    and v$session.username = USER    9  /        USERNAME        SID        ID1        ID2   LMODE    REQUEST      BLOCK TY  ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐  TKYTE             8      24055          0       3          0          0 TM  TKYTE             8      24054          0       3          0          0 TM  TKYTE             8     327697        165       6          0          0 TX        tkyte@TKYTE816> select object_name, object_id from user_objects;        OBJECT_NAME                     OBJECT_ID  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  T1                                  24054  T2                                  24055  Whereas we only get one TX lock per transaction, we can get as many TM locks as the  objects we modify. Here, the interesting thing is that the ID1 column for the TM lock is the  object ID of the DML‐locked object so it easy to find the object on which the lock is being  held.  An interesting aside to the TM lock: the total number of TM locks allowed in the system is  configurable by you (for details, see the DML_LOCKS init.ora parameter definition in the  Oracle8i Server Reference manual). It may in fact be set to zero. This does not mean that  your database becomes a read‐only database (no locks), but rather that DDL is not  permitted. This is useful in very specialized applications, such as OPS, to reduce the  amount of intra‐instance coordination that would otherwise take place. You can also  remove the ability to gain TM locks on an objectbyobject basis, using the ALTER TABLE  TABLENAME DISABLE TABLE LOCK command.  DDL Locks  Expert one-on-one Oracle 156 DDL locks are automatically placed against objects during a DDL operation to protect  them from changes by other sessions. For example, if I perform the DDL operation ALTER  TABLE T, the table T will have an exclusive DDL lock placed against it, preventing other  sessions from getting DDL locks and TM locks on this table. DDL locks are held for the  duration of the DDL statement, and are released immediately afterwards. This is done, in  effect, by always wrapping DDL statements in implicit commits (or a commit/rollback  pair). It is for this reason that DDL always commits in Oracle. Every CREATE, ALTER,  and so on, statement is really executed as shown in this pseudo‐code:   Begin     Commit;     DDL‐STATEMENT     Commit;  Exception     When others then rollback;  End;  So, DDL will always commit, even if it is unsuccessful. DDL starts by committing ‐ be  aware of this. It commits first so that if it has to rollback, it will not roll back your  transaction. If you execute DDL, itʹll make permanent any outstanding work you have  performed, even if the DDL is not successful. If you need to execute DDL, but do not want  it to commit your existing transaction, you may use an autonomous transaction (see  Chapter 15, Autonomous Transactions, for further details).  There are three types of DDL locks:  • Exclusive DDL locks ‐ These prevent other sessions from gaining a DDL lock or  TM (DML) lock themselves. This means that you may query a table during a DDL  operation but may not modify it in any way.   • Share DDL locks ‐ This protects the structure of the referenced object against  modification by other sessions, but allows modifications to the data.   • Breakable Parse locks ‐ This allows an object, such as a query plan cached in the  shared pool, to register its reliance on some other object. If you perform DDL  against that object, Oracle will review the list of objects that have registered their  dependence, and invalidate them. Hence, these ʹlocksʹ are ʹbreakableʹ; they do not  prevent the DDL from occurring.   Most DDL takes an exclusive DDL lock. If you issue a statement such as:  Alter table t add new_column date;  the table T will be unavailable for modifications during the execution of that statement.  The table may be queried using SELECT during this time, but most other operations will  Expert one-on-one Oracle 157 be prevented, including all DDL statements. In Oracle 8i, some DDL operations may now  take place without DDL locks. For example, I can issue:   create index t_idx on t(x) ONLINE;  The ONLINE keyword modifies the method by which the index is actually built. Instead  of taking an exclusive DDL lock, preventing modifications of data, Oracle will only  attempt to acquire a low‐level (mode 2) TM lock on the table. This will effectively prevent  other DDL from taking place, but will allow DML to occur normally. Oracle accomplishes  this feat by keeping a record of modifications made to the table during the DDL statement,  and applying these changes to the new index as it finishes the CREATE. This greatly  increases the availability of data.  Other types of DDL take share DDL locks. These are taken out against dependent objects  when you create stored, compiled objects, such as procedures and views. For example, if  you execute:  Create view MyView  as  select *    from emp, dept   where emp.deptno = dept.deptno;  Share DDL locks will be placed against both EMP and DEPT, while the CREATE VIEW  command is being processed. We can modify the contents of these tables, but we cannot  modify their structure.  The last type of DDL lock is a breakable parse lock. When our session parses a statement,  a parse lock is taken against every object referenced by that statement. These locks are  taken in order to allow the parsed, cached statement to be invalidated (flushed) in the  shared pool if a referenced object is dropped or altered in some way.  A view that is invaluable for looking at this information is the DBA_DDL_LOCKS view.  There is no V$ view for us to look at. The DBA_DDL_LOCKS view is built on the more  mysterious X$ tables and, by default, it will not be installed in your database. You can  install this and other locking views by running the CATBLOCK.SQL script found in the  directory [ORACLE_HOME]/rdbms/admin. This script must be executed as the user SYS  in order to succeed. Once you have executed this script, you can run a query against the  view. For example in a single user database I see:   tkyte@TKYTE816> select * from dba_ddl_locks;        session                                                            mode mode       id OWNER  NAME                           TYPE                 held reqe  Expert one-on-one Oracle 158 ‐‐‐‐‐‐‐ ‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐ ‐‐‐‐        8 SYS    DBMS_APPLICATION_INFO          Body                 Null None        8 SYS    DBMS_APPLICATION_INFO          Table/Procedure/Type Null None        8 SYS    DBMS_OUTPUT                    Table/Procedure/Type Null None        8 SYS    DBMS_OUTPUT                    Body                 Null None        8 TKYTE  TKYTE                          18                   Null None        8 SYS    DATABASE                       18                   Null None        6 rows selected.  These are all the objects that my session is ʹlockingʹ. I have breakable parse locks on a  couple of the DBMS_* packages. These are a side effect of using SQL*PLUS; it calls  DBMS_APPLICATION_INFO, for example. I may see more than one copy of various  objects here ‐ this is normal, and just means I have more than one thing Iʹm using in the  shared pool that references these objects. It is interesting to note that in the view, the  OWNER column is not the owner of the lock; rather it is the owner of the object being  locked. This is why you see many SYS rows with ‐ SYS owns these packages, but they all  belong to my session.  To see a breakable parse lock in action, we will first create and run a stored procedure, P:   tkyte@TKYTE816> create or replace procedure p as begin null; end;    2  /  Procedure created.        tkyte@TKYTE816> exec p        PL/SQL procedure successfully completed.  The procedure, P, will now show up in the DBA_DDL_LOCKS view. We have a parse lock  on it:   tkyte@TKYTE816> select * from dba_ddl_locks;        session                                                            mode mode       id OWNER  NAME                           TYPE                 held reqe  ‐‐‐‐‐‐‐ ‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐ ‐‐‐‐        8 TKYTE  P                              Table/Procedure/Type Null None        8 SYS    DBMS_APPLICATION_INFO          Body                 Null None        8 SYS    DBMS_APPLICATION_INFO          Table/Procedure/Type Null None        8 SYS    DBMS_OUTPUT                    Table/Procedure/Type Null None        8 SYS    DBMS_OUTPUT                    Body                 Null None        8 TKYTE  TKYTE                          18                   Null None  Expert one-on-one Oracle 159       8 SYS    DATABASE                       18                   Null None        7 rows selected.  We then recompile our procedure, and query the view again:  tkyte@TKYTE816> alter procedure p compile;        Procedure altered.        tkyte@TKYTE816> select * from dba_ddl_locks;        session                                                            mode mode       id OWNER  NAME                           TYPE                 held reqe  ‐‐‐‐‐‐‐ ‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐ ‐‐‐‐        8 SYS    DBMS_APPLICATION_INFO          Body                 Null None        8 SYS    DBMS_APPLICATION_INFO          Table/Procedure/Type Null None        8 SYS    DBMS_OUTPUT                    Table/Procedure/Type Null None        8 SYS    DBMS_OUTPUT                    Body                 Null None        8 TKYTE  TKYTE                          18                   Null None        8 SYS    DATABASE                       18                   Null None        6 rows selected.  We find that P is now missing from the view ‐ our parse lock has been broken.   This view is useful to you, as a developer, when it is found that some piece of code wonʹt  compile in the test or development system ‐ it hangs and eventually times out. This  indicates that someone else is using it (actually running it) and you can use this view to  see who that might be. The same will happen with GRANTS and other types of DDL  against the object. You cannot grant EXECUTE on a procedure that is running, for example.  You can use the same method as above to discover the potential blockers and waiters.  Latches and Internal Locks (Enqueues)  Latches and enqueues are lightweight serialization devices used to coordinate multi‐user  access to shared data structures, objects and files.  Latches are locks that are held for extremely short periods of time, for example the time it  takes to modify an in‐memory data structure. They are used to protect certain memory  structures, such as the database block buffer cache or the library cache in the shared pool  (as described in Chapter 2, Architecture). Latches are typically requested internally in a  ʹwilling to waitʹ mode. This means that if the latch is not available, the requesting session  Expert one-on-one Oracle 160 will sleep for a short period of time and retry the operation later. Other latches may be  requested in an ʹimmediateʹ mode, meaning that the process will go do something else  rather than sit and wait for the latch to become available. Since many requestors may be  waiting for a latch at the same time, you may see some processes waiting longer than  others. Latches are assigned rather randomly, based on the ʹluck of the drawʹ, if you will.  Whichever session asks for a latch right after it was released will get it. There is no line of  latch waiters, just a ʹmobʹ of waiters constantly retrying.  Oracle uses atomic instructions like ʹtest and setʹ for operating on latches. Since the  instructions to set and free latches are atomic, the operating system itself guarantees that  only one process gets it. Since it is only one instruction, it can be quite fast. Latches are  held for short periods of time and provide a mechanism for clean‐up in case a latch holder  ʹdiesʹ abnormally while holding it. This cleaning up process would be performed by  PMON.  Enqueues are another, more sophisticated, serialization device, used when updating rows  in a database table, fro example. They differ from latches in that they allow the requestor  to ʹqueue upʹ and wait for the resource. With a latch request, the requestor is told right  away whether they got the latch or not. With an enqueue, the requestor will be blocked  until they actually attain it. As such, they are not as fast as a latch can be, but they do  provided functionality over and above that which a latch can offer. Enqueues may be  obtained at various levels, so you can have many ʹshareʹ locks and locks with various  degrees of ʹshareabilityʹ.  Manual Locking and User‐Defined Locks  So far we have looked mostly at locks that Oracle places for us transparently. When we  update a table, Oracle places a TM lock on it to prevent other sessions from dropping that  table (or perform most DDL in fact). We have TX locks that are left on the various blocks  we modify so others can tell what data we ʹownʹ. The database employs DDL locks to  protect objects from change while we are ourselves changing them. It uses latches and  locks internally to protect its own structure. Now, letʹs take a look at how we can get  involved in some of this locking action. Our options are:  • Manually locking data via a SQL statement.   • Creating our own locks via the DBMS_LOCK package.   We will briefly discuss why you might want to do each of these.  Manual Locking  We have, in fact, already seen a couple of cases where we might want to use manual  locking. The SELECT...FOR UPDATE statement is the predominant method of manually  Expert one-on-one Oracle 161 locking data. We used it in previous examples in order to avoid the lost update issue,  whereby one session would overwrite another sessionʹs changes. Weʹve seen it used as a  method to serialize access to detail records in order to enforce business rules (the resource  scheduler example from Chapter 1, Developing Successful Oracle Applications).  We can also manually lock data using the LOCK TABLE statement. This is actually only  used rarely, because of the coarseness of the lock. It simply locks the table, not the rows in  the table. If you start modifying the rows they will be ʹlockedʹ as normal. So, it is not a  method to save on resources (as it might be in other RDBMSs). You might use the LOCK  TABLE IN EXCLUSIVE MODE statement if you were writing a large batch update that  would affect most of the rows in a given table, and you wanted to be sure that no one  would ʹblockʹ you. By locking the table in this manner, you can be assured that your  update will be able to do all of its work without getting blocked by other transactions. It  would be the rare application however that has a LOCK TABLE statement in it.  Creating your own Locks  Oracle actually exposes to developers the enqueue lock mechanism that it uses internally,  via the DBMS_LOCK package (which we will cover in much more detail in Appendix A).  You might be wondering why you would want to create your own locks. The answer is  typically application‐specific. For example, you might use this package to serialize access  to some resource external to Oracle. Say you are using the UTL_FILE routine that allows  you to write to a file on the serverʹs file system. You might have developed a common  message routine that every application calls to record messages. Since the file is external,  Oracle wonʹt coordinate the many users trying to modify it simultaneously. In comes the  DBMS_LOCK package. Now, before you open, write, and close the file, you will request a  lock named after the file in exclusive mode and after you close the file, you will manually  release the lock. In this fashion, only one person at a time will be able to write a message to  this file. Everyone else will queue up. The DBMS_LOCK package allows you to manually  release a lock when you are done with it, or to give it up automatically when you commit,  or even to keep it as long as you are logged in.  What is Concurrency Control?  Concurrency controls are the collection of functions that the database provides in order to  allow many people to access and modify data simultaneously. The implementation of  locking in the database is perhaps the most crucial factor in determining the degree of  concurrency that your application can support (basically, how well it will scale). As we  discussed previously, there are many different types of locks ‐ from TX transaction locks,  which are extremely scalable both in terms of performance and cardinality (whether you  have one or one billion of them), to TM and DDL locks (applied in the least restrictive  mode whenever possible), to the internal locks that Oracle employs to mediate access to its  Expert one-on-one Oracle 162 shared data structures, from the very lightweight and fast latching mechanism to the  heavier, but feature‐rich, enqueue.  But concurrency control goes beyond locks. There are other things that the database can  do to provide controlled, yet highly concurrent, access to data. For example, there is a  feature of Oracle called multi‐versioning (introduced in Chapter 1). Since Oracle uses  multi‐versioning to provide read‐consistent views of data, we get the rather pleasant side  effect that a reader of data will never be blocked by a writer of data; ‐ writes do not block  reads. This is one of the fundamental differences between Oracle and the rest of the  databases out there. A read query in Oracle will never be blocked, it will never deadlock  with another session, and it will never get an answer that didnʹt exist in the database.   Oracleʹs multi‐versioning model for read consistency is always applied at the statement  level (for each and every query) and can also be applied at the transaction level. What I  would like to do in this section is to demonstrate how multi‐versioning ties in with the  various transaction isolation levels defined in the SQL92 standard.  Transaction Isolation Levels  The ANSI/ISO SQL92 standard defines four levels of transaction isolation, with different  possible outcomes for the same transaction scenario. That is, the same work performed in  the same fashion with the same inputs, may result in different answers, depending on  your isolation level. These isolation levels are defined in terms of three ʹphenomenaʹ that  are either permitted or not at a given isolation level:  • Dirty read ‐ The meaning of this is as bad as it sounds. You are permitted to read  uncommitted, or ʹdirtyʹ, data. This is the effect you would achieve by just opening  an OS file that someone else is writing, and reading whatever data happened to be  there. Data integrity is compromised, foreign keys violated, unique constraints  ignored.   • Non‐REPEATABLE READ ‐ This simply means that if you read a row at time T1,  and attempt to re‐read that row at time T2, the row may have changed. It may have  disappeared, it may have been updated, and so on.   • Phantom read ‐ This means that if you execute a query at time T1, and re‐execute it  at time T2, additional rows may have been added to the database, which will affect  your results. This differs from the non‐repeatable read in that in this case, data you  already read has not been changed but rather that more data satisfies your query  criteria than before.   The SQL92 isolation levels are defined based on whether or not they allow each of the  above phenomena:  Isolation Level  Dirty Read  Non‐REPEATABLE READ  Phantom Read  Expert one-on-one Oracle 163 Isolation Level  Dirty Read  Non‐REPEATABLE READ  Phantom Read  READ UNCOMMITTED   Permitted  Permitted  Permitted  READ COMMITTED      Permitted  Permitted  REPEATABLE            READ         Permitted  SERIALIZABLE            Oracle explicitly supports the READ COMMITTED and SERIALIZABLE isolation levels,  as they are defined in the standard. However, this doesnʹt tell the whole story. The SQL92  standard was attempting to set up isolation levels that would permit various degrees of  consistency for queries performed in each level. REPEATABLE READ is the isolation level  that they claim will guarantee a read consistent result from a query. In their definition,  READ COMMITTED does not give you consistent results and READ UNCOMMITTED is  the level to use to get non‐blocking reads.   In Oracle, READ COMMITTED has all of the attributes required to achieve Read‐ consistent queries. In other databases, READ COMMITTED queries can and will return  answers that never existed in the database at any point in time. Moreover, Oracle also  supports the spirit of READ UNCOMMITTED. The goal of providing a dirty read is to  supply a non‐blocking read, whereby queries are not blocked by, and do not block,  updates of the same data. However, Oracle does not need dirty reads to achieve this goal,  nor does it support them. Dirty reads are an implementation other databases must use in  order to provide non‐blocking reads.  In addition to the four defined SQL92 isolation levels, Oracle provides another level, read  only. A read‐only transaction is equivalent to a read‐only REPEATABLE READ or  SERIALIZABLE in SQL92. It only sees those changes that were committed at the time the  transaction began, but inserts, updates and deletes are not permitted in this mode (other  sessions may update data, but not the read‐only transaction). Using this mode you can  achieve REPEATABLE READ and SERIALIZABLE READ, without phantoms.  Letʹs now move on to discuss exactly how multi‐versioning and read consistency fits into  the above isolation schemes, and how other databases that do not support multi‐ versioning would achieve the same results. This is instructive for anyone who has used  another database and believes they understand how the isolation levels must work. It is  also interesting to see how a standard that was supposed to remove the differences from  the databases, SQL92, actually allows for it. The standard, while very detailed, can be  implemented in very different ways.  READ UNCOMMITTED  Expert one-on-one Oracle 164 The READ UNCOMMITTED isolation level permits for dirty reads. Oracle does not make  use of dirty reads, nor does it even allow for them. The basic goal of a READ  UNCOMMITTED isolation level is to provide a standards‐based definition that caters for  non‐blocking reads. As we have seen, Oracle provides for non‐blocking reads by default.  You would be hard‐pressed to make a SELECT query block in the database (there is the  special case of a distributed in‐doubt transaction, which we discuss in Chapter 4). Every  single query, be it a SELECT, INSERT, UPDATE, or DELETE, executes in a read‐consistent  fashion.  In Chapter 1, Developing building Successful Oracle Applications, Oracleʹs method of  obtaining read‐consistency was demonstrated by way of an accounts example. Weʹre now  going to revisit that example to discuss in more detail what happens in Oracle, using  multi‐versioning, and what would happen in any number of other databases. Again, we  are assuming one database row per block.  We will start with the same basic table and query:  create table accounts  ( account_number number primary key,    account_balance number  );        select sum(account_balance) from accounts;  Before the query begins, we have the following data:  Row  Account Number  Account Balance  1  123  $500.00  2  456  $240.25  ...  ...  ...  342,023  987  $100.00    Now, our select statement starts executing and reads row 1, row 2, and so on. At some  point while we are in the middle of the query, a transaction moves $400.00 from account  123 to account 987. This transaction does the two updates, but does not commit. The table  now looks like this:  Row  Account Number  Account Balance  LOCKED  1  123  ($500.00) changed to $100.00  X  Expert one-on-one Oracle 165 Row  Account Number  Account Balance  LOCKED  2  456  $240.25    ...  ...  ...    342,023  987  ($100.00) changed to $500.00  X  So, two of those rows are locked ‐ if anyone tried to update them they would be blocked.  So far the behavior we are seeing is more or less consistent across all databases. The  difference will be in what happens when the query gets to the locked data.  When the query we are executing gets to the locked block, it will notice that the data on it  has changed since the beginning of its execution. In order to provide a consistent (correct)  answer, Oracle will at this point recreate the block with the locked data as it existed when  the query began. That is, Oracle takes a detour around the lock ‐ it reads around it,  reconstructing it from the rollback segment. A consistent and correct answer comes back  without waiting for the transaction to commit.  Now, a database that allowed a dirty read would simply return the value it saw in account  987 at the time it read it, in this case $500. The query would count the transferred $400  twice and would present a total that never existed in the accounts table at any point in  time. In a multi‐user database, a dirty read can be a dangerous feature and, personally, I  have never seen the usefulness of it. It not only returns the wrong answer, but it may see  data that will never actually exist in the database at any point in time. Say that, rather than  transferring, the transaction was actually just depositing $400 in account 987. The dirty  read would count the $400 and get the ʹrightʹ answer, wouldnʹt it? Well, suppose the  uncommitted transaction was rolled back. We have just counted $400 that was never  actually in the database.  The point here is that dirty read is not a feature ‐ rather it is a liability. In Oracle, it is just  not needed. You get all of the advantages of a dirty read (no blocking) without any of the  incorrect results.  READ COMMITTED  The READ COMMITTED isolation level states that a transaction may only read data that  was committed before the transaction began. There are no dirty reads. There may be non‐  REPEATABLE READ s (re‐reads of the same row return a different answer) and phantom  reads (newly inserted rows become visible to a query that were not visible earlier in the  transaction). READ COMMITTED is perhaps the most commonly used isolation level in  database applications everywhere. It is rare to see a different isolation level used.  READ COMMITTED isolation is not as cut and dry as it sounds. If you look at the matrix  above, it looks straightforward. Obviously, given the rules above, a query executed in any  Expert one-on-one Oracle 166 database using READ COMMITTED isolation would behave in the same way, would it  not? It will not. If you query multiple rows in a single statement then, in almost every  other database, READ COMMITTED isolation can be as bad as a dirty read, depending on  the implementation.   In Oracle, using multi‐versioning and read consistent queries, the answer we get from the  accounts query is the same in READ COMMITTED as it was in the READ  UNCOMMITTED example. Oracle will reconstruct the modified data as it appeared when  the query began, returning the answer that was in the database when the query started.  Letʹs now take a look at how our above example might work in READ COMMITTED  mode in other databases ‐ you might find the answer surprising. Weʹll pick up our  example at the point described in the previous table:  • We are in the middle of the table. We have read and summed the first N rows.   • The other transaction has moved $400 from account 123 to 987.   • It has not yet committed, so rows 123 and 987 are locked.   We know what happens in Oracle when it gets to account 987 ‐ it will read around the  modified data, find out it should be $100.00 and complete. Letʹs see how another database,  running in some default READ COMMITTED mode, might arrive at the answer:  Time  Query  Account transfer transaction  T1  Reads row 1, sum = $500 so far.    T2  Reads row 2, sum = $740.25 so far.    T3    Updates row 1, puts an exclusive lock on  block 1 preventing other updates and  reads. Row 1 now has $100.  T4  Reads row N, sum = ....    T5    Updates row 342023, puts an exclusive  lock on this block. Row now has $500.  T6  Reads row 342023, discovers that it  has been modified. This session will  block and wait for this block to  become available. All processing on  this query stops.     T7    Commits transaction.  T8  Reads row 342,023, sees $500 and  presents final answer.     Expert one-on-one Oracle 167 The first thing to notice is that this other database, upon getting to account 987, will block  our query. This session must wait on that row until the transaction holding the exclusive  lock commits. This is one reason why many people have a bad habit of committing in the  middle of their transactions. Updates interfere with reads in most other databases. The  really bad news in this scenario is that we are making the end user wait for the wrong  answer. You still receive an answer that never existed in the database at any point in time,  as with the dirty read, but this time we made them wait for the wrong answer.  The important lesson here is that various databases executing in the same, apparently safe  isolation level, can, and will, return very different answers under the exact same  circumstances. It is important to understand that, in Oracle, non‐blocking reads are not  had at the expense of correct answers. You can have your cake and eat it too, sometimes.   REPEATABLE READ  The goal of REPEATABLE READ in SQL92 is to provide an isolation level that gives  consistent, correct answers, and prevents lost updates. Weʹll take a look at both examples,  and see what we have to do in Oracle to achieve this, and what happens in other systems.  Getting a Consistent Answer  If I have a REPEATABLE READ isolation, the results from a given query must be  consistent with respect to some point in time. Most databases (not Oracle) achieve  REPEATABLE READs via the use of row‐level, shared read locks. A shared read lock  prevents other sessions from modifying data that you have read. This of course decreases  concurrency. Oracle opted for the more concurrent, multi‐versioning model to provide  read consistent answers.  In Oracle, using multi‐versioning, you get an answer that is consistent with respect to the  point in time the query began execution. In other databases, using shared read locks, you  get an answer that is consistent with respect to the point in time the query completes ‐ that  is, when you can get the answer at all (more on this in a moment).  In a system that employs a shared read lock to provide REPEATABLE READs, you would  observe rows in a table getting locked as the query processed them. So, using the example  from above, as our query reads the accounts table, it would leave shared read locks on  each row:  Time  Query  Account transfer transaction  T1  Reads row 1, sum = $500 so far.  Block 1 has a shared read lock  on it.     Expert one-on-one Oracle 168 Time  Query  Account transfer transaction  T2  Reads row 2, sum = $740.25 so  far. Block 2 has a shared read  lock on it.     T3    Attempts to update row 1 but is blocked.  Transaction is suspended until it can obtain  an exclusive lock.  T4  Reads row N, sum = ....    T5  Reads row 342023, sees $100 and  presents final answer.     T6  Commits transaction.    T7    Updates row 1, puts an exclusive lock on this  block. Row now has $100.  T8    Updates row 342023, puts an exclusive lock  on this block. Row now has $500. Commits.  This table shows that we now get the correct answer, but at the cost of physically  serializing the two transactions. This is one of the side effects of shared read locks for  consistent answers: readers of data will block writers of data. This is in addition to the fact that,  in these systems, writers of data will block readers of data.   So, you can see how shared read locks would inhibit concurrency, but they can also cause  spurious errors to occur. In this example we start with our original table but this time with  the goal of transferring $50.00 from account 987, to account 123:  Time  Query  Account transfer transaction  T1  Reads row 1, sum = $500 so far.  Block 1 has a shared read lock  on it.     T2  Reads row 2, sum = $740.25 so  far. Block 2 has a shared read  lock on it.     T3    Updates row 342023, puts an exclusive lock  on block 342023 preventing other updates  and shared read locks. This row now has $50. T4  Reads row N, sum = ....    T5    Attempts to update row 1 but is blocked.  Transaction is suspended until it can obtain  an exclusive lock.  Expert one-on-one Oracle 169 Time  Query  Account transfer transaction  T6  Attempts to read row 342023 but  cannot as an exclusive lock is  already in place.     We have just reached the classic deadlock condition. Our query holds resources the  update needs, and vice versa. Our query has just deadlocked with our update transaction.  One of them will be chosen as the victim and will be killed. We just spent a long time and  a lot of resources only to fail, and get rolled back at the end. This is the second side effect  of shared read locks: readers and writers of data can and frequently will deadlock each other.  As we have seen in Oracle, we have statement level read consistency without reads  blocking writes or deadlocks. Oracle never uses shared read locks ‐ ever. Oracle has chosen  the harder to implement, but infinitely more concurrent multi‐versioning scheme.  Lost Update Prevention  A common use of REPEATABLE READ would be for lost update prevention. If we have  REPEATABLE READ enabled, this cannot happen. By definition, a re‐read of that row in  the same session will result in the same exact data being returned.  In databases other than Oracle, a REPEATABLE READ may be implemented using  SELECT FOR UPDATE and shared read locks. If two users select the same row for update,  both will place a shared read lock on that data. When the first user attempts to update they  will be blocked. When the second user attempts to update, a deadlock will occur. This is  not ideal but it does prevent the lost update.  In Oracle, if we want REPEATABLE READ, but do not actually want to physically  serialize access to a table with SELECT FOR UPDATE NOWAIT (as demonstrated earlier  in the chapter), we actually need to set the isolation level to SERIALIZABLE.   SERIALIZABLE encompasses the lower levels of isolation so if you can do  SERIALIZABLE, you can do REPEATABLE READ   In Oracle, a SERIALIZABLE transaction is implemented so that the read‐consistency we  normally get at the statement level is extended to the transaction. That is, the answers to  every query we will execute in our transaction is fixed at the point in time our transaction  began. In this mode if we:   Select * from T;  Begin dbms_lock.sleep( 60*60*24 ); end;  Select * from T;  Expert one-on-one Oracle 170 The answers returned from T would be the same, even though we just slept for 24 hours  (or we might get an ORA‐1555, snapshot too old error). The isolation level would assure  us these two queries would always returns the same results. Oracle does this in the same  way it provided a read consistent query. It uses the rollback segments to reconstruct the  data as it existed when our transaction began, instead of just when our statement began. In  a SERIALIZABLE mode transaction however, if we attempt to update data and discover at  that point in time the data has changed since our transaction began, we will receive an  error about not being able to serialize access. We will cover this in more detail shortly.  It is clear that this is not the optimum approach for our HR application. What would  happen in that application is that both users would query the data; both users would  update the data on screen. The first user would save their changes, and would succeed.  The second user however, would receive an error when they attempted to save their  changes. They just wasted a lot of time for nothing. They must restart the transaction,  receive our changes, and do it all over again. It prevented the lost update, but at the price  of an annoyed end‐user. However, if a situation arises where REPEATABLE READ is  required, and you do not expect transactions to attempt to update the same rows, then use  of the SERIALIZABLE mode is a possible solution.  SERIALIZABLE  This is generally considered the most restrictive level of transaction isolation, but provides  the highest degree of isolation. A SERIALIZABLE transaction operates in an environment  that makes it appear as if there are no other users modifying data in the database, the  database will be ʹfrozenʹ at the point in time your query began. Your transaction sees the  database consistently, at a single point in time. Side effects (changes) made by other  transactions are not visible to it, regardless of how long it has been running.  SERIALIZABLE does not mean that all transactions executed by the users are the same as  if they were executed one right after another in a serial fashion. It does not imply that  there is some serial ordering of the transactions that would result in the same outcome.  This last point is a frequently misunderstood concept and a small demonstration will clear  it up. The following table represents two sessions performing work over time. The  database tables A and B start out empty and are created as follows:   tkyte@TKYTE816> create table a ( x int );  Table created.        tkyte@TKYTE816> create table b ( x int );  Table created.  Now, we have the following series of events:  Time  Session 1 Executes  Session 2 Executes  Expert one-on-one Oracle 171 Time  Session 1 Executes  Session 2 Executes  0:00  Alter session set  isolation_level=serializable;      0:01    Alter session set  isolation_level=serializable;   0:02  Insert into a select count(*) from b;      0:03    Insert into b select count(*) from a;   0:04  Commit;      0:05    Commit;   Now, when this is all said and done ‐ tables A and B will each have a row with the value  of zero in it. If there was some ʹserialʹ ordering of the transactions we could not possibly  have both tables containing the value zero in them. If Session 1 executed before Session 2 ‐  then table B would have a count of 1. If Session 2 executed before Session 1 ‐ then table A  would have a count of 1. As executed above, however, both tables will have a count of zero.  They just executed as if they were the only transaction in the database at that point in time.  No matter how many times Session 1 queried table B, the count will be the count that was  committed in the database at time 0:00. Likewise, no matter how many times Session 2  queries table A, it will be the same as it was at time 0:01.  In Oracle, serializability is achieved by extending the read consistency we get at the  statement level to the transaction level. Instead of results being consistent with respect to  the start of a statement, they are pre‐ordained at the time you begin the transaction. Pretty  deep thought there ‐ the database already knows the answer to any question you might  ask it, before you ask it.  This degree of isolation comes with a price ‐ that price is the error:  ERROR at line 1:  ORA‐08177: canʹt serialize access for this transaction  You will get this message whenever you attempt to update a row that has changed since  your transaction began. Oracle takes an optimistic approach to serialization; it gambles on  the fact that the data your transaction wants to update wonʹt be updated by any other  transaction. This is typically the way it happens and the then gamble pays off, especially in  OLTP type systems. If no one else updates your data during your transaction, this  isolation level, which will generally decrease concurrency in other systems, will provide  the same degree of concurrency as it would without SERIALIZABLE transactions. The  downside to this is that you may get the ORA‐08177 error if the gamble doesnʹt pay off. If  you think about it, however, the gamble is worth the risk. If youʹre using SERIALIZABLE  transaction, you should not be expecting to update the same information as other  Expert one-on-one Oracle 172 transactions. If you do, you should use the SELECT ... FOR UPDATE as shown above, and  this will serialize the access. So, if you  • Have a high probability of no one else modifying the same data;   • Need transaction level read consistency;   • Will be doing short transactions (in order to help make the first bullet point a  reality);   then using an isolation level of SERIALIZABLE will be achievable and effective. Oracle  finds this method scalable enough to run all of their TPC‐Cs (an industry standard OLTP  benchmark, see http://www.tpc.org/ for details). In many other implementations, you  would find this being achieved with shared read locks and their corresponding deadlocks,  and blocking. Here in Oracle, we do not get any blocking but we will get the ORA‐08177 if  other sessions change the data we want to change as well. However, we will not get it as  frequently as you will get deadlocks and blocks in the other systems.  Read‐Only Transactions  Read‐only transactions are very similar to SERIALIZABLE transactions, the only  difference being that they do not allow modifications so are not susceptible to the ORA‐ 08177 error. Read‐only transactions are intended to support reporting needs, where the  contents of the report needs to be consistent with respect to a single point in time. In other  systems, you would use the REPEATABLE READ, and suffer the associated affects of the  shared read lock. In Oracle you will use the read‐only transaction. In this mode, the output  you produce in a report that uses 50 SELECT statements to gather the data, will be  consistent with respect to a single point in time ‐ the time the transaction began. You will  be able to do this without locking a single piece of data anywhere.  This is achieved by using the same multi‐versioning as used for individual statements. The  data is reconstructed as needed from the rollback segments and presented to you as it  existed when the report began. Read‐only transactions are not trouble‐free however.  Whereas we might see an ORA‐08177 in a SERIALIZABLE transaction, we might expect to  see an ORA‐1555 snapshot too old error with read‐only transactions. This will happen on a  system where other people are actively modifying the information we are reading. Their  changes (undo) are recorded in the rollback segments. But rollback segments are used in a  circular fashion in much the same manner as redo logs. The longer the report takes to run,  the larger the chance that some undo we need to reconstruct our data wonʹt be there  anymore. The rollback segment will have wrapped around, and portion of it we need has  been reused by some other transaction. At this point, you will receive the ORA‐1555, and  will have to start over again. The only solution to this sticky issue is to have rollback  segments that are sized correctly for your system. Time and time again, I see people trying  to save a few MBs of disk space by having the smallest possible rollback segments (why  ʹwasteʹ space on something I donʹt really need?). The problem is that the rollback segments  Expert one-on-one Oracle 173 are a key component of the way the database works, and unless they are sized correctly,  you will hit this error. In 12 years of using Oracle 6, 7 and 8, I can say I have never hit an  ORA‐1555 outside of a testing or development system. If you hit them there, you know  you have not sized the rollback segments correctly and you fix it. We will revisit this issue  in Chapter 5, Redo and Rollback.  Summary  In this section, we covered a lot of material that, at times, makes you scratch your head.  While locking is rather straightforward, some of the side effects are not. However, it is  vital that you understand these issues. For example, if you were not aware of the table lock  Oracle uses to enforce a foreign key relationship when the foreign key is not indexed, then  your application would suffer from poor performance. If you did not understand how to  review the data dictionary to see who was locking whom, you might never figure that one  out. You would just assume that the database ʹhangsʹ sometimes. I sometimes wish I had a  dollar for every time I was able to solve the ʹinsolvableʹ hanging issue by simply running  the query to detect un‐indexed foreign keys, and suggesting that we index the one causing  the problem ‐ I would be very rich.   We took a look at the meaning of the isolation levels set out in the SQL92 standard, and at  how other databases implement their meaning, compared to Oracle. We saw that in other  implementations, ones that employ read locks to provide consistent data, there is a huge  trade‐off between concurrency and consistency. In order to get highly concurrent access to  data you would have to decrease your needs for consistent answers. In order to get  consistent, correct answers ‐ you would need to live with decreased concurrency. We saw  how in Oracle that is not the case ‐ all due to multi‐versioning. This short table sums up  what you might expect in a database that employs read locking versus Oracles multi‐ versioning:  Isolation Level  Implementation Writes  block  reads Reads  block  writes Deadlock  sensitive  reads  Incorrect  query  results  Lost  updates  Lock  escalation  or limits  READ  UNCOMMITTED  Read locking  No  No  No  Yes  Yes  Yes  READ  COMMITTED  (Other  databases)  Yes  No  No  Yes  Yes  Yes  REPEATABLE  READ     Yes  Yes  Yes  No  No  Yes  SERIALIZABLE    Yes  Yes  Yes  No  No  Yes  READ  COMMITTED  Multi‐ versioning  No  No  No  No  No[*]   No  Expert one-on-one Oracle 174 Isolation Level  Implementation Writes  block  reads Reads  block  writes Deadlock  sensitive  reads  Incorrect  query  results  Lost  updates  Lock  escalation  or limits  SERIALIZABLE  (Oracle)  No  No  No  No  No  No  [*]With select for update nowait        Concurrency controls, and how the database implements them, are definitely things you  want to have a good grasp of. Iʹve been singing the praises of multi‐versioning and read‐ consistency, but like everything else in the world, it is a double‐edged sword. If you donʹt  understand that it is there and how it works, you will make errors in application design.  Consider the resource scheduler example from Chapter 1. In a database without multi‐ versioning, and its associated non‐blocking reads, the original logic employed by the  program may very well have worked. However, this logic would fall apart when  implemented in Oracle ‐ it would allow data integrity to be compromised. Unless you  know how it works, you will write programs that corrupt data. It is that simple.  Expert one-on-one Oracle 175 Chapter 4: Transactions  Overview  Transactions are one of the features that set a database apart from a file system. In a file  system, if you are in the middle of writing a file and the operating system crashes, this file  is likely to be corrupted. It is true there are ʹjournaledʹ file systems, and the like, which  may be able to recover your file to some point in time. However, if you need to keep two  files synchronized, it wonʹt help you there ‐ if you update one file, and the system fails  before you finish updating the second then you will have out‐of‐sync files.  This is the main purpose of transactions in the database ‐ they take the database from one  consistent state to the next. That is their job. When you commit work in the database, you  are assured that either all of your changes have been saved, or none of them are saved.  Furthermore, you are assured that your various rules and checks that implement data  integrity are carried out.  Database transactions should exhibit attributes described by the ACID properties. ACID  is an acronym for:  • Atomicity ‐ A transaction either happens completely, or none of it happens.   • Consistency ‐ A transaction takes the database from one consistent state to the next.   • Isolation ‐ The effects of a transaction may not be visible to other transactions until  the transaction has committed.   • Durability ‐ Once the transaction is committed, it is permanent.   Transactions in Oracle exhibit all of the above characteristics. In this chapter, weʹll discuss  the implications of atomicity, and how it affects statements in Oracle. Weʹll cover  transaction control statements such as COMMIT, SAVEPOINT, and ROLLBACK and  discuss how integrity constraints and such are enforced in a transaction. We will also look  at why you may have some bad transaction habits if you have been developing in other  databases. We will look at distributed transactions and the two‐phase commit. Lastly, we  will look at some real‐world issues with regards to transactions, how they are logged, and  what role rollback segments might play.  Transaction Control Statements  There is no ʹbegin transactionʹ statement in Oracle. A transaction implicitly begins with the  first statement that modifies data (the first statement that gets a TX lock). Issuing either a  COMMIT or ROLLBACK statement explicitly ends transactions. You should always  explicitly terminate your transactions with a COMMIT or ROLLBACK ‐ otherwise the  tool/environment you are using will pick one or the other for you. If you exit your  Expert one-on-one Oracle 176 SQL*PLUS session normally without committing or rolling back, SQL*PLUS will assume  you wish you commit your work, and will do so for you. If you terminate a Pro*C  program on the other hand, a rollback will take place.  Transactions are atomic in Oracle ‐ either every statement that comprises the transaction is  committed (made permanent), or all of the statements are rolled back. This protection is  extended to individual statements as well. A statement either entirely succeeds, or it is  entirely rolled back. Note that I said the statement is rolled back. The failure of one  statement does not cause previously executed statements to be automatically rolled back.  Their work is preserved and must either be committed or rolled back by you. Before we  get into the details of exactly what it means for a statement and transaction to be ʹatomicʹ,  we will take a look at the various transaction control statements available to us. They are:  • COMMIT ‐ In its simplest form, you would just issue COMMIT. You could be more  verbose and say COMMIT WORK, but the two are equivalent. A COMMIT ends  your transaction and makes any changes permanent (durable). There are extensions  to the COMMIT statement used in distributed transactions. These extensions allow  you to label a COMMIT (label a transaction) with some meaningful comment, and  to force the commit of an in‐doubt distributed transaction.   • ROLLBACK ‐ In its simplest form, you would just issue ROLLBACK. Again, you  could be more verbose and say ROLLBACK WORK, but the two are equivalent. A  rollback ends your transaction and undoes any uncommitted changes you have  outstanding. It does this by reading information stored in the rollback segments,  and restoring the database blocks to the state they were in prior to your transaction  beginning.   • SAVEPOINT ‐ A SAVEPOINT allows you to create a ʹmarked pointʹ within a  transaction. You may have multiple SAVEPOINTs within a single transaction.   • ROLLBACK TO  ‐ This is used with the SAVEPOINT command  above. You may roll back your transaction to that marked point without rolling  back any of the work that preceded it. So, you could issue two UPDATE statements,  followed by a SAVEPOINT and then two DELETE statements. If an error, or some  sort or exceptional condition, occurs during execution of the DELETE statements,  the transaction will rollback to the named SAVEPOINT, undoing the DELETEs but  not the UPDATE statements.   • SET TRANSACTION ‐ This statement allows you to set various transaction  attributes, such as its isolation level and whether it is read‐only or readwrite. You  can also use this statement to instruct the transaction to use a specific rollback  segment.   Thatʹs it ‐ there are no more. The most frequently used control statements are COMMIT  and ROLLBACK. The SAVEPOINT statement has a somewhat special purpose. Internally,  Oracle uses it frequently, and you may find some use for it in your application as well.  Expert one-on-one Oracle 177 Now that weʹve had a brief overview of the transaction control statements, we are ready to  see what is meant by statement and transaction atomicity. Consider the following  statement:   Insert into t values ( 1 );  It seems fairly clear that if it fails due to a constraint violation, our row will not be inserted.  However, consider the following example, where an insert or delete on table T fires a  trigger that adjusts the cnt column in table T2, appropriately:   tkyte@TKYTE816> create table t2 ( cnt int );  Table created.        tkyte@TKYTE816> insert into t2 values ( 0 );  1 row created.        tkyte@TKYTE816> create table t ( x int check ( x>0 ) );  Table created.        tkyte@TKYTE816> create trigger t_trigger    2  before insert or delete on t for each row    3  begin    4     if ( inserting ) then    5          update t2 set cnt = cnt +1;    6     else    7          update t2 set cnt = cnt ‐1;    8     end if;    9     dbms_output.put_line( ʹI fired and updated ʹ  ||                                          sql%rowcount || ʹ rowsʹ );   10  end;   11  /  Trigger created.  In this situation it is less clear what should happen. If the error occurs after the trigger has  fired, should the effects of the trigger be there or not? That is, if the trigger fired and  updated T2, but the row was not inserted into T, what should the outcome be? Clearly the  answer is that we would not like the cnt column in T2 to be incremented if a row is not  actually inserted into T. Fortunately, in Oracle, the original statement from the client, the  INSERT INTO T in this case, either entirely succeeds, or entirely fails. This statement is  atomic. We can confirm this, as follows:   tkyte@TKYTE816> set serveroutput on        Expert one-on-one Oracle 178 tkyte@TKYTE816> insert into t values ( 1 );  I fired and updated 1 rows        1 row created.        tkyte@TKYTE816> insert into t values (‐1 );  insert into t values (‐1 )  *  ERROR at line 1:  ORA‐02290: check constraint (TKYTE.SYS_C001570) violated              tkyte@TKYTE816> exec null  /* this is needed to retrieve the dbms_output */  I fired and updated 1 rows        PL/SQL procedure successfully completed.        tkyte@TKYTE816> select * from t2;               CNT  ‐‐‐‐‐‐‐‐‐‐           1  We successfully inserted one row into T, duly receiving the message, I fired and updated 1  rows. The next INSERT statement violates the integrity constraint we have on T. I needed  to exec NULL, to run a Null statement, and get SQL*PLUS to show me the  DBMS_OUTPUT information, (since SQL*PLUS will not print out the DBMS_OUTPUT  buffer after a SELECT), but this shows that again, the trigger fired and updated one row.  We would maybe expect T2 to have a value of 2 now, but we see it has a value of 1. Oracle  made the original insert atomic.  It does this by silently wrapping a SAVEPOINT around each of our calls. The above two  inserts were really treated like this:   Savepoint statement1;     Insert into t values ( 1 );  If error then rollback to statement1;  Savepoint statement2;     Insert into t values ( ‐1 );  If error then rollback to statement2;  For programmers used to Sybase or SQLServer, this may be confusing at first. In those  databases exactly the opposite is true. The triggers in those systems execute independently of  Expert one-on-one Oracle 179 the firing statement. If they encounter an error, the triggers must explicitly roll back their  own work, and then raise another error to roll back the triggering statement. Otherwise,  the work done by a trigger could persist even if the triggering statement, or some other  part of the statement, ultimately fails.  In Oracle, this statement level atomicity extends as deep as it needs to. If in the above  example, the INSERT INTO T fired a trigger that updates another table, and that table has  a trigger that deletes from another table (and so on, and so on) either all of the work  succeeds, or none does. You do not need to code anything special to ensure this ‐ it is the  way it works.  It is interesting to note that Oracle considers PL/SQL anonymous blocks to be statements  as well. Consider the following stored procedure:  tkyte@TKYTE816> create or replace procedure p    2  as    3  begin    4          insert into t values ( 1 );    5          insert into t values (‐1 );    6  end;    7  /  Procedure created.        tkyte@TKYTE816> select * from t;  no rows selected        tkyte@TKYTE816> select * from t2;               CNT  ‐‐‐‐‐‐‐‐‐‐           0  So, we have a procedure we know will fail. The second insert will always fail in this case.  Letʹs see what happens if we just run that stored procedure:   tkyte@TKYTE816> begin    2    p;    3  end;    4  /  I fired and updated 1 rows  I fired and updated 1 rows  begin  *  Expert one-on-one Oracle 180 ERROR at line 1:  ORA‐02290: check constraint (TKYTE.SYS_C001570) violated  ORA‐06512: at ʺTKYTE.Pʺ, line 5  ORA‐06512: at line 2              tkyte@TKYTE816> select * from t;        no rows selected        tkyte@TKYTE816> select * from t2;               CNT  ‐‐‐‐‐‐‐‐‐‐           0  As you can see, Oracle treated the stored procedure call as an atomic statement. The client  submitted a block of code, BEGIN P; END;, and Oracle wrapped a SAVEPOINT around it.  Since P failed, Oracle restored the database back to the point right before it was called.  Now, if we submit a slightly different block, we will get entirely different results:   tkyte@TKYTE816> begin    2          p;    3  exception    4          when others then null;    5  end;    6  /  I fired and updated 1 rows  I fired and updated 1 rows        PL/SQL procedure successfully completed.        tkyte@TKYTE816> select * from t;                 X  ‐‐‐‐‐‐‐‐‐‐           1        tkyte@TKYTE816> select * from t2;               CNT  ‐‐‐‐‐‐‐‐‐‐           1  Expert one-on-one Oracle 181 Here, we ran a block of code that ignored any, and all, errors and the difference in  outcome here is huge. Whereas the first call to P effected no changes, here the first INSERT  succeeds and the cnt column in T2 is incremented accordingly. Oracle considered the  ʹstatementʹ to be the block that the client submitted. This statement succeeded, by catching  and ignoring the error itself, so the ʹIf error then rollback...ʹ didnʹt come into effect and  Oracle did not roll back to the SAVEPOINT after execution. Hence, the partial work  performed by P was preserved. The reason that this partial work was preserved in the first  place is that we have statement level atomicity within P ‐ each statement in P is atomic. P  becomes the client of Oracle when it submits its two INSERT statements. Each INSERT  either entirely succeeds, or fails. This is evidenced by the fact that we can see the trigger on  T fired twice and updated T2 twice, yet the count in T2 only reflects one update. The  second INSERT executed in P had an implicit SAVEPOINT wrapped around it.  The difference between the two blocks of code is subtle, and something you must consider  in your applications. Adding an exception handler to a block of PL/SQL code can radically  change its behaviour. A more correct way to code this, one that restores the statement level  atomicity to the entire PL/SQL block would be:  tkyte@TKYTE816> begin    2      savepoint sp;    3      p;    4  exception    5      when others then    6          rollback to sp;    7  end;    8  /  I fired and updated 1 rows  I fired and updated 1 rows        PL/SQL procedure successfully completed.        tkyte@TKYTE816>  tkyte@TKYTE816> select * from t;        no rows selected        tkyte@TKYTE816> select * from t2;               CNT  ‐‐‐‐‐‐‐‐‐‐           0  Expert one-on-one Oracle 182 Here, by mimicking the work Oracle normally does for us with the SAVEPOINT, we are  able to restore the original behavior while still catching and ʹignoringʹ the error.  Integrity Constraints and Transactions  It is interesting to note exactly when integrity constraints are checked. By default, integrity  constraints are checked after the entire SQL statement has been processed. Note I said  ʹSQL statementʹ and not just ʹstatementʹ. If I have many SQL statements in a PL/SQL stored  procedure, then each SQL statement will have its integrity constraints validated  immediately after their individual execution, not after the stored procedure completes.  Integrity constraint checking can be programmatically postponed until the transaction  commits, or until you the developer want to validate them.   So, why are constraints validated after the SQL statement executes, why not during? This  is because it is very natural for a single statement to make individual rows in a table  momentarily ʹinconsistentʹ. Taking a look at the partial work of a statement would result  in Oracle rejecting the results, even if the end result would be OK. For example, suppose  you have a table like this:  tkyte@TKYTE816> create table t  ( x int unique );  Table created.        tkyte@TKYTE816> insert into t values ( 1 );  1 row created.        tkyte@TKYTE816> insert into t values ( 2 );  1 row created.  And now we want to execute a multiple‐row update:  tkyte@TKYTE816> update t set x = x+1;  2 rows updated.  If Oracle checked the constraint after each row was updated then on any given day you  would stand a 50/50 chance of having the update fail. The rows in T are accessed in some  order, and if Oracle updated the X=1 row first, then we would momentarily have a  duplicate value for X and it would reject the update. Since Oracle waits patiently to the  end of the statement, the statement succeeds because by the time it is done, there are no  duplicates.  Starting with Oracle 8.0, we also have the ability to defer constraint checking. This ability  can be quite advantageous for various operations. The one that immediately jumps to  mind is the requirement to cascade an update of a primary key to the child keys. There are  Expert one-on-one Oracle 183 many that will say you should never need to do this, that primary keys are immutable (I  am one of those people), but many people persist in their desire to have a cascading  update. Deferrable constraints make this possible.  In prior releases, it was actually possible to do a cascade update, but it involved a  tremendous amount of work, and had certain limitations. With deferrable constraints, it  becomes almost trivial. It could look like this:  tkyte@TKYTE816> create table p    2  ( pk  int primary key )    3  /  Table created.        tkyte@TKYTE816>  tkyte@TKYTE816> create table c    2  ( fk  int constraint c_fk    3            references p(pk)    4            deferrable    5            initially immediate    6  )    7  /  Table created.        tkyte@TKYTE816> insert into p values ( 1 );  1 row created.        tkyte@TKYTE816> insert into c values ( 1 );  1 row created.  So, I have a parent table P, and a child table C. Table C references table P, and the  constraint used to enforce that rule is called C_FK (child foreign key). This constraint was  created as DEFERRABLE, but it is set to INITIALLY IMMEDIATE. This means I can defer  that constraint until commit or to some other time. By default, however, it will be  validated at the statement level. This is the most common use of the deferrable constraints.  Most existing applications wonʹt be checking for constraint violations on a COMMIT  statement, it is best not to surprise them with that. As defined, our table C behaves in the  fashion tables always have, but it gives us the ability to explicitly change its behaviour.  Now, lets try some DML on the tables and see what happens:   tkyte@TKYTE816> update p set pk = 2;  update p set pk = 2  *  ERROR at line 1:  Expert one-on-one Oracle 184 ORA‐02292: integrity constraint (TKYTE.C_FK) violated ‐ child record found  Since the constraint is in IMMEDIATE mode, this update fails. We will change the mode  and try again:   tkyte@TKYTE816> set constraint c_fk deferred;  Constraint set.        tkyte@TKYTE816> update p set pk = 2;  1 row updated.  Now it succeeds. For illustration purposes, I am going to show how to check a DEFERRED  constraint procedurally, to see if the modifications you made are in agreement with the  business rules (in other words, that the constraint hasnʹt been violated). It is a good idea to  do this before committing or releasing control to some other part of the program (which  may not be expecting the DEFERRED constraints):   tkyte@TKYTE816> set constraint c_fk immediate;  set constraint c_fk immediate  *  ERROR at line 1:  ORA‐02291: integrity constraint (TKYTE.C_FK) violated ‐ parent key not found  It fails and returns an error immediately, as expected since we knew that the constraint  had been violated. The update to P was not rolled back (that would violate the statement  level atomicity). It is still outstanding. You should also note that our transaction is still  working with the C_FK constraint DEFERRED since the SET CONSTRAINT command  failed. Weʹll continue on now by cascading the update to C:   tkyte@TKYTE816> update c set fk = 2;  1 row updated.        tkyte@TKYTE816> set constraint c_fk immediate;  Constraint set.        tkyte@TKYTE816> commit;  Commit complete.  And that is the way it works.  Bad Transaction Habits  Expert one-on-one Oracle 185 Many developers have some bad habits when it comes to transactions. I see this frequently  with developers who have worked with a database that ʹsupportsʹ but does not ʹpromoteʹ  the use of transactions. For example, in Informix (by default), Sybase and SQLServer you  must explicitly BEGIN a transaction, otherwise each individual statement is a transaction  all by itself. In a similar manner to the way in which Oracle wraps a SAVEPOINT around  discrete statements, they wrap a BEGIN WORK/COMMIT or ROLLBACK around each  statement. This is because, in these databases, locks are a precious resource and readers  block writers, and writers block readers. In an attempt to increase concurrency, they  would like you to make the transaction as short as possible ‐ sometimes at the expense of  data integrity.  Oracle takes the opposite approach. Transactions are always implicit and there is no way  to have an ʹautocommitʹ unless an application implements it (see the discussion of the  JDBC API, at the end of this section). In Oracle, every transaction should be committed  when you must and never before. Transactions should be as large as they need to be.  Issues such as locks, blocking, and so on should not really be considered ‐ data integrity is  the driving force behind the size of your transaction. Locks are not a scarce resource, and  there are no contention issues between concurrent readers and writers of data. This allows  you to have robust transactions in the database. These transactions do not have to be short  in duration ‐ they should be exactly as long as they need to be. Transactions are not for the  convenience of the computer and its software, they are to protect your data.  Faced with the task of updating many rows, most programmers will try to figure out some  procedural way to do it in a loop, so that they can commit every so many rows. There are  two main reasons that I hear for doing it this way:  • It is faster and more efficient to frequently commit lots of small transactions than it  is to process and commit one big one.   • We donʹt have enough rollback space.   Both of these conclusions are misguided. It is generally not faster to commit frequently ‐ it  is almost always faster to do the work in a single SQL statement. By way of a small  example, letʹs say we have a table T with lots of rows, and we want to update a column  value for every row in that table. We could simply do it in a single update like this:   tkyte@TKYTE816> create table t as select * from all_objects;  Table created.        tkyte@TKYTE816> set timing on  tkyte@TKYTE816> update t set object_name = lower(object_name);        21946 rows updated.        Expert one-on-one Oracle 186 Elapsed: 00:00:01.12  Many people however, for whatever reason, feel compelled to do it like this:  tkyte@TKYTE816> begin    2     for x in ( select rowid rid, object_name, rownum r    3                  from t )    4     loop    5          update t    6             set object_name = lower(x.object_name)    7           where rowid = x.rid;    8          if ( mod(x.r,100) = 0 ) then    9             commit;   10          end if;   11     end loop;   12     commit;   13  end;   14  /  PL/SQL procedure successfully completed.        Elapsed: 00:00:05.99  In this simple example, it is about five times slower to commit frequently in a loop. If you  can do it in a single SQL statement, do it that way, as it is almost certainly faster.  Letʹs now look at the second reason, which arises from developers using a ʹlimited  resourceʹ (rollback segments) sparingly. This is a configuration issue; you need to ensure  that you have enough rollback space to size your transactions correctly. Committing in a  loop, apart from generally being slower, is also the most common cause of the dreaded  ORA‐01555 error. Letʹs look at this in more detail.  As you will appreciate after reading the Locking and Concurrency and Developing Successful  Oracle Applications chapters, Oracleʹs multi‐versioning model uses rollback segment data to  reconstruct blocks as they appeared at the beginning of your statement or transaction  (depending on the isolation mode). If the necessary rollback information no longer exists,  you will receive an ORA‐01555 snapshot too old error message, and your query will not  complete. So, if you are modifying the table that you are reading (as in the above  procedure), you are generating rollback information required for your query. Your update  generates undo information that your query will probably be making use of in order to get  the read consistent view of the data it needs to update. If you commit, you are allowing  the system to reuse the rollback segment space you just filled up. If it does reuse the  rollback, wiping out old rollback data that your query subsequently needs, you are in big  trouble. Your SELECT will fail and your update will stop part of the way through. You  Expert one-on-one Oracle 187 have a part‐finished transaction, and probably no good way to restart it (more about this  in a moment). Letʹs see this in action with a small demonstration. In a small test database I  had setup a table:   tkyte@TKYTE816> create table t as select * from all_objects;  Table created.        tkyte@TKYTE816> create index t_idx on t(object_name);  Index created.  I then offlined all of my rollback segments and created a small one:  tkyte@TKYTE816> create rollback segment rbs_small storage (initial 64k    2  next 64k minextents 2 maxextents 4 ) tablespace tools;        Rollback segment created.  Now, with only the small rollback segment online, I ran this block of code to do the update:  tkyte@TKYTE816> begin    2     for x in ( select rowid rid, object_name, rownum r    3                  from t    4                  where object_name > chr(0) )    5     loop    6          update t    7             set object_name = lower(x.object_name)    8           where rowid = x.rid;    9          if ( mod(x.r,100) = 0 ) then   10             commit;   11          end if;   12     end loop;   13     commit;   14  end;   15  /  begin  *  ERROR at line 1:  ORA‐01555: snapshot too old: rollback segment number 10 with name ʺRBS_SMALLʺ too  small  ORA‐06512: at line 2  I get the error. I should point out that I added an index and a WHERE clause. I wanted to  make sure that I was reading the table randomly. The WHERE clause will use the index (I  Expert one-on-one Oracle 188 used the rule‐based optimizer for this). When we process a table via an index, we will tend  to read a block for a single row and then the next row we want will be on a different block.  We will process all of the rows on block 1, just not concurrently. Block 1 might hold, say,  rows A, M, N, Q, and Z. So we would hit the block four times with long time intervals  between each hit. Since we are committing frequently and reusing rollback space, we  eventually revisit a block we can simply no longer reproduce and get the error.  This was a very artificial example just to show how it happens, in a reliable manner. Our  UPDATE statement was generating rollback. We had four 64K extents of rollback to play  with, for a total of 256K. We wrapped around in our rollback segment many times, since  they are used in a circular fashion. Every time we committed we allowed Oracle to  overwrite the rollback data we generated. Eventually, we needed some piece of data that  we had generated, but it no longer existed and we received the ORA‐01555 error.  You would be right to point out that, in this case, if we had not committed we would get  the following error:   begin  *  ERROR at line 1:  ORA‐01562: failed to extend rollback segment number 10  ORA‐01628: max # extents (4) reached for rollback segment RBS_SMALL  ORA‐06512: at line 6  The major differences between the two errors, however, are:  • The ORA‐01555 example left our update in a totally unknown state. Some of the work  had been done, some had not.   • There is absolutely nothing we can do to avoid the ORA‐01555, given that we  committed in the cursor FOR loop.   • We can always avoid the ORA‐01562, by allocating appropriate resources in our  system. The second error is avoidable by right sizing; the first error is not.   The bottom line here is that you cannot ʹsaveʹ on rollback space by committing frequently ‐  you need that rollback (I was in a single user system when I received the ORA‐01555. It  only takes one session to get that). Developers and DBAs need to work together to size  these objects adequately for the jobs that need to be done. There can be no short‐changing  here. You must discover, through analysis of your system, what your biggest transactions  are, and size appropriately for them. Given the above example, this might be a one‐time  update. In that case, I might have created a huge rollback segment somewhere on my  system for the sole purpose of doing this update. I would then use the SET  TRANSACTION statement to tell my transaction to use this really large rollback segment.  I would then drop that rollback segment, and release the space. If this is not a one‐time  Expert one-on-one Oracle 189 thing but is run frequently, then you need to size that into your system, and have the  space readily available. Many people consider things like temp, rollback, and redo as  ʹoverheadʹ ‐ things to allocate as little storage to as possible. This is reminiscent of a  problem the computer industry had on January 1, 2000 ‐ all caused by trying to save 2  bytes in a date field. These components of the database are not overhead, but rather are  key components of the system ‐ they must be sized appropriately (not too big, not too  small, just right).  Of course, the most serious problem with the ʹcommit before the transaction is overʹ  approach, is the fact that it frequently leaves your database in an unknown state, if the  update fails half way through. Unless you planned for this ahead of time, it is very hard to  restart the failed transaction, allowing it to pick up where it left off. For example, say we  were not applying the LOWER() function to the column, but rather some other function of  the column such as:   last_ddl_time = last_ddl_time + 1;  If we halted the update loop partway through, how would we restart it? We could not just  rerun it, as we would end up adding 2 to some dates, and one to others. If we fail again,  we would add 3 to some 2 to others, and 1 to the rest, and so on. We need yet more  complex logic ‐ some way to ʹpartitionʹ the data. For example, we could process all of the  object_names that start with A, and then B, and so on:   tkyte@TKYTE816> create table to_do    2  as    3  select distinct substr( object_name, 1,1 ) first_char    4    from T    5  /  Table created.  tkyte@TKYTE816> begin    2          for x in ( select * from to_do )    3          loop    4              update t set last_ddl_time = last_ddl_time+1    5               where object_name like x.first_char || ʹ%ʹ;    6    7              dbms_output.put_line( sql%rowcount || ʹ rows updatedʹ );    8              delete from to_do where first_char = x.first_char;    9   10              commit;   11          end loop;   12  end;   13  /  11654 rows updated  Expert one-on-one Oracle 190 21759 rows updated  309 rows updated  6 rows updated  270 rows updated  830 rows updated  412 rows updated  7 rows updated  378 rows updated  95 rows updated  203 rows updated  2482 rows updated  13 rows updated  318 rows updated  83 rows updated  14 rows updated  1408 rows updated  86 rows updated  2 rows updated  35 rows updated  2409 rows updated  57 rows updated  306 rows updated  379 rows updated  1 rows updated  1 rows updated        PL/SQL procedure successfully completed.  Now, we could restart this process if it fails, since we would not process any object name  that had already been processed successfully. The problem with this approach, however,  is that unless you have some attribute that evenly partitions the data, you will end up  having a very wide distribution of rows. The second update did more work than all of the  others combined. Additionally, if other sessions are accessing this table and modifying the  data, they might update the object_name field as well. Suppose that some other session  updates the object named Z to be A, after you already processed the Aʹs ‐ you would miss  that record. Furthermore, this is a very inefficient process compared to update t set  last_ddl_time = last_ddl_time+1. We are probably using an index to read every row in the  table, or we are full scanning it n‐times ‐ both of which are undesirable. There are so many  bad things to be said about this approach.   The best approach here is the one I advocated in the opening section of Chapter 1: do it  simply. If it can be done in SQL, do it in SQL. What canʹt be done in SQL, do in PL/SQL.  Do it using the least amount of code you can. Have sufficient resources allocated. Always  Expert one-on-one Oracle 191 think about what happens in the event of an error. So many times, Iʹve seen people code  update loops that worked great on the test data but then failed halfway through when  applied to the real data. Now they are really stuck, as they have no idea where it stopped  processing. It is a lot easier to size rollback correctly than it is to write a restartable  transaction. If you have truly large tables that need to be updated, you should be using  partitions (more on that in Chapter 14, Partitioning), allowing you to update each partition  individually. You can even use parallel DML to perform the update.  My final word on bad transaction habits concerns the one that arises from use of the  popular programming APIs ODBC and JDBC. These APIs ʹautocommitʹ by default.  Consider the following statements, transferring $1000 from a checking to a savings  account:  update accounts set balance = balance ‐ 1000 where account_id = 123;  update accounts set balance = balance + 1000 where account_id = 456;  If your program is using JDBC when you submit these statements, JDBC will (silently)  inject a commit after each update. Consider the impact of this if the system fails after the  first update, and before the second. Youʹve just lost $1000!  I can understand why ODBC does this. The developers of SQLServer designed ODBC and  this database demands that you use very short transactions due to its concurrency model  (writes block reads, reads block writes, and locks are a scarce resource). What I cannot  understand is how this got carried over into JDBC, an API that is supposed to be in  support of the ʹEnterpriseʹ. It is my belief that the very next line of code after opening a  connection in JDBC should always be:  connection conn81 = DriverManager.getConnection                (ʺjdbc:oracle:oci8:@ora8idevʺ,ʺscottʺ,ʺtigerʺ);        conn81.setAutoCommit (false);  This will return control over the transaction back to you, the developer, which is where it  belongs. You can then safely code your account transfer transaction, and commit it after  both statements have succeeded. Lack of knowledge of your API can be deadly in this case.  Iʹve seen more than one developer, unaware of this autocommit ʹfeatureʹ, get into big  trouble with their application when an error occurred.  Distributed Transactions  One of the really nice features of Oracle is its ability to transparently handle distributed  transactions for us. I can update data in many different databases in the scope of a single  transaction. When I commit, I either commit the updates in all of the instances, or I commit  Expert one-on-one Oracle 192 none of them (they will all be rolled back). I need no extra code to achieve this, I simply  ʹcommitʹ.  A key to distributed transactions in Oracle is the database link. A database link is a  database object that describes how to log into another instance from your instance.  However, the purpose of this section is not to cover the syntax of the database link  command (it is fully documented). Once you have the database link set up, accessing  remote objects is as easy as:   select * from T@another_database;  This would select from the table T in the database instance defined by the database link  ANOTHER_DATABASE. Typically, you would ʹhideʹ the fact that T is a remote table by  creating a view of it, or a synonym. For example, I can:   create synonym T for T@another_database;  and then access T as if it were a local table. Now that we have this database link set up and  can read some tables, we are also able to modify them (given that we have the appropriate  privileges, of course). Performing a distributed transaction is now no different from a local  transaction. All we would do is:   update local_table set x = 5;  update remote_table@another_database set y = 10;  commit;  That is it. Oracle will either commit in both databases or in neither. It uses a two‐phase  distributed commit (2PC) protocol to do this. The 2PC is a distributed protocol that allows  for a modification that affects many disparate databases to be committed atomically. It  attempts to close the window for distributed failure as much as possible before  committing. In a 2PC between many databases, one of the databases, typically the one the  client is logged into initially, will be the coordinator for the distributed transaction. This  one site will ask the other sites if they are ready to commit. In effect, this one site will go to  the other sites, and ask them to be prepared to commit. Each of the other sites reports back  their ʹprepared stateʹ as YES or NO. If any one of the sites votes NO, the entire transaction  is rolled back. If all sites vote YES, the site coordinator broadcasts a message to make the  commit permanent on each of the sites.  This limits the window in which a serious error could occur. Prior to the ʹvotingʹ on the  two‐phase commit, any distributed error would result in all of the sites rolling back. There  would be no doubt as to the outcome of the transaction. After the order to commit or  rollback, there again is no doubt as to the outcome of the distributed transaction. It is only  during the very short window when the coordinator is collecting the votes that the  outcome might be in doubt, after a failure. Assume for example, we have three sites  Expert one-on-one Oracle 193 participating in the transaction with Site 1 being the coordinator. Site 1 has asked Site 2 to  prepare to commit, and Site 2 has done so. Site 1 then asks Site 3 to prepare to commit, and  it does so. At this point in time, Site 1 is the only site that knows the outcome of the  transaction and it is now responsible for broadcasting the outcome to the other sites. If an  error occurs right now – the network fails, Site 1 loses power, whatever – Site 2 and Site 3  will be left ʹhangingʹ. They will have what is known as an in‐doubt distributed transaction.  The two‐phase commit protocol attempts to close the window of error as much as possible,  but it cannot close it entirely. Sites 2 and 3 must keep that transaction open, awaiting  notification from Site 1 of the outcome. If you recall from the architecture discussion in  Chapter 2, it is the function of the RECO process to resolve this issue. This is also where  the COMMIT and ROLLBACK with the FORCE option come into play. If the cause of the  problem was a network failure between Sites 1, 2, and 3, then the DBAs at Sites 2 and 3  could actually call the DBA at Site 1, ask them for the outcome, and apply the commit or  rollback manually, as appropriate.  There are some limits to what you can do in a distributed transaction. Not many though,  and they are reasonable (to me they seem reasonable anyway). The big ones are:  • You cannot issue a COMMIT over a database link. You may only commit from the  site that initiated the transaction.   • You cannot do DDL over a database link. This is a direct result of the first issue  above. DDL commits. You cannot commit from any other site other then the  initiating site, hence we cannot do DDL over a database link.   • You cannot issue a SAVEPOINT over a database link. In short, you cannot issue any  transaction control statements over a database link.   The lack of transaction control over a database link is reasonable, since the initiating site is  the only one that has a list of everyone involved in the transaction. If in our three‐site  configuration above, Site 2 attempted to commit, it would have no way of knowing that  Site 3 was involved. In Oracle, only Site 1 can issue the commit command. At that point it  is then permissible for Site 1 to delegate responsibility for distributed transaction control  to another site.  We can influence which site will be the actual commit site by setting the commit point  strength (an init.ora parameter) of the site. A commit point strength associates a relative  level of importance to a server in a distributed transaction – the more important the server  (the more available the data needs to be), the more probable that it will coordinate the  distributed transaction. You might want to do this in the event that you need to perform a  distributed transaction between your production machine and a test machine. Since the  transaction coordinator is never in doubt as to the outcome of a transaction, it would be  best if the production machine coordinated the distributed transaction. You do not care so  much if your test machine has some open transactions and locked resources. You certainly  do care if your production machine does.  Expert one-on-one Oracle 194 The inability to do DDL over a database link is actually not so bad at all. First off, DDL is  ʹrareʹ. You do it once at installation, or after an upgrade. Production systems donʹt do DDL  (well, they shouldnʹt do DDL). Secondly, there is a method to do DDL over a database link,  in a fashion, using the job queue facility, DBMS_JOB (covered in the Necessary Supplied  Packages appendix). Instead of trying to do DDL over the link, we use the link to schedule  a job to be executed as soon as we commit. In that fashion, the job runs on the remote  machine, is not a distributed transaction, and can do the DDL. In fact, this is the method  by which the Oracle Replication services perform distributed DDL to do schema  replication.  Redo and Rollback  I would like to finish up this chapter on transactions with a description of how redo and  rollback (undo) are generated, and how they fit into transactions, recovery, and so on. This  is a question I am asked frequently. A good conceptual understanding of how redo and  undo work, and what takes place, will help you understand the database in general. It is  important for developers as well as DBAʹs to have a good working knowledge of what  really happens when an update takes place. You need to understand the ramifications of  your actions. What I am presenting below is the pseudo‐code for these mechanisms in  Oracle. What actually takes place is a little more involved, but having a good  understanding of the flow of how it works is valuable.  As an example, we will investigate what might happen with a transaction like this:  insert into t (x,y) values  (1,1);  update t set x = x+1 where x = 1;  delete from t where x = 2;  We will follow this transaction down different paths and discover:  • What happens if the system fails after the update?   • What happens if we succeed and commit?   • What happens if we rollback?   The initial INSERT INTO T will generate both redo and undo. The undo generated will be  enough information to make the insert ʹgo awayʹ. The redo generated will be enough  information to make the insert ʹhappen againʹ. The undo may consist of many pieces of  information. There may be indexes on the columns X and Y for example, and their changes  must also be undone upon a rollback as well Undo is stored in a rollback segment. A  rollback segment is stored in a tablespace, and (this is the important part) is protected by  the redo log just like any other segment. In other words, rollback data is treated just like  table data or index data ‐ changes to rollback segments generate some redo, which is  logged. (why this is so will become clear in a moment when we discuss what happens  Expert one-on-one Oracle 195 when the system crashes). Undo data is added to the rollback segment, and is cached in  the buffer cache just like any other piece of data would be. Like the undo generated, redo  may consist of many pieces of information as well.  So, at this point in time, after the insert has occurred we have:    There are some cached, modified rollback blocks, index blocks and table data blocks. Each  of these modified blocks is protected by entries in the redo log buffer. All of this  information is cached right now.  Hypothetical scenario: the system crashes right now. Everything is OK. The SGA is wiped  out but we donʹt need anything that was in the SGA. It will be as if this transaction never  happened when we restart. None of the blocks with changes got flushed, and none of the  redo got flushed.  Hypothetical scenario: the buffer cache fills up right now. DBWR must make room and must  flush the blocks we just modified. In this case, DBWR will start by asking LGWR to flush  the redo blocks that protect the database blocks. Before DBWR can write any of the blocks  that are changed to disk, LGWR must flush the redo information related to these blocks.  This makes sense, for if we flush the modified blocks for table T, and did not flush the  redo for the associated undo blocks, and the system failed, we would have a modified  table T block with no undo information associated with it. We need to flush the redo log  buffers before writing these blocks out so that we can redo all of these changes necessary  to get the SGA back into the state it is in right now, so that a rollback can take place.  This second scenario should show some of the foresight that has gone into all of this. The  set of conditions ʹif we flushed table T blocks and did not flush the redo for the undo  blocks and the system failedʹ is starting to get complex. It only gets more complex as you  add users, and more objects, and concurrent processing, and so on.   So, at this point, we have the situation depicted on the previous page. We have generated  some modified table and index blocks. These have created some new rollback segment  Expert one-on-one Oracle 196 blocks, and all three types of blocks have generated redo to protect them. If you recall  from our earlier discussion on the redo log buffer, it is flushed every three seconds, when  a third full, and whenever a commit takes place. It is very likely that at some point during  our processing, the redo log buffer will be flushed, and some of our changes will go to  disk as well. In that case the picture becomes this:    Now, the blocks themselves may end up on disk, but probably not in this case. Next, we  do the update. Much the same sort of things take place. This time, the amount of undo will  be larger (we now have some ʹbeforeʹ images to save as a result of the update). Now, we  have the following picture:    We have more new rollback segment blocks in the block buffer cache. In order to undo the  update, if necessary, we have modified database table and index blocks in the cache. We  have also generated more redo log buffer entries. Some of our generated redo log is on  disk, some is in cache.  Hypothetical scenario: the system crashes right now. Upon startup, Oracle would read the  redo logs, and find some redo log entries for our transaction. Given the state in which we  left the system, with the redo entries for the insert in the redo log files, and the redo for the  update still in the buffer, Oracle would ʹroll forwardʹ the insert. We would end up with a  picture much like the first, with some rollback segment undo blocks (to undo the insert),  modified table blocks (right after the insert), and modified index blocks (right after the  insert). Now Oracle will discover that our transaction never committed and will roll it  Expert one-on-one Oracle 197 back since the system is doing crash recovery and, of course, our session is no longer  connected. It will take the undo it just rolled forward in the buffer cache, and apply it to  the data and index blocks, making them look as they did before the insert took place. Now  everything is back the way it was. The blocks that are on disk may, or may not, reflect the  INSERT (it depends on whether or not our blocks got flushed before the crash). If they do,  then the insert has been, in effect, undone and when the blocks are flushed from the buffer  cache, the data file will reflect that. If they do not reflect the insert ‐ so be it, they will be  overwritten later anyway.   Hypothetical scenario: the application rolls back the transaction. At this point, Oracle will find  the undo information for this transaction, either in the cached rollback segment blocks  (most likely), or on disk if they have been flushed (more likely for very large transactions).  It will apply the undo information to the data and index blocks in the buffer cache, or if  they are no longer in the cache request, they are read from disk into the cache to have the  UNDO applied to them. These blocks will later be flushed to the data files in the original  form.  The first scenario covers the rudimentary details of a crash recovery. The system performs  this as a two‐step process. First it rolls forward, bringing the system right to the point of  failure, and then it proceeds to rollback everything that had not yet committed. This action  will resynchronize the data files. It replays the work that was in progress, and undoes  anything that had not yet completed.  The second scenario is one that is played out much more often. It is useful to note that  during the rollback process, the redo logs are never involved. The only time redo logs are  read is during recovery and archival. This is a key tuning concept ‐ redo logs are written to.  Oracle does not read them during normal processing. As long as you have sufficient  devices so that when ARCH is reading a file, LGWR is writing to a different device, then  there is no contention for redo logs. Many other databases treat the log files as ʹtransaction  logsʹ. They do not have this separation of redo and undo ‐ they keep both in the same file.  For those systems, the act of rolling back can be disastrous ‐ the rollback process must read  the logs their log writer is trying to write to. They introduce contention into the part of the  system that can least stand it. Oracleʹs goal is to make it so that logs are written  sequentially, and no one ever reads them while they are being written ‐ ever.  Now, onto the DELETE statement. Again, undo is generated, blocks are modified, and  redo is sent over to the redo log buffer. This is not very different from before. In fact, it is  so similar to the UPDATE that we are going to go right onto the COMMIT. Weʹve looked  at various failure scenarios and different paths, and now we finally made it to the  COMMIT. Here, Oracle will flush the redo log buffer to disk and the picture will look like  this:  Expert one-on-one Oracle 198   The modified blocks are in the buffer cache; maybe some of them have been flushed to  disk. All of the redo necessary to replay this transaction is safely on disk and the changes  are now permanent. If we were to read the data directly from the data files, we probably  would see the blocks as they existed before the transaction took place, as DBWR most likely  has not yet written them. That is OK ‐ the redo log files can be used to restore those blocks  in the event of a failure. The undo information will hang around until the rollback  segment wraps around and reuses those blocks. Oracle will use that undo to provide for  consistent reads of the affected objects for any session that needs it.  Summary  In this section we looked at many aspects of transaction management in Oracle.  Transactions are one of the major features that set a database apart from a file system.  Understanding how they work how to use them is necessary and in order to implement  applications correctly in any database. Understanding that, in Oracle, all statements are  atomic (including their side effects) and that this atomicity is extended to stored  procedures is crucial. We saw how the placement of a WHEN OTHERS exception handler  in a PL/SQL block could radically affect what changes took place in the database. As  database developers, having a good understanding of how transactions work is crucial.  We took a look at the somewhat complex interaction between integrity constraints (unique  keys, check constraints and the like) and transactions in Oracle. We discussed how Oracle  typically processes integrity constraints immediately after a statement executes, but that  we can defer this constraint validation until the end of the transaction if we wish. This  feature is key in implementing complex multi‐table updates when the tables being  modified are all dependent on each other ‐ the cascading update was an example of that.  We moved on to consider some of the bad transaction habits that people tend to pick up  from working with databases that ʹsupportʹ rather than ʹpromoteʹ the use of transactions.  We looked at the cardinal rule of transactions: they should be as sort as they can be but as  long as they need to be. Data integrity drives the transaction size ‐ that is a key concept to  take away from this chapter. The only things that should drive the size of your  Expert one-on-one Oracle 199 transactions are the business rules that govern your system, not rollback space, not locks ‐  business rules.  We covered distributed transactions and how they differ from single database transactions.  We explored the limitations imposed upon us in a distributed transaction and discussed  why they are there. Before you build a distributed system, you need to understand these  limitations. What works in a single instance might not work in a distributed database.   We closed with a look at redo and undo, the architectural features that serve to enforce the  ACID transaction properties discussed at the start of this chapter. We looked at what  happens when we modify data in a transaction with regards to redo and undo (rollback  data). This last topic will be considered in much more detail in the next chapter.  Expert one-on-one Oracle 200 Chapter 5: Redo and Rollback  Overview  In Chapter 4 on Transactions, we covered the basic mechanics of redo and rollback (also  known as undo). There we covered what redo is. Simply put, it is the information Oracle  records in the online redo log files in order to replay your transaction in the event of a  failure. It allows Oracle to, in effect, ʹredoʹ your transaction(s). We also covered undo, or  rollback, the information Oracle records in the rollback segments in order to undo or roll  back your transaction. Additionally, we touched on a couple of issues, such as why you  might get an ORA‐01555: snapshot too old error, and why you might see checkpoint not  complete, cannot allocate new log. What I would like to do in this chapter, is go over in  more depth the concepts behind redo and rollback, and what you, the developer, need to  know about them.  Redo and rollback is a topic that bridges the DBA and developer roles. Both need a good  fundamental understanding of their purpose, how they work, and how to avoid issues  with regards to them. Weʹll be covering this information here. What we will not cover are  the things your DBA should be exclusively in charge of figuring out and tuning. For  example, how to find the optimum setting for RECOVERY_PARALLELISM or the  FAST_START_IO_TARGET init.ora parameters are topics we will not cover. Instead, we  will concentrate on the things that a database developer should be concerned with, and  how they will impact your application.  Redo  Redo log files are extremely crucial to the Oracle database. These are the transaction logs  for the database. They are used only for recovery purposes; their only purpose in life is to  be used in the event of an instance or media failure. If the power goes off on your database  machine, causing an instance failure, Oracle will use the online redo logs in order to  restore the system to exactly the point it was at, immediately prior to the power outage. If  your disk drive fails, Oracle will utilize archived redo logs as well as online redo logs in  order to restore a backup of that drive to the correct point in time. Additionally, if you  ʹaccidentallyʹ drop a table, or remove some critical information and commit this operation,  you can restore a backup of the affected data and restore it to the point in time  immediately prior to the ʹaccidentʹ, using these online and archive redo log files.   Oracle maintains two types of redo log files, online and archived. Every Oracle database  has at least two online redo log files. These online redo log files are used in a circular  fashion. Oracle will write to log file 1, and when it gets to the end of that file, it will switch  to log file 2, and begin writing to this one. When it has filled log file 2, it will switch back  Expert one-on-one Oracle 201 to log file 1 (assuming we have only two redo log files, if you have three, it would of  course proceed onto the third file). Archived redo log files are simply copies of old, full  online redo log files. As the system fills up log files, the ARCH process will make a copy of  the online redo log file in another location. These archived redo log files are used to  perform media recovery, when a failure is caused by a disk drive going bad, or some other  physical fault. Oracle can take these archived redo log files, and apply them to backups of  the data files to catch them up to the rest of the database. They are the transaction history  of the database.  Redo, or transaction logs, are one of the major features that make a database a database.  They are perhaps its most important recovery structure, although without the other pieces  such as rollback segments, distributed transaction recovery, and so on, nothing works.  They are major components of what sets a database apart from a file system. The online  redo logs allow us to effectively recover from a power outage ‐ one that happens while  Oracle might be in the middle of a write. The archived redo logs allow us to recover from  media failures, when for instance, the hard disk goes bad. Without them, the database  would not offer any more protection than a file system.  It is important for us to understand how these log files might impact on us as developers.  We will look at how the different ways we can write our code affect their utilization. We  will look at why some database errors (specifically the ORA‐01555: snapshot too old)  happen, and how to avoid them. Weʹve already seen the mechanics of redo in Chapter 4,  and now weʹll look at some specific issues. Many of these scenarios might be detected by  you, but would be fixed by the DBA as they affect the database instance as a whole. Weʹll  start with what happens during a COMMIT, and then get into commonly asked questions  and issues surrounding the online redo logs.  What Does a COMMIT Do?  Sometimes, people want to understand exactly what happens during a COMMIT, and as a  developer you should have an understanding of what goes on. A COMMIT is a very fast  operation, regardless of the transaction size. One might think that the bigger a transaction  is (in other words, the more data it affects) the longer a COMMIT will take. This is not true.  The response time of a COMMIT is generally ʹflatʹ, regardless of the transaction size. This  is because a COMMIT does not really have too much work to do, but what it does do is  vital.  One of the reasons this is an important fact to understand and embrace, is that it will lead  you down the path of letting your transactions be as big as they should be. Many  developers artificially constrain the size of their transactions, committing every so many  rows, instead of committing when a logical unit of work has been performed. They do this  in the mistaken belief that they are lessening the resources on the system, when in fact  they are increasing them. If a COMMIT of one row takes X units of time, and the COMMIT  Expert one-on-one Oracle 202 of one thousand rows takes the same X units of time, then performing work in a manner  that does 1000 one‐row COMMITs will not take an additional 1000*X units of time to  perform. By only committing when you have to (when the transaction is complete), you  will not only increase performance, but also reduce contention for shared resources (the  log files, various internal latches, and the like). A simple example demonstrates that it  necessarily takes longer:   tkyte@TKYTE816> create table t ( x int );  tkyte@TKYTE816> set serveroutput on  tkyte@TKYTE816> declare    2          l_start number default dbms_utility.get_time;    3          begin    4          for i in 1 .. 1000    5          loop    6                  insert into t values ( 1 );    7          end loop;    8          commit;    9          dbms_output.put_line   10          ( dbms_utility.get_time‐l_start || ʹ hsecsʹ );   11  end;   12  /  7 hsecs        PL/SQL procedure successfully completed.        tkyte@TKYTE816> declare    2          l_start number default dbms_utility.get_time;    3  begin    4          for i in 1 .. 1000    5          loop    6                  insert into t values ( 1 );    7                  commit;    8          end loop;    9          dbms_output.put_line   10          ( dbms_utility.get_time‐l_start || ʹ hsecsʹ );   11  end;   12  /  21 hsecs        PL/SQL procedure successfully completed.  In this case, it is three times longer ‐ your mileage will vary on this. When you compound  the above example with multiple users doing the same work, all committing too  Expert one-on-one Oracle 203 frequently, the numbers will go up rapidly. Weʹve seen this, time and time again with  other similar situations. For example, weʹve seen how not using bind variables and  performing hard parses frequently severely reduces concurrency due to library cache  contention, and excessive CPU utilization. Even when we switch to using bind variables,  soft parsing too frequently incurs massive overhead. You must only perform operations  when you need to ‐ a COMMIT is just another operation like a parse. It is best to size your  transactions based on business need, not based on misguided attempts to lessen resource  usage on the database.  So, why is a COMMITʹs response time fairly flat, regardless of the transaction size? Before  we even go to COMMIT in the database, we have already done the really hard work.  Weʹve already modified the data in the database, so weʹve already done 99.9 percent of the  work. For example, operations such as the following have already taken place:  • Rollback segment records have been generated in the SGA.   • Modified data blocks have been generated in the SGA.   • Buffered redo for the above two items has been generated in the SGA.   • Depending on the size of the above three, and the amount of time spent, some  combination of the above data may be flushed onto disk already.   • All locks have been acquired.   When we COMMIT, all that is left to happen is the following:  • Generate a SCN (System Change Number) for our transaction.   • LGWR writes all of our remaining buffered redo log entries to disk, and records the  SCN in the online redo log files as well. This step is actually the COMMIT. If this  step occurs, we have committed. Our transaction entry is removed, this shows that  we have committed. Our record in the V$TRANSACTION view will ʹdisappearʹ.   • All locks held by our session are released, and everyone who was enqueued  waiting on locks we held will be released.   • Many of the blocks our transaction modified will be visited and ʹcleaned outʹ in a  fast mode if they are still in the buffer cache.   As you can see, there is very little to do in order to process a COMMIT. The lengthiest  operation is, and always will be, the activity performed by LGWR, as this is physical disk  I/O. The amount of time spent by LGWR here will be gated (limited) by the fact that it has  already been flushing the contents of the redo log buffer on a recurring basis. LGWR will  not buffer all of the work you do for as long as you do it. Rather, it will incrementally flush  the contents of the redo log buffer in the background as we are going along. This is to  avoid having a COMMIT wait for a very long time in order to flush all of your redo at  once. LGRW does this flushing continuously as we are processing at least:  • Every three seconds.   Expert one-on-one Oracle 204 • When one third or one MB full.   • Upon any transaction COMMIT.   So, even if we have a long running transaction, much of the buffered redo log it generates  will have been flushed to disk, prior to committing. On the flip side of this however, is the  fact that when we COMMIT, we must wait until all buffered redo weʹve generated that has  not been written yet, is safely on disk. That is, our call to LGWR is a synchronous one.  While LGWR may use asynchronous I/O to write in parallel to our log files, our  transaction will wait for LGWR to complete all writes, and receive confirmation that the  data exists on disk before returning.  In case you are not familiar with it, the SCN I refer to above is a simple timing mechanism  Oracle uses to guarantee the ordering of transactions, and to enable recovery from failure.  It is also used to guarantee read‐consistency, and checkpointing in the database. Think of  the SCN as a ticker; every time someone COMMITs, the SCN is incremented by one.  The last point to clarify from the above list, is what is meant by ʹcleaned outʹ as related to  database blocks. I said that we would revisit some of the blocks our transaction modified  during the COMMIT process, and clean them out. This refers to the lock‐related  information we store in the database block header. In the section on Block Cleanout below,  we will discuss this in more detail. Briefly, we are cleaning out our transaction information  on the block, so the next person who visits the block wonʹt have to. We are doing this in a  way that need not generate redo log information, saving considerable work later.  In order to demonstrate that a COMMIT is a ʹflat response timeʹ operation, Iʹll generate  varying amounts of redo, and time the INSERTs and COMMITs. In order to do this, weʹll  need a couple of GRANTs on some V$ tables (see Chapter 10, Tuning Strategies and Tools,  for more information on these tables). Once we get those GRANTs, weʹll set up a fairly  large table to test with. In this example, I use the ALL_OBJECTS view to generate rows of  data, and INSERT enough copies of these rows to give me about 100,000 rows to work  with (your INSERTs may need to be executed more or less times to achieve the same row  counts):   tkyte@TKYTE816> connect sys/change_on_install        sys@TKYTE816> grant select on v_$mystat to tkyte;        Grant succeeded.        sys@TKYTE816> grant select on v_$statname to tkyte;        Grant succeeded.        Expert one-on-one Oracle 205 sys@TKYTE816> connect tkyte/tkyte        tkyte@TKYTE816> drop table t;        Table dropped.        tkyte@TKYTE816> create table t    2  as    3  select * from all_objects    4  /        Table created.        tkyte@TKYTE816> insert into t select * from t;        21979 rows created.        tkyte@TKYTE816> insert into t select * from t;        43958 rows created.        tkyte@TKYTE816> insert into t select * from t where rownum < 12000;        11999 rows created.        tkyte@TKYTE816> commit;        Commit complete.        tkyte@TKYTE816> create or replace procedure do_commit( p_rows in number )    2  as    3      l_start        number;    4      l_after_redo   number;    5      l_before_redo  number;    6  begin    7      select v$mystat.value into l_before_redo    8        from v$mystat, v$statname    9       where v$mystat.statistic# = v$statname.statistic#   10         and v$statname.name = ʹredo sizeʹ;   11   12      l_start := dbms_utility.get_time;   13      insert into t select * from t where rownum < p_rows;   14      dbms_output.put_line  Expert one-on-one Oracle 206  15      ( sql%rowcount || ʹ rows createdʹ );   16      dbms_output.put_line   17      ( ʹTime to INSERT: ʹ ||   18         to_char( round( (dbms_utility.get_time‐l_start)/100, 5 ),   19                 ʹ999.99ʹ) ||   20        ʹ secondsʹ );   21   22      l_start := dbms_utility.get_time;   23      commit;   24      dbms_output.put_line   25      ( ʹTime to COMMIT: ʹ ||   26         to_char( round( (dbms_utility.get_time‐l_start)/100, 5 ),   27                 ʹ999.99ʹ) ||   28        ʹ secondsʹ );   29   30      select v$mystat.value into l_after_redo   31        from v$mystat, v$statname   32       where v$mystat.statistic# = v$statname.statistic#   33         and v$statname.name = ʹredo sizeʹ;   34   35      dbms_output.put_line   36      ( ʹGenerated ʹ ||   37         to_char(l_after_redo‐l_before_redo,ʹ999,999,999,999ʹ) ||   38        ʹ bytes of redoʹ );   39      dbms_output.new_line;   40  end;   41  /        Procedure created.  Now we are ready to see the effects of committing various sizes of transactions. We will  call the above procedure, ask it to create new rows in varying sizes, and then report the  results:   tkyte@TKYTE816> set serveroutput on format wrapped  tkyte@TKYTE816> begin    2      for i in 1 .. 5    3      loop    4          do_commit( power(10,i) );    5      end loop;    6  end;    7  /  9 rows created  Expert one-on-one Oracle 207 Time to INSERT:     .06 seconds  Time to COMMIT:     .00 seconds  Generated            1,512 bytes of redo        99 rows created  Time to INSERT:     .06 seconds  Time to COMMIT:     .00 seconds  Generated           11,908 bytes of redo        999 rows created  Time to INSERT:     .05 seconds  Time to COMMIT:     .00 seconds  Generated          115,924 bytes of redo        9999 rows created  Time to INSERT:     .46 seconds  Time to COMMIT:     .00 seconds  Generated        1,103,524 bytes of redo        99999 rows created  Time to INSERT:   16.36 seconds  Time to COMMIT:     .00 seconds  Generated       11,220,656 bytes of redo              PL/SQL procedure successfully completed.        tkyte@TKYTE816> show parameter log_buffer        NAME                                 TYPE    VALUE  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  log_buffer                           integer 512000  As you can see, as we generate varying amount of redo from 1,512 bytes to 11,220,656  bytes, the time to COMMIT is not measurable using a timer with a one hundredth of a  second resolution. I timed the INSERTs specifically to demonstrate that the timer logic  actually ʹworksʹ. If you do something that takes a measurable amount of time, itʹll report it  ‐ the COMMITs just happened too fast. As we were processing, and generating the redo  log, LGWR was constantly flushing our buffered redo information to disk in the  background as we went along. So, when we generated eleven MB of redo log information,  LGWR was busy flushing every 170 KB or so (a third of 512,000 bytes). When it came to  the COMMIT, there wasnʹt much left to do ‐ not much more than when we created nine  Expert one-on-one Oracle 208 rows of data. You should expect to see similar (but not exactly the same) results,  regardless of the amount of redo generated.  What Does a ROLLBACK Do?  Now, if we change the COMMIT to ROLLBACK, we can expect a totally different result.  The time to roll back will definitely be a function of the amount of data modified. I  changed the DO_COMMIT routine we developed in the What Does a COMMIT Do? section  to perform a ROLLBACK instead (simply change the COMMIT on line 23 to ROLLBACK)  and the timings are very different. For example:   9 rows created  Time to INSERT:     .06 seconds  Time to ROLLBACK:     .02 seconds  Generated            1,648 bytes of redo        99 rows created  Time to INSERT:     .04 seconds  Time to ROLLBACK:     .00 seconds  Generated           12,728 bytes of redo        999 rows created  Time to INSERT:     .04 seconds  Time to ROLLBACK:     .01 seconds  Generated          122,852 bytes of redo        9999 rows created  Time to INSERT:     .94 seconds  Time to ROLLBACK:     .08 seconds  Generated        1,170,112 bytes of redo        99999 rows created  Time to INSERT:    8.08 seconds  Time to ROLLBACK:    4.81 seconds  Generated       11,842,168 bytes of redo              PL/SQL procedure successfully completed.  This is to be expected, as a ROLLBACK has to physically undo the work weʹve done.  Similar to a COMMIT, there is a series of operations that must be performed. Before we  even get to the ROLLBACK, the database has already done a lot of work. To recap, the  following would have happened:  Expert one-on-one Oracle 209 • Rollback segment records have been generated in the SGA.   • Modified data blocks have been generated in the SGA.   • Buffered Redo log for the above two items has been generated in the SGA.   • Depending on the size of the above three, and the amount of time spent, some  combination of the above data may be flushed onto disk already.   • All locks have been acquired.   When we ROLLBACK:  • We undo all of the changes made. This is accomplished by reading the data back  from the ROLLBACK (undo) segment, and in effect, reversing our operation. If we  inserted a row, a ROLLBACK will delete it. If we updated a row, a rollback will  reverse the update. If we deleted a row, a rollback will re‐insert it again.   • All locks held by our session are released, and everyone who was enqueued  waiting on locks we held will be released.   A COMMIT on the other hand just flushes any remaining data in the redo log buffers. It  does very little work compared to a ROLLBACK. The point here is that you donʹt want to  roll back unless you have to. It is expensive since you spend a lot of time doing the work,  and youʹll also spend a lot of time undoing the work. Donʹt do work unless you are sure  you are going to want to COMMIT it. This sounds like common sense; of course, I  wouldnʹt do all of the work unless I wanted to COMMIT it. I have seen many times  however, where a developer will utilize a ʹrealʹ table as a temporary table, fill it up with  data, and then roll back. Below, weʹll talk about true temporary tables, and how to avoid  this issue.  How Much Redo Am I Generating?  As a developer, you will find that it can be relevant to be able to measure how much redo  your operations generate. The more redo you generate, the longer your operations will  take, and the slower the entire system will be. You are not just affecting your session, but  every session. Redo management is a point of serialization within the database ‐ eventually  all transactions end up at LGWR, asking it to manage their redo and COMMIT their  transaction. The more it has to do, the slower the system will be. By seeing how much redo  an operation tends to generate, and testing more than one approach to a problem, you can  find the best way to do things.  It is pretty straightforward to see how much redo I am generating, as shown above. I used  the dynamic performance view V$MYSTAT, which has just my sessionʹs statistics in it,  joined to V$STATNAME. I retrieved the value of statistic named redo size. I didnʹt have to  guess at this name, because I used the view V$STATNAME to find it:   ops$tkyte@DEV816> select * from v$statname  Expert one-on-one Oracle 210   2  where name like ʹredo%ʹ;        STATISTIC# NAME                                CLASS  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐          61 redo synch writes                       8          62 redo synch time                         8          98 redo entries                            2          99 redo size                               2         100 redo buffer allocation retries          2         101 redo wastage                            2         102 redo writer latching time               2         103 redo writes                             2         104 redo blocks written                     2         105 redo write time                         2         106 redo log space requests                 2         107 redo log space wait time                2         108 redo log switch interrupts              2         109 redo ordering marks                     2        14 rows selected.  Now we are ready to investigate how we might go about determining the amount of redo  a given transaction would generate. It is straightforward to estimate how much redo will  be generated if you know how much data you will be modifying. Below, I will create a  table whose row size is about 2010 bytes, plus or minus a couple of bytes. Since the CHAR  data type always consumes the maximum amount of storage, this row is 2000 bytes for the  CHAR, seven bytes for the DATE, and three bytes for the number ‐ about 2010 bytes, plus  some row overhead:   tkyte@TKYTE816> create table t ( x int, y char(2000), z date );        Table created.  Weʹll take a look at what it takes to INSERT, then UPDATE, and then DELETE one, ten,  and lots of these rows. We will also see if there is any measurable difference between  updating a set of rows in bulk, versus updating each row one by one, based on the amount  of redo generated. We already know that updating row by row is slower than using a  single UPDATE statement.  To measure the redo generated, we will use either the SQL*PLUS AUTOTRACE facility, or  a direct query against a view of V$MYSTAT/V$STATNAME that shows our sessionʹs redo  size:   Expert one-on-one Oracle 211 tkyte@TKYTE816> create or replace view redo_size    2  as    3  select value    4    from v$mystat, v$statname    5   where v$mystat.statistic# = v$statname.statistic#    6     and v$statname.name = ʹredo sizeʹ;        View created.  For the statements that AUTOTRACE will trace (INSERTs, UPDATEs, and DELETEs),  weʹll use it. For our PL/SQL blocks, weʹll resort to V$MYSTAT/V$STATNAME instead,  since AUTOTRACE will not generate information for these statements.  Now, for the process to exercise redo generation, we will use the table T above which has a  fairly fixed row size of 2010 bytes if all of the columns are not Null. We will do various  operations and measure the redo generated for each. What we will do is INSERT a single  row, then ten rows with a single statement, then 200 rows with a single statement, and  then 200 rows one at a time. We will do similar operations for UPDATE and DELETE as  well.   The code for this example follows. Instead of the usual cut and paste directly from  SQL*PLUS, weʹll just look at the statements that were used, and then look at a table that  summarizes the results:  set autotrace traceonly statistics  insert into t values ( 1, user, sysdate );        insert into t  select object_id, object_name, created    from all_objects   where rownum <= 10;        insert into t  select object_id, object_name, created    from all_objects   where rownum <= 200  /        declare    l_redo_size number;    l_cnt       number := 0;  begin     select value into l_redo_size from redo_size;  Expert one-on-one Oracle 212    for x in ( select * from all_objects where rownum <= 200 )     loop        insert into t values        ( x.object_id, x.object_name, x.created );        l_cnt := l_cnt+1;     end loop;     select value‐l_redo_size into l_redo_size from redo_size;     dbms_output.put_line( ʹredo size = ʹ || l_redo_size ||                           ʹ rows = ʹ || l_cnt );  end;  /  The above code fragment does our INSERTs as described ‐ 1, 10, 200 at a time, and then  200 individual INSERTs. Next, weʹll do the UPDATEs:   update t set y=lower(y) where rownum = 1;        update t set y=lower(y) where rownum <= 10;        update t set y=lower(y) where rownum <= 200;        declare    l_redo_size number;    l_cnt       number := 0;  begin    select value into l_redo_size from redo_size;    for x in ( select rowid r from t where rownum <= 200 )    loop       update t set y=lower(y) where rowid = x.r;       l_cnt := l_cnt+1;    end loop;    select value‐l_redo_size into l_redo_size from redo_size;    dbms_output.put_line( ʹredo size = ʹ || l_redo_size ||                          ʹ rows = ʹ || l_cnt );  end;  /  and then the DELETEs:   delete from t where rownum = 1;        delete from t where rownum <= 10;        Expert one-on-one Oracle 213 delete from t where rownum <= 200;        declare    l_redo_size number;    l_cnt       number := 0;  begin     select value into l_redo_size from redo_size;     for x in ( select rowid r from t ) loop        delete from t where rowid = x.r;        l_cnt := l_cnt+1;     end loop;     select value‐l_redo_size into l_redo_size from redo_size;     dbms_output.put_line( ʹredo size = ʹ || l_redo_size ||                           ʹ rows = ʹ || l_cnt );  end;  /  Here are the end results:  Operation  Rows Affected  Total Redo  Average per Row INSERT one row   1  2,679  2,679  INSERT 10 rows in one statement   10  22,260  2,226  INSERT 200 rows in one statement   200  442,784  2,213  INSERT 200 rows, one at a time   200  464,224  2,321  UPDATE one row   1  4,228  4,228  UPDATE 10 rows in one statement   10  42,520  4,252  UPDATE 200 rows in one statement   200  849,600  4,248  UPDATE 200 rows individually   200  849,700  4,248  DELETE one row   1  2,236  2,236  DELETE 10 rows in bulk   10  23,688  2,369  DELETE 200 rows in bulk   200  469,152  2,345  DELETE 200 rows one at a time   200  469,212  2,346        The interesting thing to note is that, in general, whether you UPDATE 200 rows with one  statement or 200 statements, the same amount of redo is generated. The same is true for  the DELETEs ‐ one statement or 200 statements, the result is pretty much the same. An  INSERT behaves a little differently. It generates slightly more redo for single row inserts  Expert one-on-one Oracle 214 which is reasonable, given that it must organize data on the block differently when  inserting one at a time, versus many at a time (it does slightly more work).  As you can see, the redo generated is a function of the amount of data modified. If we  INSERT 2000 byte rows, we generate a little more than 2000 bytes per row. When we  UPDATE, we generate double the amount of redo (we log the data and rollback as you  recall, so that makes sense). A DELETE is similar to an INSERT in size. The entire row is  recorded in the rollback segment and this is logged, as well as the block changes  themselves, accounting for the small disparity. So, as long as you understand the amount  of data you will be modifying, and to some extent, how you modify it, determining the  amount of redo is straightforward.  This is not a case study that proves that single row processing is as efficient as set  processing. It only shows that the amount of redo generated is the same. We have seen in  other sections that procedurally processing a row at a time is never as efficient as doing  the set operation itself. Additionally, if you are tempted to throw a COMMIT in the loop,  as many people do in the mistaken belief it will conserve some resource, youʹll only  compound the problem. Now that we know how to measure the redo we generate, we can  see the effect of this bad idea clearly. Weʹll use the same example schema from above and  measure what happens when we throw a COMMIT in the loop:   tkyte@TKYTE816> declare    2    l_redo_size number;    3    l_cnt       number := 200;    4    procedure report    5    is    6    begin    7       select value‐l_redo_size into l_redo_size from redo_size;    8       dbms_output.put_line( ʹredo size = ʹ || l_redo_size ||    9                             ʹ rows = ʹ || l_cnt || ʹ ʹ ||   10                             to_char(l_redo_size/l_cnt,ʹ99,999.9ʹ) ||   11                             ʹ bytes/rowʹ );   12     end;   13  begin   14    select value into l_redo_size from redo_size;   15    for x in ( select object_id, object_name, created   16                 from all_objects   17                where rownum <= l_cnt )   18    loop   19          insert into t values   20          ( x.object_id, x.object_name, x.created );   21          commit;   22    end loop;  Expert one-on-one Oracle 215  23    report;   24   25    select value into l_redo_size from redo_size;   26    for x in ( select rowid rid from t )   27    loop   28        update t set y = lower(y) where rowid = x.rid;   29        commit;   30        end loop;   31    report;   32   33    select value into l_redo_size from redo_size;   34    for x in ( select rowid rid from t )   35    loop   36        delete from t where rowid = x.rid;   37        commit;   38    end loop;   39    report;   40  end;   41  /  redo size = 530396 rows = 200   2,652.0 bytes/row  redo size = 956660 rows = 200   4,783.3 bytes/row  redo size = 537132 rows = 200   2,685.7 bytes/row        PL/SQL procedure successfully completed.  As you can see, committing each row increased the amount of redo generated measurably,  (the table below summarizes this). There are other performance‐related issues as to why  you should not COMMIT every row (or possibly even groups of rows). We saw concrete  proof of this above, where a COMMIT for each row took three times as long as committing  when the transaction was complete. There is the overhead of calling into the database  kernel with each individual statement, there is unnecessary latching and contention for  shared resources for each individual statement. The bottom line is that if you can do it in a  single SQL statement ‐ do it. Additionally make sure you COMMIT when the transaction  is complete, never before.  Operation  Rows affected Total redo (no  COMMITs)  Total redo (with  COMMITs)  % increase  INSERT 200 rows   200  442,784  530,396  20%  UPDATE 200 rows   200  849,600  956,660  13%  DELETE 200 rows   200  469,152  537,132  14%  Expert one-on-one Oracle 216 The method we outline above is useful in general for seeing the side effects of various  other options. One question that comes up frequently is, other than the fact that you can  modify the values of a row in a BEFORE trigger, are there any other differences? Well, as it  turns out, yes there is. A BEFORE trigger tends to add additional redo information, even if  it does not modify any of the values in the row. In fact, this is an interesting case study and  using the techniques above weʹll discover that:  • A BEFORE or AFTER trigger does not affect DELETEs.   • An INSERT generates extra redo in the same volume for either a BEFORE or  AFTER trigger.   • An UPDATE is affected only by the existence of a BEFORE trigger ‐ the AFTER  trigger adds no additional redo.   • The size of the row affects the amount of additional redo generated for INSERTs,  but not for the UPDATE.   In order to perform this test, weʹll use the table T from above:   create table t ( x int, y char(N), z date );  but weʹll create it with varying sizes for N. In this example, weʹll use N = 30, 100, 500, 1000,  and 2000 to achieve rows of varying widths. I used a simple log table to capture the results  of my many runs:   create table log ( what varchar2(15), ‐‐ will be no trigger, after or before                     op varchar2(10),   ‐‐ will be insert/update or delete                     rowsize int,       ‐‐ will be the size of Y                     redo_size int,     ‐‐ will be the redo generated                     rowcnt int );      ‐‐ will be the count of rows affected  After we run our test for various sized Y columns, weʹll analyze the results. I used this  stored procedure to generate my transactions, and record the redo generated. The  subprocedure REPORT is a local procedure (only visible in the DO_WORK procedure),  and it simply reports out on screen what happened, and captures the findings into our  LOG table. The main body of the procedure does the real transactions we are monitoring.  They all start by capturing our sessionʹs current redo size, performing some work,  committing, and then generating the report:   tkyte@TKYTE816> create or replace procedure do_work( p_what in varchar2 )    2  as    3    l_redo_size number;    4    l_cnt       number := 200;    5    6    procedure report( l_op in varchar2 )    7    is  Expert one-on-one Oracle 217   8    begin    9       select value‐l_redo_size into l_redo_size from redo_size;   10       dbms_output.put_line(l_op || ʹ redo size = ʹ || l_redo_size ||   11                             ʹ rows = ʹ || l_cnt || ʹ ʹ ||   12                             to_char(l_redo_size/l_cnt,ʹ99,999.9ʹ) ||   13                             ʹ bytes/rowʹ );   14      insert into log   15      select p_what, l_op, data_length, l_redo_size, l_cnt   16        from user_tab_columns   17       where table_name = ʹTʹ   18         and column_name = ʹYʹ;   19     end;   20  begin   21    select value into l_redo_size from redo_size;   22    insert into t   23    select object_id, object_name, created   24      from all_objects   25     where rownum <= l_cnt;   26    l_cnt := sql%rowcount;   27    commit;   28    report(ʹinsertʹ);   29   30    select value into l_redo_size from redo_size;   31    update t set y=lower(y);   32    l_cnt := sql%rowcount;   33    commit;   34    report(ʹupdateʹ);   35   36    select value into l_redo_size from redo_size;   37    delete from t;   38    l_cnt := sql%rowcount;   39    commit;   40    report(ʹdeleteʹ);   41  end;   42  /        Procedure created.  Now, once I have this in place, I drop and create the table T, changing the size of the  column Y. I then run the following script to test the various scenarios (no TRIGGER,  before TRIGGER, and after TRIGGER):   tkyte@TKYTE816> truncate table t;  Expert one-on-one Oracle 218       Table truncated.        tkyte@TKYTE816> exec do_work( ʹno triggerʹ );  insert redo size = 443280 rows = 200   2,216.4 bytes/row  update redo size = 853968 rows = 200   4,269.8 bytes/row  delete redo size = 473620 rows = 200   2,368.1 bytes/row        PL/SQL procedure successfully completed.        tkyte@TKYTE816> create or replace trigger before_insert_update_delete    2  before insert or update or delete on T for each row    3  begin    4          null;    5  end;    6  /        Trigger created.        tkyte@TKYTE816> truncate table t;        Table truncated.        tkyte@TKYTE816> exec do_work( ʹbefore triggerʹ );  insert redo size = 465640 rows = 200   2,328.2 bytes/row  update redo size = 891628 rows = 200   4,458.1 bytes/row  delete redo size = 473520 rows = 200   2,367.6 bytes/row        PL/SQL procedure successfully completed.        tkyte@TKYTE816> drop trigger before_insert_update_delete;        Trigger dropped.        tkyte@TKYTE816> create or replace trigger after_insert_update_delete    2  after insert or update or delete on T    3  for each row    4  begin    5          null;    6  end;    7  /        Trigger created.  Expert one-on-one Oracle 219       tkyte@TKYTE816> truncate table t;        Table truncated.        tkyte@TKYTE816> exec do_work( ʹafter triggerʹ );  insert redo size = 465600 rows = 200   2,328.0 bytes/row  update redo size = 854028 rows = 200   4,270.1 bytes/row  delete redo size = 473580 rows = 200   2,367.9 bytes/row        PL/SQL procedure successfully completed.  The above output was from a run where the size of Y was 2000 bytes. After all of the runs  were complete, I was able to query the log table and see:   tkyte@TKYTE816> break on op skip 1  tkyte@TKYTE816> set numformat 999,999        tkyte@TKYTE816> select op, rowsize, no_trig, before_trig‐no_trig, after_trig‐  no_trig    2  from ( select op, rowsize,    3             sum(decode( what, ʹno triggerʹ, redo_size/rowcnt,0 ) )                                                                  no_trig,    4             sum(decode( what, ʹbefore triggerʹ, redo_size/rowcnt, 0 ) )                                                                  before_trig,    5             sum(decode( what, ʹafter triggerʹ, redo_size/rowcnt, 0 ) )                                                                  after_trig    6           from log    7          group by op, rowsize    8       )    9   order by op, rowsize   10  /        OP          ROWSIZE  NO_TRIG BEFORE_TRIG‐NO_TRIG AFTER_TRIG‐NO_TRIG  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  delete           30       272                   0                  0                      100      344                  ‐0                 ‐0                      500      765                  ‐0                 ‐0                   1,000    1,293                  ‐1                 ‐0                   2,000    2,368                  ‐1                 ‐0        insert           30       60                 213                213                  100      136                 208                208  Expert one-on-one Oracle 220                 500      574                 184                184                1,000    1,113                 162                162                2,000    2,216                 112                112        update           30      294                 189                  0                  100      431                 188                  0                  500    1,238                 188                 ‐0                1,000    2,246                 188                 ‐0                2,000    4,270                 188                  0        15 rows selected.  Note If you are curious about this query and how I pivoted the result set, see Chapter 12  on Analytic Functions where I discuss pivoting of result sets in detail.   What the inner most query generated was a resultset that computed the average redo  bytes generated per row for each of the three test cases. The outer query simply displays  the average bytes per row for the case where no trigger was in place, and then the  subsequent two columns show the difference between the other cases, and the case with  no trigger in place. So, we can see that for the DELETE cases, there was no measurable  difference in the amount of redo generated per row at any time. Looking at the INSERT  case however, we can see that there was an overhead of 213 to 112 bytes per row,  regardless of the trigger type used (before or after). The bigger the row, the less the  overhead for some reason (I did not investigate why this is so, but just that this is the case).  Lastly, for the UPDATE case we see two things. First, the amount of redo generated is  constant regardless of the redo size ‐ the overhead for UPDATEs is fixed. Secondly, we can  see that an AFTER trigger is much more efficient for UPDATEs, as it does not affect the  redo generation at all. For this reason, you can develop a rule of thumb; use AFTER  triggers whenever possible for UPDATEs. If, and only if, you need functionality available  in the BEFORE trigger should you actually use one.  So, now you know how to estimate the amount of redo, which every developer should be  able to do. You can:  • Estimate your ʹtransactionʹ size ‐ how much data you modify.   • Add 10 to 20 percent overhead depending on the number of rows you will be  modifying ‐ the more rows, the less overhead.   • Double this value for UPDATEs.   In most cases, this will be a good estimate. The doubling on the UPDATEs is a guess ‐ it  really depends on how you modify the data. The doubling assumes you take a row of X  bytes, and UPDATE it to be a row of X bytes. If you take a small row and make it big, you  will not double the value (it will behave more like an INSERT). If you take a big row and  make it small, you will not double the value (it will behave like a DELETE). The doubling  Expert one-on-one Oracle 221 is a ʹworst caseʹ number, as there are various options and features that will impact this, for  example the existence of indexes (or lack thereof as in my case) will contribute to the  bottom line. The amount of work that must be done in order to maintain the index  structure may vary from UPDATE to UPDATE, and so on. Side effects from triggers have  to be taken into consideration (in addition to the fixed overhead described above). Implicit  operations performed on your behalf, such as an ON DELETE CASCADE setting on a  foreign key, must be considered as well. This will allow you to estimate the amount of  redo for sizing/performance purposes. Only real‐world testing will tell you for sure. Given  the above script, you can see how to measure this for yourself, given any of your objects  and transactions.  Can I Turn Off Redo Log Generation?  This question is often asked. The simple short answer is ʹnoʹ, since redo logging is crucial  for the database; it is not overhead, it is not a waste. You do need it, regardless of whether  you believe you do or not. It is a fact of life, and it is the way the database works. However,  that said, there are some operations that can be done without generating redo log in some  cases.   Some SQL statements and operations support the use of a NOLOGGING clause. This does  not mean that all operations against the object will be performed without generating redo  log, just that some very specific operations will generate significantly less redo then normal.  Note that I said ʹsignificantly lessʹ, not ʹnoneʹ. All operations will generate some redo ‐ all  data dictionary operations will be logged regardless of the logging mode. The amount of  redo generated can be significantly less. For example, I ran the following in a database  running in ARCHIVELOG mode. If you test on a NOARCHIVELOG mode database, you  will not see any differences. The CREATE TABLE will not be logged, with the exception of  the data dictionary modifications in a NOARCHIVELOG mode database. Oracle 7.3 users  however, will see the difference, as this optimization was not present in that database.  They will have to use UNRECOVERABLE instead of the NOLOGGING keyword as well  (NOLOGGING used to be UNRECOVERABLE in prior releases of Oracle). If you would  like to see the difference on a NOARCHIVELOG mode database, you can replace the  DROP TABLE and CREATE TABLE with a DROP INDEX and CREATE INDEX statement  on some table. These operations are logged by default, regardless of the mode in which the  database is running. This also points out a valuable tip ‐ test your system in the mode it  will be run in production, as the behavior may be different. Your production system will  be running in ARCHIVELOG mode; if you perform lots of operations that generate redo in  this mode, but not in NOARCHIVELOG mode, youʹll want to discover this during testing,  not during rollout to the users! Now for the example of the NOLOGGING clause:   tkyte@TKYTE816> column value new_value old_value  tkyte@TKYTE816> select value from redo_size;        Expert one-on-one Oracle 222      VALUE  ‐‐‐‐‐‐‐‐‐‐     5195512        tkyte@TKYTE816> create table t    2  as    3  select * from all_objects    4  /        Table created.        tkyte@TKYTE816> select value‐&old_value REDO_GENERATED from redo_size;  old   1: select value‐&old_value REDO_GENERATED from redo_size  new   1: select value‐   5195512 REDO_GENERATED from redo_size        REDO_GENERATED  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐         2515860  Here, over 2.5 MB of redo is generated in my database.  tkyte@TKYTE816> drop table t;        Table dropped.        tkyte@TKYTE816> select value from redo_size;             VALUE  ‐‐‐‐‐‐‐‐‐‐     7741248        tkyte@TKYTE816> create table t    2  NOLOGGING    3  as    4  select * from all_objects    5  /        Table created.        tkyte@TKYTE816> select value‐&old_value REDO_GENERATED from redo_size;  old   1: select value‐&old_value REDO_GENERATED from redo_size  new   1: select value‐   7741248 REDO_GENERATED from redo_size        Expert one-on-one Oracle 223 REDO_GENERATED  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐           43264  This time, there is only 50 KB of redo generated.   As you can see, this makes a tremendous difference; 2.5 MB of redo versus 50 KB. The 2.5  MB is the actual table data itself ‐ it was written directly to disk, with no redo log  generated for it. Of course, it is now obvious that we will do everything we can with  NOLOGGING, right? In fact the answer is a resounding no. You must use this very  carefully, and only after discussing the issues with the person in charge of backup and  recovery. Letʹs say you create this table and it is now part of your application (for example,  you used a CREATE TABLE AS SELECT NOLOGGING as part of an upgrade script).  Your users modify this table over the course of the day. That night, the disk that the table  is on fails. No problem the DBA says ‐ we are running in ARCHIVELOG mode, we can  perform media recovery. The problem is however, that the initially created table, since it  was not logged, is not recoverable from the archived redo log. This table is unrecoverable  and this brings out the very most important point about NOLOGGING operations ‐ they  must be coordinated with your DBA and the system as a whole. If you use them, and  others are not aware of the fact, you may compromise the ability of your DBA to recover  your database fully after a media failure. They must be used judiciously and carefully.  The important things to note with NOLOGGING operations are:  • Some amount of redo will be generated, as a matter of fact. This redo is to protect  the data dictionary. There is no avoiding this at all. It will be of a significantly less  amount than before, but there will be some.   • NOLOGGING does not prevent redo from being generated by all subsequent  operations. In the above example, I did not create a table that is never logged. Only  the single, individual operation of creating the table was not logged. All subsequent  ʹnormalʹ operations such as INSERTs, UPDATEs, and DELETEs will be logged.  Other special operations such as a direct path load using SQLLDR, or a direct path  insert using the INSERT /*+ APPEND */ syntax will not be logged. In general  however, the operations your application performs against this table will be logged.   • After performing NOLOGGING operations in an ARCHIVELOG mode database,  you must take a new baseline backup of the affected data files as soon as possible.  This is in order to avoid losing subsequent changes to these objects due to media  failure. We wouldnʹt actually lose the changes, as these are in the redo log. What  weʹve actually lost is the data to apply the changes to.   There are two ways to use the NOLOGGING option. You have already seen one method,  by embedding the keyword NOLOGGING in the SQL command itself at the appropriate  location. The other method allows operations to be performed implicitly in a  Expert one-on-one Oracle 224 NOLOGGING mode. For example, I can alter an index to be NOLOGGING by default.  This means that subsequent direct path loads and direct path inserts performed which  affect this index, will not be logged (the index will not generate redo ‐ other indexes and  the table itself might but this index will not).  The operations that may be performed in a NOLOGGING mode are:  • Index creations and ALTERs (rebuilds).   • Bulk INSERTs using a ʹdirect path insertʹ via the /*+ APPEND */ hint.   • LOB operations (updates to large objects do not have to be logged).   • Table creations via the CREATE TABLE AS SELECT.   • Various ALTER TABLE operations such as MOVE and SPLIT.   • TRUNCATE (but it does not need a NOLOGGING clause, as it is always in  NOLOGGING mode).   Used appropriately on an ARCHIVELOG mode database, NOLOGGING can speed up  many operations by dramatically reducing the amount of redo log generated. Suppose you  have a table you need to move from one tablespace to another. You can schedule this  operation to take place immediately before a backup occurs ‐ you would ALTER the table  to be NOLOGGING, move it, rebuild the indexes (without logging as well), and then  ALTER the table back to logging mode. Now, an operation that might have take X hours  can happen in X/2 hours perhaps. The appropriate use of this feature includes  involvement of the DBA, or whoever is responsible for database backup and recovery. If  they are not aware of the use of this feature and a media failure occurs, you may lose data.  This is something to seriously consider.  Cannot Allocate a New Log?  I see this happen all of the time, though not on my database of course! You are getting  warning messages to this effect (this will be found in your alert.log on your server):   Sun Feb 25 10:59:55 2001  Thread 1 cannot allocate new log, sequence 326  Checkpoint not complete  It might say Archival required instead of Checkpoint not complete, but the effect is pretty  much the same. This is really something the DBA should be looking out for however. In  the event that they do not do this, it is something you need to look for yourself. This  message will be written to the alert.log on the server whenever the database attempts to  reuse an online redo log file, and finds that it cannot. This will happen when DBWR has  not yet finished checkpointing the data protected by the redo log, or ARCH has not  finished copying the redo log file to the archive destination. If DBWR or ARCH do not  mean anything to you, please review Chapter 2 on Architecture, for more information. At  Expert one-on-one Oracle 225 this point in time, the database effectively halts as far as the end user is concerned. It stops  cold. DBWR or ARCH will be given priority to flush the blocks to disk. Upon completion  of the checkpoint or archival, everything goes back to normal. The reason the database  suspends user activity is that there is simply no place to record the changes they are  making. Oracle is attempting to reuse an online redo log file, but because either the file is  needed to recover the database in the event of a failure (Checkpoint not complete), or the  archiver has not yet finished copying it (Archival required). Oracle must wait (and our end  users will wait) until the redo log file can safely be reused.  If you see that your sessions spend a lot of time waiting on a ʹlog file switchʹ, ʹlog buffer  spaceʹ, or ʹlog file switch checkpoint or archival incompleteʹ, then you are most likely  hitting this (refer to Chapter 10, Tuning Strategies and Tools, for how to see what wait  events your session is suffering from). You will notice it during prolonged periods of  database modifications if your log files are sized incorrectly, or because DBWR and ARCH  need to be tuned by the DBA or System Administrator. I frequently see this with the  ʹstarterʹ database that has not been customized. The ʹstarterʹ database typically sizes the  redo logs far too small for any sizable amount of work (including the initial database build  of the data dictionary itself). As soon as you start loading up the database, you will notice  that the first 1000 rows go fast, and then things start going in spurts; 1000 go fast, then  hang, then fast, then hang, and so on. These are the indications you are hitting this  condition.  There are a couple of things you can do to solve this:  • Make DBWR faster. Have your DBA tune DBWR by enabling ASYNC I/O, using  DBWR I/O slaves, or using multiple DBWR processes. Look at the I/O on the system  and see if one disk, or a set of disks, is ʹhotʹ so we need to, therefore, spread it out.  The same general advice applies for ARCH as well. The pros of this are that you get  ʹsomething for nothingʹ here ‐ increased performance without really changing any  logic/structures/code. There really are no downsides to this approach.   • Add more redo log files. This will postpone the Checkpoint not complete in some  cases and, after a while, will postpone it so long that it perhaps doesnʹt happen (we  gave DBWR enough breathing room to checkpoint). The same applies to the  Archival required message. The benefit to this approach is the removal of the  ʹpausesʹ in your system. The downside is it consumes more disk, but the benefit far  outweighs any downside here.   • Recreate the log files with a larger size. This will extend the amount of time  between the time we fill the online redo log, and the time we need to reuse it. The  same applies to the Archival required message, if the redo log file usage is ʹburstyʹ.  If you have a period of massive log generation (nightly loads, batch processes)  followed by periods of relative calm, then having larger online redo logs can buy  enough time for ARCH to catch up during the calm periods. The pros and cons are  identical to the above approach of adding more files. Additionally, it may postpone  Expert one-on-one Oracle 226 a checkpoint from happening until later, since checkpoints happen at each log  switch (at least), and the log switches will now be further apart.   • Cause checkpointing to happen more frequently, and more continuously. Use a  smaller block buffer cache (not entirely desirable), or various init.ora settings such  as FAST_START_IO_TARGET, DB_BLOCK_MAX_DIRTY_TARGET,  LOG_CHECKPOINT_INTERVAL, and LOG_CHECKPOINT_TIMEOUT. This will  force DBWR to flush dirty blocks more frequently. The pros to this approach is that  recovery time from a failure is reduced. There will always be less work in the online  redo logs to be applied. The downside is that blocks will be written to disk more  frequently. The buffer cache will not be as effective as it could be, and can defeat  the block cleanout mechanism discussed below.   • The approach you take will depend on your circumstances. This is something that  must be fixed at the database level, taking the entire instance into consideration.   Block Cleanout  If you recall earlier, Chapter 3 on Locking and Concurrency, when we talked about data  locks and how they were managed, I described how they are actually attributes of the data,  stored on the block header. A side effect of this is that the next time that block is accessed,  we may have to ʹclean it outʹ, in other words, remove the transaction information. This  action generates redo and causes the block to become ʹdirtyʹ if it wasnʹt already. What this  means is that a simple SELECT may generate redo, and may cause lots of blocks to be  written to disk with the next checkpoint. Under most normal circumstances however, this  will not happen. If you have mostly small to medium‐sized transactions (OLTP), or you  are a data warehouse that analyzes tables after bulk operations, youʹll find the blocks are  generally ʹcleanedʹ for you. If you recall from the earlier section What Does a COMMIT Do?  one of the steps of COMMIT‐time processing is to revisit our blocks if they are still in the  SGA, if they are accessible (no one else is modifying them), and then clean them out. This  activity is known as a commit clean out. Our transaction cleans out the block enough so  that a SELECT (read) will not have to clean it out. Only an UPDATE of this block would  truly clean out our residual transaction information, and since this is already generating  redo, the cleanout is not noticeable.  We can force a cleanout to happen to see the side effects by understanding how the  commit cleanout functions. Oracle will allocate lists of blocks we have modified in a  commit list associated with our transaction. Each of these lists is 20 blocks long, and Oracle  will allocate as many of these lists as it needs up to a point. If the sum of the blocks we  modify exceeds 10 percent of the block buffer cache size, Oracle will stop allocating new  lists for us. For example, if our block buffer cache is set to 3,000, Oracle will maintain a list  of up to 300 blocks (10 percent of 3,000) for us. Upon COMMIT, Oracle will process each of  these lists of 20 block pointers, and if the block is still available, it will perform a fast  cleanout. So, as long as the number of blocks we modify does not exceed 10 percent of the  number of blocks in the cache and our blocks are still in the cache and available to us,  Expert one-on-one Oracle 227 Oracle will clean them out upon COMMIT. Otherwise, it just skips them (does not clean  them out). Given this understanding, we can set up artificial conditions to see how this  works. I set my DB_BLOCK_BUFFERS to a low value of 300. Then, I created a table such  that a row fits on exactly one block ‐ weʹll never have two rows per block. Then, I fill this  table up with 499 rows and COMMIT. Weʹll measure the amount of redo I have generated  so far, run a SELECT that will visit each block, and then measure the amount of redo that  SELECT generated.   Surprising to many people, the SELECT will have generated redo. Not only that, but it will  also have ʹdirtiedʹ these modified blocks, causing DBWR to write them again. This is due  to the block cleanout. Next, Iʹll run the SELECT once again and weʹll see that no redo is  generated. This is expected as the blocks are all ʹcleanʹ at this point.   tkyte@TKYTE816> create table t    2  ( x char(2000) default ʹxʹ,    3    y char(2000) default ʹyʹ,    4    z char(2000) default ʹzʹ )    5  /        Table created.        tkyte@TKYTE816> insert into t    2  select ʹxʹ,ʹyʹ,ʹzʹ    3  from all_objects where rownum < 500    4  /        499 rows created.        tkyte@TKYTE816> commit;        Commit complete.  So, this is our table with one row per block (in my 8 KB block size database). Now we will  measure the amount of redo generated during the read of the data:  tkyte@TKYTE816> column value new_value old_value  tkyte@TKYTE816> select * from redo_size;             VALUE  ‐‐‐‐‐‐‐‐‐‐     3250592        tkyte@TKYTE816> select *  Expert one-on-one Oracle 228   2    from t    3   where x = y;        no rows selected        tkyte@TKYTE816> select value‐&old_value  REDO_GENERATED from redo_size;  old   1: select value‐&old_value  REDO_GENERATED from redo_size  new   1: select value‐   3250592  REDO_GENERATED from redo_size        REDO_GENERATED  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐           29940        tkyte@TKYTE816> commit;        Commit complete.  So, this SELECT generated about 30 KB of redo during itʹs processing. This represents the  block headers it modified during the full scan of T. DBWR will be writing these modified  blocks back out to disk at some point in the future. Now, if we run the query again:   tkyte@TKYTE816> select value from redo_size;             VALUE  ‐‐‐‐‐‐‐‐‐‐     3280532        tkyte@TKYTE816> select *    2    from t    3   where x = y;        no rows selected        tkyte@TKYTE816>  tkyte@TKYTE816> select value‐&old_value  REDO_GENERATED from redo_size;  old   1: select value‐&old_value  REDO_GENERATED from redo_size  new   1: select value‐   3280532  REDO_GENERATED from redo_size        REDO_GENERATED  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐               0        Commit complete.  Expert one-on-one Oracle 229 we see that no redo is generated ‐ the blocks are all clean.  If we were to re‐run the above example with block buffers set to 6,000 instead of 300, weʹll  find that we generate no redo on any of the SELECTs ‐ we will have dirtied no blocks  during either of our SELECT statements. This is because the 499 blocks we modified fit  comfortably into 10 percent of our block buffer cache, and we are the only user. There is  no one else is mucking around with the data, and no one else is causing our data to be  flushed to disk or accessing those blocks. In a live system, it will be normal for at least  some of the blocks to not be cleaned out sometimes.  Where this behavior will most affect you, is after a large INSERT (as demonstrated above),  UPDATE, or DELETE ‐ one that affects many blocks in the database (anything more than  10 percent of the size of the cache will definitely do it). You will notice that the first query  to touch the block after this will generate a little redo and dirty the block, possibly causing  it to be rewritten if DBWR had already flushed it, or the instance had been shutdown,  clearing out the buffer cache all together. There is not too much you can do about it. It is  normal and to be expected. If Oracle did not do this deferred cleanout of a block, a  COMMIT could take as long to process as the transaction itself. The COMMIT would have  to revisit each and every block, possibly reading them in from disk again (they could have  been flushed). If you are not aware of block cleanouts and how they work, it will be one of  those mysterious things that just seem to happen for no reason. For example, you  UPDATE a lot of data and COMMIT. Now you run a query against that data to verify the  results. The query appears to generate tons of write I/O and redo. Seems impossible if you  are unaware of this ‐ it was to me the first time I saw it. You go and get someone to  observe this with you but it is not reproducible, as the blocks are now ʹcleanʹ on the second  query. You simply write it off as one of those ʹmysteriesʹ.  In an OLTP system, you will probably never see this happening. All of the transactions are  short and sweet. Modify a couple of blocks and they all get cleaned out. In a warehouse  where you make massive UPDATEs to the data after a load, block cleanouts may be a  factor in your design. Some operations will create data on ʹcleanʹ blocks. For example,  CREATE TABLE AS SELECT, direct path loaded data, direct path inserted data; they will  all create ʹcleanʹ blocks. An UPDATE, normal INSERT, or DELETE, may create blocks that  need to be cleaned with the first read. This could really affect you if your processing  consists of:   • Bulk loading lots of new data into the data warehouse.   • Running UPDATEs on all of the data you just loaded (producing blocks that need  to be cleaned out).   • Letting people query the data.   You will have to realize that the first query to touch the data will incur some additional  processing if the block needs to be cleaned. Realizing this, you yourself should ʹtouchʹ the  Expert one-on-one Oracle 230 data after the UPDATE. You just loaded or modified a ton of data, you need to analyze it  at the very least. Perhaps you need to run some reports yourself to validate the load. This  will clean the block out, and make it so the next query doesnʹt have to do this. Better yet,  since you just bulk loaded the data, you now need to refresh the statistics anyway.  Running the ANALYZE command to update statistics will clean out all of the blocks as  well.  Log Contention  This, like the Cannot Allocate New Log message, is something the DBA must fix, typically  in conjunction with the system administrator. However, it is something you might detect if  they arenʹt watching close enough. When we discuss the important V$ dynamic  performance views in Chapter 10 on Tuning Strategies and Tools, we will look at how to see  what exactly we are waiting on. Many times, the biggest wait event will be ʹlog file syncʹ. If  it is, you are experiencing contention on the redo logs; they are not going fast enough. This  can happen for many reasons. One application reason (one that the DBA cannot fix, but  which the developer must fix) is that you are committing too frequently ‐ committing  inside of a loop doing INSERTs for example. Here, you have introduced the COMMIT in  the misguided hope of reducing your need for resources. Committing too frequently, aside  from being a bad programming practice, is a surefire way to introduce lots of log file waits,  as we must wait for LGWR to flush our redo log buffers to disk. Normally, LGWR can do  this in the background and we donʹt have to wait. When we COMMIT more often than we  should (or have to), we wait more than we should. Assuming all of your transactions are  correctly sized (you are not committing more frequently than your business rules dictate),  the most common causes for log file waits that Iʹve seen are:  • Putting redo on a slow device. The disks are just poor performing disks. It is time to  buy faster disks.   • Putting redo on the same device as other files. Redo is designed to be written with  large sequential writes and to be on dedicated devices. If other components of your  system, even other Oracle components, are attempting to read and write to this  device at the same time as LGWR, you will experience some degree of contention.  Here, you want to ensure LGWR has exclusive access to them.   • Mounting the devices in a buffered manner. Here, you are using a ʹcookedʹ file  system (not RAW disks). The operating system is buffering the data, the database is  also buffering the data (redo log buffer). Double buffering slows things down. If  possible, mount the devices in a ʹdirectʹ fashion. How to do this varies by operating  system and device, but it is usually possible.   • Putting redo on a slow technology, such as RAID‐5. RAID‐5 is great for reads, but it  is terrible for writes. As we saw earlier on what happens during a COMMIT, we  must wait for LGWR to ensure the data is on disk. Using any technology that slows  this down will not be a good idea.   Expert one-on-one Oracle 231 If at all possible, you really want at least five dedicated devices for logging, and optimally  six in order to mirror your archives as well. In the days of 9, 20, 36 GB, and larger disks,  this is getting harder, but if you can set aside four of the smallest, fastest disks you can  find and one or two big ones, you can affect LGWR and ARCH in a positive fashion. To  lay out the disks, we would break them into three groups:   • Redo Group 1 ‐ Disks 1 and 3   • Redo Group 2 ‐ Disks 2 and 4   • Archive ‐ Disk 5 and optionally disk 6 (the big disk)    You would place redo log group 1 with members A and B onto Group 1. You would place  redo log group 2 with members C and D onto Group 2. If you have groups 3, 4, and so on,  theyʹll go onto the odd and even groups of disks. The effect of this is that LGWR, when  using group 1, will write to disk 1 and disk 3 simultaneously. When this group fills up,  LGWR will move to disks 2 and 4. When they fill up it will go back to disks 1 and 3.  Meanwhile, ARCH will be processing the full online redo logs, and writing them to Group  3, the big disk. The net effect is neither ARCH nor LGWR is ever reading a disk being  written to, or writing to a disk being read from ‐ there is no contention:    So, when LGWR is writing Group 1, ARCH is reading Group 2, and writing to the archive  disks. When LGWR is writing Group 2, ARCH is reading Group 1, and writing to the  archive disks. In this fashion, each of LGWR and ARCH will have their own dedicated  devices, and will not be contending with anyone, not even each other.  Expert one-on-one Oracle 232 Log files are the one set of Oracle files that benefit the most from the use of RAW disks. If  there is one set of files you might consider for RAW, log files would be the ones. There is  much back and forth discussion on the pros and cons of using RAW versus cooked file  systems. As this is not a book on DBA/SA tasks, we wonʹt get into them. Iʹll just mention  that if you are going to use them anywhere, log files would be the best candidates. You  never backup online redo log files, so the fact that they are on RAW partitions versus a  cooked file system wonʹt impact any backup scripts you might have. ARCH will always  turn the RAW logs into cooked file system files (you cannot use a RAW device to archive  to), hence the ʹmystiqueʹ of RAW devices is very much minimized in this case.   Temporary Tables and Redo/Rollback  Temporary tables are a new feature of Oracle 8.1.5. As such, there is an amount of  confusion surrounding them, in particular in the area of logging. In Chapter 6 on the  different types of database Tables available to you, we will cover how and why you might  use temporary tables. In this section, we will cover only the question of, ʹHow do  temporary tables work with respect to logging?ʹ  Temporary tables generate no redo for their blocks. Therefore, an operation on a  temporary table is not ʹrecoverableʹ. When you modify a block in a temporary table, no  record of this change will be made in the redo log files. However, temporary tables do  generate rollback, and the rollback is logged. Hence, temporary tables will generate some  redo. At first glance, it doesnʹt seem to make total sense: why would they need to generate  rollback? This is because you can rollback to a savepoint within a transaction. You might  erase the last 50 INSERTs into a temporary table, but not the first 50. Temporary tables can  have constraints and everything else a normal table can have. They might fail a statement  on the 500th row of a 500 row INSERT, necessitating a rollback of that statement. Since  temporary tables behave in general just like a ʹnormalʹ table, temporary tables must  generate rollback. Since rollback data must be logged, they will generate some redo log for  the rollback they generate.  This is not nearly as ominous as it seems. The primary SQL statements used against  temporary tables are INSERTs and SELECTs. Fortunately, INSERTs generate very little  rollback (you need to restore the block to ʹnothingʹ, and it doesnʹt take very much room to  store ʹnothingʹ), and SELECTs generate no rollback. Hence, if you use temporary tables for  INSERTs and SELECTs exclusively, this section means nothing to you. It is only if you  UPDATE or DELETE you might be concerned about this.  I set up a small test to demonstrate the amount of redo generated, an indication therefore,  of the amount of rollback generated for temporary tables, since only the rollback is logged  for them. In order to do this we will take an identically configured ʹpermanentʹ table and  ʹtemporaryʹ table, and then perform the same operations on them, measuring the amount  of redo generated each time. The tables I used were simply:  Expert one-on-one Oracle 233 tkyte@TKYTE816> create table perm    2  ( x char(2000) default ʹxʹ,    3    y char(2000) default ʹyʹ,    4    z char(2000) default ʹzʹ )    5  /        Table created.        tkyte@TKYTE816>  tkyte@TKYTE816>  tkyte@TKYTE816> create global temporary table temp    2  ( x char(2000) default ʹxʹ,    3    y char(2000) default ʹyʹ,    4    z char(2000) default ʹzʹ )    5  on commit preserve rows    6  /        Table created.  I set up a small stored procedure to do some SQL on the table, and report the results:   tkyte@TKYTE816> create or replace procedure do_sql( p_sql in varchar2 )    2  as    3      l_start_redo    number;    4      l_redo            number;    5  begin    6  select value into l_start_redo from redo_size;    7    8      execute immediate p_sql;    9      commit;   10   11      select value‐l_start_redo into l_redo from redo_size;   12   13      dbms_output.put_line   14      ( to_char(l_redo,ʹ9,999,999ʹ) ||ʹ bytes of redo generated for ʺʹ ||   15        substr( replace( p_sql, chr(10), ʹ ʹ), 1, 25 ) || ʹʺ...ʹ );   16  end;   17  /        Procedure created.  Then, I ran equivalent INSERTs, UPDATEs, and DELETEs against them:   Expert one-on-one Oracle 234 tkyte@TKYTE816> set serveroutput on format wrapped  tkyte@TKYTE816> begin    2      do_sql( ʹinsert into perm    3               select 1,1,1    4                 from all_objects    5                where rownum <= 500ʹ );    6    7      do_sql( ʹinsert into temp    8               select 1,1,1    9                 from all_objects   10                where rownum <= 500ʹ );   11   12      do_sql( ʹupdate perm set x = 2ʹ );   13      do_sql( ʹupdate temp set x = 2ʹ );   14   15      do_sql( ʹdelete from permʹ );   16      do_sql( ʹdelete from tempʹ );   17  end;   18  /   3,238,688 bytes of redo generated for ʺinsert into permʺ...      72,572 bytes of redo generated for ʺinsert into tempʺ...   2,166,376 bytes of redo generated for ʺupdate perm set x = 2ʺ...   1,090,336 bytes of redo generated for ʺupdate temp set x = 2ʺ...   3,320,244 bytes of redo generated for ʺdelete from permʺ...   3,198,236 bytes of redo generated for ʺdelete from tempʺ...        PL/SQL procedure successfully completed.  As you can see:  • The INSERT into the ʹrealʹ table generated a lot of redo. Almost no redo was  generated for the temporary table. This makes sense ‐ there is very little rollback  data generated for INSERTs and only rollback data is logged for temporary tables.   • The UPDATE of the real table generated about twice the amount of redo as the  temporary table. Again, this makes sense. About half of that UPDATE, the ʹbefore  imageʹ, had to be saved. The ʹafter imageʹ (redo) for the temporary table did not  have to be saved.   • The DELETEs took about the same amount of redo space. This makes sense as the  rollback for a DELETE is big, but the redo for the modified blocks is very small.  Hence, a DELETE against a temporary table takes place very much in the same  fashion as a DELETE against a permanent table.   The rules of thumb therefore are the following:  Expert one-on-one Oracle 235 • An INSERT will generate little to no rollback/redo activity.   • A DELETE will generate the same amount of redo as a normal table.   • An UPDATE of a temporary table will generate about half the redo of an UPDATE  of a normal table.   There are notable exceptions to the last rule of thumb. For example, if I UPDATE a column  that is entirely Null with 2000 bytes of data, there will be very little rollback data  generated. This UPDATE will behave like the INSERT. On the other hand, if I UPDATE a  column with 2000 bytes of data to be Null, it will behave like the DELETE as far as redo  generation is concerned. On average, you can expect an UPDATE against a temporary  table to produce about 50 percent of the rollback/redo you would experience with a real  table.  In general, common sense prevails on the amount of redo created. If the operation you  perform causes rollback data to be created, then determine how easy or hard it will be to  reverse (undo) the effect of your operation. If you INSERT 2000 bytes, the reverse of this is  easy. You simply go back to no bytes. If you DELETE 2000 bytes, the reverse is INSERTing  2000 bytes. In this case, the redo is substantial.  Armed with this knowledge, you will avoid deleting from temporary tables. You can use  TRUNCATE, or just let them empty themselves automatically after a COMMIT or when  your session terminated. All of these methods generate no rollback and therefore, no redo.  You will try to avoid updating a temporary table unless you really have to for the same  reason. You will use temporary tables mostly as something to be INSERTed into and  SELECTed from. In this fashion, youʹll make optimum use of their unique ability to not  generate redo.  Analyzing Redo  Lastly in the area of redo log, is a question that is asked frequently, ʹHow do you analyze  these files?ʹ In the past, prior to Oracle 8i, it was very difficult. You could call Oracle  Support and get the magic command to ʹdumpʹ a redo log file. This would create a textual  report that you could read and, if you really understood lots of internal, undocumented  stuff about Oracle, you might be able to make sense of them. Practically speaking, this was  not feasible, especially if you want to analyze the redo log files to find out when a table  was dropped, or what transaction set the SALARY column to a negative value, and so on.  Enter LogMiner; which is a new package, DBMS_LOGMNR, supplied with Oracle 8i that  allows you to load your redo log files into a database V$ table, and query them using SQL.  You can see the before and after values of each column affected by a DML statement. You  can see the SQL that would effectively ʹreplayʹ your transaction or ʹundoʹ your transaction.  You can look in there to find the DELETE against OBJ$ (a SYS‐owned data dictionary table)  Expert one-on-one Oracle 236 to see when your table was dropped ʹaccidentallyʹ, in the hopes that your DBA can reverse  to a point in time right before this DROP TABLE and recover it.  For now, Iʹm just going to mention Log Miner. In Appendix A on Necessary Supplied  Packages, I go into some depth on the DBMS_LOGMNR package. Refer to the section at the  back of this book for more details in this area.  Rollback  Weʹve already discussed a lot of rollback segment topics. Weʹve seen how they are used  during recovery, how they interact with the redo logs, and that they are used for  consistent, non‐blocking reads of data. In this section, I would like to cover the most  frequently raised issues with rollback segments. The bulk of our time will be spent on the  infamous ORA‐01555: snapshot too old error as this single issue causes more confusion  than any other topic in the entire database set of topics. Before we do this, weʹll investigate  two other rollback‐related issues. First, we will address the question of what generates the  most/least undo (you might already be able to answer that yourself given the preceding  examples with temporary tables). Then, we will look at using the SET TRANSACTION  statement to pick a rollback segment, and discuss why we might want to use it. Then, weʹll  finish up with a detailed look at the mysterious ORA‐01555.   What Generates the Most/Least Undo?  This is a frequently asked, but easily answered question. An INSERT will generate the  least amount of undo, since all Oracle needs to record for this is a row ID to ʹdeleteʹ. An  UPDATE is typically second in the race (in most cases). All that needs to be recorded are  the changed bytes. It is most common that you UPDATE some small fraction of the entire  rowʹs data. Therefore, a small fraction of the row must be remembered in the undo. Many  of my examples above run counter to this rule of thumb, but that is because they update  large, fixed sized rows, and update the entire row. It is much more common to UPDATE a  row and change a small percentage of the total row. A DELETE will in general, generate  the most undo. For a DELETE, Oracle must record the entire rowʹs before image into the  undo segment. The temporary table example, with regards to redo generation above, is a  classic example of this. The INSERT generated very little undo that needed to be logged.  The UPDATE generated an amount equal to the before image of the data that was changed,  and the DELETE generates the entire set of data written into the rollback segment.  SET TRANSACTION  The SET TRANSACTION SQL statement may be used to ʹpickʹ the rollback segment you  would like your transaction to use. This is generally used in order to have a really big  rollback segment for some large operation. I am generally not a big fan of this practice,  especially when it is overused. For some infrequent mass updates, this might be an OK  Expert one-on-one Oracle 237 thing to do on a one‐time basis. It is my opinion however, that you should have equi‐sized  rollback segments (all rollback segments are the same size), and you let the system pick  and choose the one you are going to use for a given transaction. If you use rollback  segments that can grow (MAXEXTENTS is high enough) and, if need be, have an optimal  setting so they shrink back to some size after extending a large amount, you do not need to  have a special ʹbig oneʹ.  One of the problems with one big rollback segment, is that there is nothing to stop any  other transaction from using it. There is nothing that you can realistically do to make a  rollback segment belong to a single transaction. You are going to have others working in  your space. It is just much more straightforward to let the system pick a rollback segment  and let it grow. It also gets to be an issue if the tool you are using does not allow you to  choose a rollback segment, for example IMP (import) does not, a snapshot refresh on the  other hand does, SQLLDR does not, and so on. I believe strongly that your rollback  segments should be sized for the transactions your system performs, and any rollback  segment should be big enough.  That being said, there are infrequent, one‐time uses for this statement. If you have to  perform some sort of mass update of lots of information, this might be a good use of the  feature. You would create a temporary rollback segment on some scratch disks and do  your update. After the update is done, you would offline, and drop the rollback segment.  Perhaps a better alternative to this even, would be to have the really large table partitioned  in the first place, and perform a parallel update. In this case, each of the parallel query  slaves will be assigned to their own rollback segment, making it possible for your  transaction to, in effect, use all of the available rollback segments simultaneously. This is  something you cannot do with a serially executed update.  ʹORA‐01555: snapshot too oldʹ  The ORA‐01555 is one of those errors that confound people. It is the foundation for many  myths, inaccuracies, and suppositions. The error is actually straightforward and has only  two real causes, but since there is a special case of one of them that happens so frequently,  Iʹll say that there are three. They are:  • The rollback segments are too small for the work you perform on your system.   • Your programs fetch across COMMITs (actually a variation on the above).   • Block cleanout.   Points one and two above are directly related to Oracleʹs read consistency model. As you  recall from Chapter 2, Architecture, the results of your query are ʹpre‐ordainedʹ ‐ they are  well‐defined before Oracle goes to retrieve even the first row. Oracle provides this  consistent point in time ʹsnapshotʹ of the database by using the rollback segments to roll  Expert one-on-one Oracle 238 back blocks that have changed since your query began. Every statement you execute such  as:  update t set x = 5 where x = 2;        insert into t select * from t where x = 2;        delete from t where x = 2;        select * from t where x = 2;  will see a read consistent view of T and the set of rows where x=2, regardless of any other  concurrent activity in the database. All statements that ʹreadʹ the table take advantage of  this read consistency. In the above, the UPDATE reads the table to find rows where x=2  (and then UPDATEs them). The INSERT reads the table to find where x=2, and then  INSERTs them, and so on. It is this dual use of the rollback segments, both to roll back  failed transactions, and to provide for read consistency that results in the ORA‐01555.  The third item above is a more insidious cause of the ORA‐01555, in that it can happen in a  database where there is a single session, and this session is not modifying the table that  raises the error! This doesnʹt seem possible; why would we need rollback data for a table  we can guarantee is not being modified? Weʹll find out below.  Before we take a look at all three cases with illustrations, Iʹd like to share with you the  solutions to the ORA‐1555. In general they are:  • Analyze related objects. This will help avoid the third bullet point above. Since the  block cleanout is the result of a very large mass UPDATE or INSERT, this needs to  be done anyway after a mass UPDATE or large load.   • Increase or add more rollback segments. This decreases the likelihood of rollback  data being overwritten during the course of your long running query. This goes  towards solving all three of the above points.   • Reduce the run time of your query (tune it). This is always a good thing if possible,  so it might be the first thing you try. This will reduce the need for larger rollback  segments. This goes towards solving all three of the above points.   Weʹll come back to these again below, as they are important facts to know. It seemed  appropriate to display them prominently before we begin.  Expert one-on-one Oracle 239 Rollback Segments Are in Fact Too Small  The scenario is this: you have a system where the transactions are small. As a result of this,  you need very little rollback segment space allocated. Say, for example, the following is  true:  • Each transaction generates 1 KB of undo on average.   • You do five of these transactions per second on average (5 KB of undo per second,  300 KB per minute).   • You have a transaction that generates 1 MB of undo that occurs once per minute on  average. In total, you generate about 1.3 MB of undo per minute.   • You have 5 MB of rollback configured for the system.   That is more than sufficient undo for this database when processing transactions. The  rollback segments will wrap around, and reuse space about every three to four minutes or  so, on average. If we were to size rollback segments based on our transactions that do  modifications, we did alright.  In this same environment however, you have some reporting needs. Some of these queries  take a really long time to run ‐ five minutes, perhaps. Here is where the problem comes in.  If these queries take five minutes to execute and they need a view of the data as it existed  when the query began, we have a very good probability of the ORA‐01555 error occurring.  Since our rollback segments will wrap during this query execution, we know that some  rollback information generated since our query began is gone ‐ it has been overwritten. If  we hit a block that was modified near the time we started our query, the undo information  for this block will be missing, and we will receive the ORA‐01555.  Here is a small example. Letʹs say we have a table with blocks 1, 2, 3, ... 1,000,000 in it. The  following is a serial list of events that could occur:  Time(min:secs)  Action  0:00  Our query begins.  0:01  Another session UPDATEs block 1,000,000. Rollback information  for this is recorded into some rollback segment.   0:01  This UPDATE session COMMITs. The rollback data it generated  is still there, but is now subject to being overwritten if we need  the space.   1:00  Our query is still chugging along. It is at block 200,000.  1:01  Lots of activity going on, we have generated a little over 1.3 MB  of rollback by now.  Expert one-on-one Oracle 240 Time(min:secs)  Action  3:00  Our query is still going strong. We are at block 600,000 or so by  now.  4:00  Our rollback segments start to wrap around and reuse the space  that was active when our query began at time 0:00. Specifically,  we have just reused the rollback segment space that the UPDATE  to block 1,000,000 used back at time 0:01.   5:00  Our query finally gets to block 1,000,000. It finds it has been  modified since the query began. It goes to the rollback segment  and attempts to find the undo for that block to get a consistent  read on it. At this point, it discovers the information it needs no  longer exists. ORA‐01555 is raised and the query fails.   This is all it takes. If your rollback segments are sized such that they have a good chance of  wrapping around during the execution of your queries, and your queries access data that  will probably be modified, you stand a very good chance of hitting the ORA‐01555 on a  recurring basis. It is at this point you must resize your rollback segments and make them  larger (or have more of them). You need enough rollback configured to last as long as your  long running queries. The system was sized for the transactions that modify data ‐ and we  forgot to size for the other components of the system. This is where one of the points of  confusion comes into play. People will say, ʹWell, we have X MB of rollback configured  but they can grow ‐ we have MAXEXTENTS set at 500 and each extent is 1 MB, so the  rollback can get quite large.ʹ The problem is that the rollback segments will never grow  due to a query, only due to INSERTs, UPDATEs, and DELETEs. The fact that a long  running query is executing does not cause Oracle to grow a rollback segment to retain the  data in case it might need it. Only a long running UPDATE transaction would do this. In  the above example, even if the rollback segments had the potential to grow, they will not.  What you need to do for this system is have rollback segments that are already big. You  need to permanently allocate space to the rollback segments, not give them the  opportunity to grow on their own.   The only solutions to the above problem are to either make it so that the rollback segments  are sized so they do not wrap but every six to ten minutes, or make it so your queries  never take more than two to three minutes to execute. The first suggestion is based on the  fact that we have queries that take five minutes to execute. In this case, the DBA needs to  make the amount of permanently allocated rollback two to three times larger. The second  suggestion, a perfectly valid suggestion, is equally appropriate. Any time you can make  the queries go faster, you should. If the rollback generated since the time your query  began is never overwritten, you will avoid the ORA‐01555.  The important thing to remember is that the probability of an ORA‐01555 is dictated by  the smallest rollback segment in your system, not the largest, not the average. Adding one  Expert one-on-one Oracle 241 ʹbigʹ rollback segment will not make this problem go away. All it takes is for the smallest  rollback segment to wrap around while a query is processing, and then this query stands a  chance of an ORA‐01555. This is why I am a big fan of equi‐sized rollback segments. In  this fashion, each rollback segment is both the smallest and the largest. This is also why I  avoid using ʹoptimallyʹ sized rollback segments. If you shrink a rollback segment that was  forced to grow, you are throwing away a lot of undo that may be needed right after that. It  discards the oldest rollback data when it does this, minimizing the risk but still, the risk is  there. I prefer to manually shrink rollback segments during off peak time if at all. I am  getting a little too deep into the DBA role at this point, so weʹll be moving on to the next  case. It is just important that you understand the ORA‐01555 in this case, is due to the  system not being sized correctly for your workload. The only solutions are to size correctly  for your workload. It is not your fault, but it is your problem since you hit it. It is the same  as if you run out of temporary space during a query. You either configure sufficient  temporary space for the system, or you rewrite the queries so they use a plan that does not  require temporary space.  In order to see this effect for yourself, we can set up a small, but somewhat artificial test.  What I will do below is create a very small rollback segment. Weʹll have one session that  will use only this rollback segment, virtually assuring us that it will wrap around and  reuse its allocated space many times. The session that uses this rollback segment will be  modifying a table T. It will use a full scan of T, and read it from ʹtopʹ to ʹbottomʹ. In another  session, we will execute a query that will read the table T via an index. In this fashion, it  will read the table somewhat randomly. It will read row 1, then row 1000, then row 500,  then row 20,001, and so on. In this way, we will tend to visit blocks very randomly, and  perhaps many times during the processing of our query. The odds of getting an ORA‐ 01555 in this case are virtually 100 percent. So, in one session we start with:   tkyte@TKYTE816> create rollback segment rbs_small    2  storage    3  ( initial 8k next 8k    4    minextents 2 maxextents 3 )    5  tablespace rbs_test    6  /        Rollback segment created.        tkyte@TKYTE816> alter rollback segment rbs_small online;        Rollback segment altered.        tkyte@TKYTE816> create table t    2  as    3  select *  Expert one-on-one Oracle 242   4    from all_objects    5  /        Table created.        tkyte@TKYTE816> create index t_idx on t(object_id)    2  /  Index created.        tkyte@TKYTE816> begin    2          for x in ( select rowid rid from t )    3          loop    4                  commit;    5                  set transaction use rollback segment rbs_small;    6                  update t    7                     set object_name = lower(object_name)    8                   where rowid = x.rid;    9          end loop;   10          commit;   11  end;   12  /  Now, in another session, while this PL/SQL block is executing, we issue:  tkyte@TKYTE816> select object_name from t where object_id > 0 order by object_id;        OBJECT_NAME  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  i_obj#  tab$  ...  /91196853_activationactivation  /91196853_activationactivation  ERROR:  ORA‐01555: snapshot too old: rollback segment number 10 with name ʺRBS_SMALLʺ  too  small        3150 rows selected.        tkyte@TKYTE816> select count(*) from t;          COUNT(*)  Expert one-on-one Oracle 243 ‐‐‐‐‐‐‐‐‐‐       21773  As you can see, it took a little while, but after reading about three thousand rows (about  one seventh of the data) randomly, we eventually hit the ORA‐01555. In this case, it was  purely due to the fact that we read the table T via the index, and performed random reads  all over the table. If we had full scanned the table instead, there is a good chance we would  not get the ORA‐01555 in this particular case. (Try it: change the SELECT query to be  SELECT /*+ FULL(T) */ ... and see what happens. On my system, this query did not get the  ORA‐1555 in repeated runs). This is because both the SELECT and UPDATE would have  been full scanning T, and the SELECT would most likely race ahead of the UPDATE  during its scan (the SELECT just has to read, the UPDATE must read and update,  therefore itʹll go slower). By doing the random reads, we increase the probability that the  SELECT will need to read a block, which the UPDATE modified and committed many  rows ago. This just demonstrates the somewhat insidious nature of the ORA‐01555. Itʹs  occurrence depends on how concurrent sessions access, and manipulate the underlying  tables.   You Fetch Across COMMITs  This is simply a variation on the theme. It is the same case as above, but you are doing it to  yourself. You need no help from another session. Weʹve already investigated this in  Chapter 4 on Transactions, and will briefly review it again. The bottom line is this; fetching  across COMMITs is a surefire way to get the ORA‐01555. My observation is that most  occurrences of the ORA‐01555 are because of this operation. The amusing thing is that  people sometimes react to this error by committing even more frequently since the  message says rollback segment too small. The thinking is that this will help the problem  (that our modification must be using too much rollback is the incorrect conclusion here),  when in fact it will only ensure that it happens even faster.  The scenario is that you have to update lots of information. You are hesitant to configure  the system with sufficient rollback space for whatever reasons. So, you decide to COMMIT  every X rows to ʹsaveʹ on rollback. Never mind that this is both probably slower, and in  the end, generates more undo and redo, it is also the surefire way to get into the ORA‐ 01555. Continuing with the above example, I can easily demonstrate this. Using the same  table T from above, simply execute:   tkyte@TKYTE816> declare    2          l_cnt number default 0;    3  begin    4          for x in ( select rowid rid, t.* from t where object_id > 0 )    5          loop  Expert one-on-one Oracle 244   6                  if ( mod(l_cnt,100) = 0 )    7                  then    8                          commit;    9                          set transaction use rollback segment rbs_small;   10                  end if;   11                  update t   12                     set object_name = lower(object_name)   13                   where rowid = x.rid;   14                  l_cnt := l_cnt + 1;   15          end loop;   16      commit;   17  end;   18  /  declare  *  ERROR at line 1:  ORA‐01555: snapshot too old: rollback segment number 10 with name ʺRBS_SMALLʺ  too  small  ORA‐06512: at line 4  Here, we are performing a random read on the table T via the index. We UPDATE a single  row at a time in the loop. Every 100 rows of UPDATEs, we COMMIT. At some point in  time, we will revisit a block with our query that we modified with our UPDATE, and that  block will no longer be recoverable from the rollback segments (since we overwrote that  data long ago). Now we are in the uncomfortable position of having our UPDATE process  fail partway through.  We could, as outlined in Chapter 4 on Transactions, come up with a more sophisticated  method of updating the data. For example, we could find the minimum OBJECT_ID and  the maximum. We could divide this up into ranges of 100 and do the UPDATEs, recording  in another table what we had successfully updated so far. This makes the process, which  should be a single statement and a single transaction, many individual transactions  implemented in complex procedural code that could be restarted from a failure. For  example:   tkyte@TKYTE816> create table done( object_id int );        Table created.        tkyte@TKYTE816> insert into done values ( 0 );        1 row created.  Expert one-on-one Oracle 245       tkyte@TKYTE816> declare    2          l_cnt number;    3          l_max number;    4  begin    5          select object_id into l_cnt from done;    6          select max(object_id) into l_max from t;    7    8          while ( l_cnt < l_max )    9          loop   10                  update t   11                     set object_name = lower(object_name)   12                   where object_id > l_cnt   13                     and object_id <= l_cnt+100;   14   15                  update done set object_id = object_id+100;   16   17                  commit;   18                  set transaction use rollback segment rbs_small;   19                  l_cnt := l_cnt + 100;   20          end loop;   21  end;   22  /        PL/SQL procedure successfully completed.  What we did here mostly, was to come up with a complex solution that has to be tested  and reviewed for accuracy, and will run much slower than the simple:  update t set object_name = lower(object_name) where object_id > 0;  The simple, and in my opinion correct, solution to this dilemma, is to configure an  appropriate amount of rollback space for your system, and use the single UPDATE  statement. If you have the very occasional large update, use a ʹscratchʹ rollback segment  that you create just for the large process, and drop it afterwards. It is so much easier and  less error‐prone than coming up with a complex solution that might fail, simply due to its  complexity (programming error). It is so much easier to let the system do what it needs to  do rather than try to come up with complex workarounds to ʹsave spaceʹ.  Expert one-on-one Oracle 246 Delayed Block Cleanout  This cause of the ORA‐01555 is harder to eliminate entirely, but is rare, as the  circumstances under which it occurs do not happen frequently (at least not in Oracle 8i  anymore). We have already discussed the block cleanout mechanism, but to summarize, it  is the process whereby the next session to access a block after it has been modified, may  have to check to see if the transaction that last modified the block is still active. Once it  determines that it is not active, it cleans out the block so that the next session to access it  does not have to go through the same process again. In order to clean out the block, Oracle  determines the rollback segment used for the previous transaction (from the blocks  header), and then determines whether the rollback header indicates whether it has been  committed or not. This confirmation is accomplished in one of two ways. One way is that  Oracle can determine that the transaction committed a long time ago, even though itʹs  transaction slot has been overwritten in the rollback segment transaction table. The other  way is that the COMMIT SCN is still in the transaction table of the rollback segment,  meaning the transaction committed a short period of time ago, and itʹs transaction slot  hasnʹt been overwritten.   In order to receive the ORA‐01555 from a delayed block cleanout, all of the following  conditions must be met:  • A modification is made and COMMITed, and the blocks are not cleaned out  automatically (for example, it modified more blocks than can be fitted in 10 percent  of the SGA block buffer cache).   • These blocks are not touched by another session, and will not be touched until our  unfortunate query below hits it.   • A ʹlong runningʹ query begins. This query will ultimately read some of those blocks  from above. This query starts at SCN t1. This is the read consistent SCN it must roll  data back to, in order to achieve read consistency. The transaction entry for the  modification transaction is still in the rollback segment transaction table when we  began.   • During the query, many commits are made in the system. These transactions do not  touch the blocks in question (if they did, then we wouldnʹt have the impending  problem).   • The transaction tables in the rollback segments roll around and reuse slots due to  the high degree of COMMITs. Most importantly, the transaction entry for the  original modification transaction is cycled over and reused. In addition, the system  has reused rollback segment extents, so as to prevent a consistent read on the  rollback segment header block itself.   • Additionally, the lowest SCN recorded in the rollback segment now exceeds t1 (it is  higher than the read consistent SCN of the query), due to the large amount of  commits.   Expert one-on-one Oracle 247 Now, when our query gets to the block that was modified and committed before it began,  it is in trouble. Normally, it would go to the rollback segment pointed to by the block and  find the status of the transaction that modified it (in other words, find the COMMIT SCN  of that transaction). If the COMMIT SCN is less than t1, our query can use this block. If the  COMMIT SCN is greater tha t1, our query must roll back that block. The problem is  however, that our query is unable to determine in this particular case if the COMMIT SCN  of the block is greater than or less than t1. It is unsure as to whether it can use it or not. The  ORA‐01555 then results.  We can force this error to occur artificially with a single session, but it is more impressive  if we use two sessions, as that will drive home the point that this error is not caused by  fetching across COMMITs. Weʹll show both examples as they are both small, and very  similar.  What we will do is create many blocks in a table that need to be cleaned out. We will then  open a cursor on that table and, inside of a tight loop, initiate many transactions. I am  making all modifications to go into the same rollback segment to cause this error to occur  in a predicable fashion. Eventually, the ORA‐01555 will occur as the outer query in the  loop (SELECT * FROM T) hits this block cleanout issue, due to the small UPDATE inside  the innermost loop modifying and committing frequently:   tkyte@TKYTE816> create table small( x int );        Table created.        tkyte@TKYTE816> insert into small values ( 0 );        1 row created.        tkyte@TKYTE816> begin    2      commit;    3      set transaction use rollback segment rbs_small;    4      update t    5         set object_type = lower(object_type);    6      commit;    7    8          for x in ( select * from t )    9          loop   10                  for i in 1 .. 20   11                  loop   12                          update small set x = x+1;   13                          commit;   14                          set transaction use rollback segment rbs_small;  Expert one-on-one Oracle 248  15                  end loop;   16          end loop;   17  end;   18  /  begin  *  ERROR at line 1:  ORA‐01555: snapshot too old: rollback segment number 10 with name ʺRBS_SMALLʺ  too  small  ORA‐06512: at line 8        tkyte@TKYTE816> select * from small;                 X  ‐‐‐‐‐‐‐‐‐‐      196900  In order to ʹachieveʹ the above error, I used the same table T as before with some 22,000  rows in it (about 300 blocks on my system). I had a block buffer cache of 300 blocks (10  percent of that is 30). The UPDATE on line 4 should have left about 270 blocks in need of a  block cleanout. We then proceeded to SELECT * from this table, where lots of blocks were  in need of cleanout. For every row we fetched, we performed 20 transactions, and I made  sure that these transactions were directed to the same rollback segment that did the  UPDATE in the first place (the goal after all is to overwrite that transaction slot). After  processing about 10,000 rows in our query from T (as evidenced by the value in SMALL),  we hit the ORA‐01555.  Now, to show this is not caused by the fetch across COMMIT (it looks like it could be since  we are fetching across a COMMIT), weʹll use two sessions. The first session weʹll start by  creating a table called STOP_OTHER_SESSION. Weʹll use this table to notify the other  session that generates lots of transactions, that it is time to stop. Then, it will dirty lots of  blocks in the table again like the above did, and start a long running query on this table.  The DBMS_LOCK.SLEEP is used to put a wait between each row fetched to make this  simple query a long running one. It simulates the think time that the work would be  performing on each row:   tkyte@TKYTE816> create table stop_other_session ( x int );        Table created.        tkyte@TKYTE816> declare    2          l_cnt number := 0;  Expert one-on-one Oracle 249   3  begin    4      commit;    5      set transaction use rollback segment rbs_small;    6      update t    7         set object_type = lower(object_type);    8      commit;    9   10      for x in ( select * from t )   11      loop   12          dbms_lock.sleep(1);   13      end loop;   14  end;  .15  /  Now, while the above code is executing, you would run the following in another session:  tkyte@TKYTE816> create table small( x int );        Table created.        tkyte@TKYTE816> insert into small values ( 0 );        1 row created.        tkyte@TKYTE816> begin    2          commit;    3          set transaction use rollback segment rbs_small;    4          for i in 1 .. 500000    5          loop    6                  update small set x = x+1;    7          commit;    8          set transaction use rollback segment rbs_small;    9          for x in ( select * from stop_other_session )   10          loop   11               return; ‐‐ stop when the other session tells us to   12          end loop;   13      end loop;   14  end;   15  /        PL/SQL procedure successfully completed.  After a while, the first session will report back with:  Expert one-on-one Oracle 250 declare  *  ERROR at line 1:  ORA‐01555: snapshot too old: rollback segment number 10 with name ʺRBS_SMALLʺ too  small  ORA‐06512: at line 10  This is the same error, but this time we did not fetch across a COMMIT. In fact, no one was  modifying any of the data we were reading.  As I said, the above is a rare case. It took a lot of conditions, that all must exist  simultaneously to occur. We needed blocks that were in need of a cleanout to exist and  these blocks are rare in Oracle8i (they used to occur more frequently in version 7.x and 8.0  releases, but are relatively rare in 8.1). An ANALYZE statement to collect statistics, gets rid  of them so the most common causes, large mass updates and bulk loads, should not be a  concern, since the tables need to be analyzed after such operations anyway. Most  transactions tend to touch less then 10 percent of the block the buffer cache and hence, do  not generate blocks that need to be cleaned out. In the event that you believe this issue is  encountered, whereby a SELECT against a table that has no other DML applied to it is  raising the ORA‐01555, the things to try are:   • Ensure you are using ʹright sizedʹ transactions in the first place. Make sure you are  not committing more frequently than you should.   • ANALYZE related objects. Since the block cleanout is the result of a very large mass  UPDATE or INSERT, this needs to be done anyway.   • Increase or add more rollback segments. This decreases the likelihood of a rollback  segment transaction table slot being overwritten during the course of your long  running query. This is the same as the solution for the other cause of an ORA‐01555  (in fact, the two are very much related; you are experiencing rollback segment reuse  during the processing of your query).   • Reduce the run‐time of your query (tune it). This is always a good thing if possible,  so it might be the first thing you try.   Summary  In this chapter, we have taken a look at redo and rollback, and what they mean to the  developer. What I have presented here is mostly things for you to be on the look out for,  since it is actually the DBAs or SAs who must correct these issues. The most important  things to take away from this chapter are the importance of redo and rollback, and the fact  that they are not overhead ‐ they are in fact integral components of the database, they are  necessary and mandatory. Once you have a good understanding of how they work, and  what they do, youʹll be able to make better use of them. Understanding that you are not  ʹsavingʹ anything by committing more frequently than you should (you are actually  Expert one-on-one Oracle 251 wasting resources, it takes more CPU, more disk, and more programming), is probably the  most important point. Understand what the database needs to do, and then let the  database do it.  Expert one-on-one Oracle 252 Chapter 6: Database Tables  Overview  In this chapter, we will discuss database tables. We will look at the various types of tables  and see when you might want to use each type; when one type of table is more  appropriate than another. We will be concentrating on the physical storage characteristics  of the tables; how the data is organized and stored.  Once upon a time, there was only one type of table really; a ʹnormalʹ table. It was managed  in the same way a ʹheapʹ is managed (the definition of which is below). Over time, Oracle  added more sophisticated types of tables. There are clustered tables (two types of those),  index organized tables, nested tables, temporary tables, and object tables in addition to the  heap organized table. Each type of table has different characteristics that make it suitable  for use in different application areas.  Types of Tables  We will define each type of table before getting into the details. There are seven major  types of tables in Oracle 8i. They are:  • Heap Organized Tables ‐ This is a ʹnormalʹ, standard database table. Data is  managed in a heap‐like fashion. As data is added, the first free space found in the  segment that can fit the data will be used. As data is removed from the table, it  allows space to become available for reuse by subsequent INSERTs and UPDATEs.  This is the origin of the name heap as it refers to tables like this. A heap is a bunch  of space and it is used in a somewhat random fashion.   • Index Organized Tables ‐ Here, a table is stored in an index structure. This imposes  physical order on the rows themselves. Whereas in a heap, the data is stuffed  wherever it might fit, in an index organized table the data is stored in sorted order,  according to the primary key.   • Clustered Tables ‐ Two things are achieved with these. First, many tables may be  stored physically joined together. Normally, one would expect data from only one  table to be found on a database block. With clustered tables, data from many tables  may be stored together on the same block. Secondly, all data that contains the same  cluster key value will be physically stored together. The data is ʹclusteredʹ around  the cluster key value. A cluster key is built using a B*Tree index.   • Hash Clustered Tables ‐ Similar to the clustered table above, but instead of using a  B*Tree index to locate the data by cluster key, the hash cluster hashes the key to the  cluster, to arrive at the database block the data should be on. In a hash cluster the  Expert one-on-one Oracle 253 data is the index (metaphorically speaking). This would be appropriate for data that  is read frequently via an equality comparison on the key.   • Nested Tables ‐ These are part of the Object Relational extensions to Oracle. They  are simply system generated and maintained child tables in a parent/child  relationship. They work much in the same way as EMP and DEPT in the SCOTT  schema. EMP is considered to be a child of the DEPT table, since the EMP table has  a foreign key, DEPTNO, that points to DEPT. The main difference is that they are  not ʹstandaloneʹ tables like EMP.   • Temporary Tables ‐ These tables store scratch data for the life of a transaction or  the life of a session. These tables allocate temporary extents as needed from the  users temporary tablespace. Each session will only see the extents it allocates and  never sees any of the data created in any other session.   • Object Tables ‐ These are tables that are created based on an object type. They are  have special attributes not associated with non‐object tables, such as a system  generated REF (object identifier) for each row. Object tables are really special cases  of heap, index organized, and temporary tables, and may include nested tables as  part of their structure as well.   In general, there are a couple of facts about tables, regardless of their type. Some of these  are:  • A table can have up to 1,000 columns, although I would recommend against a  design that does, unless there was some pressing need. Tables are most efficient  with far fewer than 1,000 columns.   • A table can have a virtually unlimited number of rows. Although you will hit other  limits that prevent this from happening. For example, a tablespace can have at most  1,022 files typically. Say you have 32 GB files, that is to say 32,704 GB per tablespace.  This would be 2,143,289,344 blocks, each of which are 16 KB in size. You might be  able to fit 160 rows of between 80 to 100 bytes per block. This would give you  342,926,295,040 rows. If we partition the table though, we can easily multiply this  by ten times or more. There are limits, but youʹll hit other practical limitations  before even coming close to these figures.   • A table can have as many indexes as there are permutations of columns, taken 32 at  a time (and permutations of functions on those columns), although once again  practical restrictions will limit the actual number of indexes you will create and  maintain.   • There is no limit to the number of tables you may have. Yet again, practical limits  will keep this number within reasonable bounds. You will not have millions of  tables (impracticable to create and manage), but thousands of tables, yes.   We will start with a look at some of the parameters and terminology relevant to tables and  define them. After that weʹll jump into a discussion of the basic ʹheap organizedʹ table and  then move onto the other types.  Expert one-on-one Oracle 254 Terminology  In this section, we will cover the various storage parameters and terminology associated  with tables. Not all parameters are used for every table type. For example, the PCTUSED  parameter is not meaningful in the context of an index organized table. We will mention in  the discussion of each table type below which parameters are relevant. The goal is to  introduce the terms and define them. As appropriate, more information on using specific  parameters will be covered in subsequent sections.  High Water Mark  This is a term used with objects stored in the database. If you envision a table for example  as a ʹflatʹ structure, as a series of blocks laid one after the other in a line from left to right,  the high water mark would be the right most block that ever contained data. For example:    This shows that the high water mark starts at the first block of a newly created table. As  data is placed into the table over time and more blocks get used, the high water mark rises.  If we delete some (or even all) of the rows in the table, we might have many blocks that no  longer contain data, but they are still under the high water mark and will remain under the  high water mark until the object is rebuilt or truncated.  The high water mark is relevant since Oracle will scan all blocks under the high water  mark, even when they contain no data, during a full scan. This will impact the  performance of a full scan ‐ especially if most of the blocks under the high water mark are  empty. To see this, just create a table with 1,000,000 rows (or create any table with a large  number of rows). Do a SELECT COUNT(*) from this table. Now, DELETE every row in it  Expert one-on-one Oracle 255 and you will find that the SELECT COUNT(*) takes just as long to count zero rows as it did  to count 1,000,000. This is because Oracle is busy reading all of the blocks below the high  water mark to see if they contain data. You should compare this to what happens if you  used TRUNCATE on the table instead of deleting each individual row. TRUNCATE will  reset the high water mark of a table back to ʹzeroʹ. If you plan on deleting every row in a  table, TRUNCATE would be the method of my choice for this reason.   FREELISTS  The FREELIST is where Oracle keeps tracks of blocks under the high water mark for  objects that have free space on them. Each object will have at least one FREELIST  associated with it and as blocks are used, they will be placed on or taken off of the  FREELIST as needed. It is important to note that only blocks under the high water mark of  an object will be found on the FREELIST. The blocks that remain above the high water  mark, will be used only when the FREELISTs are empty, at which point Oracle advances  the high water mark and adds these blocks to the FREELIST. In this fashion, Oracle  postpones increasing the high water mark for an object until it has to.  An object may have more than one FREELIST. If you anticipate heavy INSERT or  UPDATE activity on an object by many concurrent users, configuring more then one  FREELIST can make a major positive impact on performance (at the cost of possible  additional storage). As we will see later, having sufficient FREELISTs for your needs is  crucial.  Freelists can be a huge positive performance influence (or inhibitor) in an environment  with many concurrent inserts and updates. An extremely simple test can show the benefits  of setting this correctly. Take the simplest table in the world:  tkyte@TKYTE816> create table t ( x int );  and using two sessions, start inserting into it like wild. If you measure the system‐wide  wait events for block related waits before and after, you will find huge waits, especially on  data blocks (trying to insert data). This is frequently caused by insufficient FREELISTs on  tables (and on indexes but weʹll cover that again in Chapter 7, Indexes). For example, I set  up a temporary table:   tkyte@TKYTE816> create global temporary table waitstat_before    2  on commit preserve rows    3  as    4  select * from v$waitstat    5  where 1=0    6  /        Expert one-on-one Oracle 256 Table created.  to hold the before picture of waits on blocks. Then, in two sessions, I simultaneously ran:   tkyte@TKYTE816> truncate table waitstat_before;        Table truncated.        tkyte@TKYTE816> insert into waitstat_before    2  select * from v$waitstat    3  /        14 rows created.        tkyte@TKYTE816> begin    2          for i in 1 .. 100000    3          loop    4                  insert into t values ( i );    5                  commit;    6          end loop;    7  end;    8/        PL/SQL procedure successfully completed.  Now, this is a very simple block of code, and we are the only users in the database here.  We should get as good performance as you can get. Iʹve plenty of buffer cache configured,  my redo logs are sized appropriately, indexes wonʹt be slowing things down; this should  run fast. What I discover afterwards however, is that:  tkyte@TKYTE816> select a.class, b.count‐a.count count, b.time‐a.time time    2    from waitstat_before a, v$waitstat b    3   where a.class = b.class    4  /        CLASS                   COUNT       TIME  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  bitmap block                0          0  bitmap index block          0          0  data block               4226       3239  extent map                  0          0  free list                   0          0  save undo block             0          0  Expert one-on-one Oracle 257 save undo header            0          0  segment header              2          0  sort block                  0          0  system undo block           0          0  system undo header          0          0  undo block                  0          0  undo header               649         36  unused                      0          0  I waited over 32 seconds during these concurrent runs. This is entirely due to not having  enough FREELISTs configured on my tables for the type of concurrent activity I am  expecting to do. I can remove all of that wait time easily, just by creating the table with  multiple FREELISTs:   tkyte@TKYTE816> create table t ( x int ) storage ( FREELISTS 2 );        Table created.  or by altering the object:  tkyte@TKYTE816> alter table t storage ( FREELISTS 2 );        Table altered.  You will find that both wait events above go to zero; it is that easy. What you want to do  for a table is try to determine the maximum number of concurrent (truly concurrent)  inserts or updates that will require more space. What I mean by truly concurrent, is how  often do you expect two people at exactly the same instant, to request a free block for that  table. This is not a measure of overlapping transactions, it is a measure of sessions doing  an insert at the same time, regardless of transaction boundaries. You want to have about as  many FREELISTs as concurrent inserts into the table to increase concurrency.   You should just set FREELISTs really high and then not worry about it, right? Wrong ‐ of  course, that would be too easy. Each process will use a single FREELIST. It will not go  from FREELIST to FREELIST to find space. What this means is that if you have ten  FREELISTs on a table and the one your process is using exhausts the free buffers on its list,  it will not go to another list for space. It will cause the table to advance the high water  mark, or if the tables high water cannot be advanced (all space is used), to extend, to get  another extent. It will then continue to use the space on its FREELIST only (which is empty  now). There is a tradeoff to be made with multiple FREELISTs. On one hand, multiple  FREELISTs is a huge performance booster. On the other hand, it will probably cause the  table to use more disk space then absolutely necessary. You will have to decide which is  less bothersome in your environment.  Expert one-on-one Oracle 258 Do not underestimate the usefulness of this parameter, especially since we can alter it up  and down at will with Oracle 8.1.6 and up. What you might do is alter it to a large number  to perform some load of data in parallel with the conventional path mode of SQLLDR.  You will achieve a high degree of concurrency for the load with minimum waits. After the  load, you can alter the FREELISTs back down to some, more day‐to‐day, reasonable  number, the blocks on the many existing FREELISTs will be merged into the one master  FREELIST when you alter the space down.  PCTFREE and PCTUSED  These two settings control when blocks will be put on and taken off the FREELISTs. When  used with a table (but not an Index Organized Table as weʹll see), PCTFREE tells Oracle  how much space should be reserved on a block for future updates. By default, this is 10  percent. What this means is that if we use an 8 KB block size, as soon as the addition of a  new row onto a block would cause the free space on the block to drop below about 800  bytes, Oracle will use a new block instead of the existing block. This 10 percent of the data  space on the block is set aside for updates to the rows on that block. If we were to update  them ‐ the block would still be able to hold the updated row.  Now, whereas PCTFREE tells Oracle when to take a block off the FREELIST making it no  longer a candidate for insertion, PCTUSED tells Oracle when to put a block on the  FREELIST again. If the PCTUSED is set to 40 percent (the default), and the block hit the  PCTFREE level (it is not on the FREELIST currently), then 61 percent of the block must be  free space before Oracle will put the block back on the FREELIST. If we are using the  default values for PCTFREE (10) and PCTUSED (40) then a block will remain on the  FREELIST until it is 90 percent full (10 percent free space). Once it hits 90 percent, it will be  taken off of the FREELIST and remain off the FREELIST, until the free space on the block  exceeds 60 percent of the block.  Pctfree and PCTUSED are implemented differently for different table types as will be  noted below when we discuss each type. Some table types employ both, others only use  PCTFREE and even then only when the object is created.  There are three settings for PCTFREE, too high, too low, and just about right. If you set  PCTFREE for blocks too high, you will waste space. If you set PCTFREE to 50 percent and  you never update the data, you have just wasted 50 percent of every block. On another  table however, 50 percent may be very reasonable. If the rows start out small and tend to  double in size, a large setting for PCTFREE will avoid row migration.  Row Migration  So, that poses the question; what exactly is row migration? Row migration is when a row  is forced to leave the block it was created on, because it grew too large to fit on that block  Expert one-on-one Oracle 259 with the rest of the rows. Iʹll illustrate a row migration below. We start with a block that  looks like this:    Approximately one seventh of the block is free space. However, we would like to more  than double the amount of space used by row 4 via an UPDATE (it currently consumes a  seventh of the block). In this case, even if Oracle coalesced the space on the block like this:    there is still insufficient room to grow row 4 by more than two times its current size,  because the size of the free space is less than the size of row 4. If the row could have fitted  in the coalesced space, then this would have happened. This time however, Oracle will not  perform this coalescing and the block will remain as it is. Since row 4 would have to span  more than one block if it stayed on this block, Oracle will move, or migrate, the row.  However, it cannot just move it; it must leave behind a ʹforwarding addressʹ. There may be  indexes that physically point to this address for row 4. A simple update will not modify  the indexes as well (note that there is a special case with partitioned tables that a row ID,  the address of a row, will change. We will look at this case in the Chapter 14 on  Partitioning,). Therefore, when Oracle migrates the row, it will leave behind a pointer to  where the row really is. After the update, the blocks might look like the following:    Expert one-on-one Oracle 260 So, this is what a migrated row is; it is a row that had to move from the block it was  inserted into, onto some other block. Why is this an issue? Your application will never  know, the SQL you use is no different. It only matters for performance reasons. If we go to  read this row via an index, the index will point to the original block. That block will point  to the new block. Instead of doing the two or so I/Os to read the index plus one I/O to read  the table, weʹll need to do yet one more I/O to get to the actual row data. In isolation, this  is no ʹbig dealʹ; you wonʹt even notice this. However, when you have a sizable percentage  of your rows in this state with lots of users you will begin to notice this side effect. Access  to this data will start to slow down (additional I/Os add to the access time), your buffer  cache efficiency goes down (you need to buffer twice the amount of blocks you would if  they were not migrated), and your table grows in size and complexity. It is for these  reasons that you do not want migrated rows. It is interesting to note what Oracle will do if  the row that was migrated from the block on the left to the block on the right, in the  diagram above, was to have to migrate again at some future point in time. This would be  due to other rows being added to the block it was migrated to and then updating this row  to make it even larger. Oracle will actually migrate the row back to the original block and if  there is sufficient space leave it there (the row might become ʹun‐migratedʹ). If there isnʹt  sufficient space, Oracle will migrate the row to another block all together and change the  forwarding address on the original block. As such, row migrations will always involve one  level of indirection. So, now we are back to PCTFREE and what it is used for; it is the  setting that will help you to minimize row chaining when set properly.  Setting PCTFREE and PCTUSED values  Setting PCTFREE and PCTUSED is an important, and a greatly overlooked, topic, I would  like to show you how you can measure the behavior of your objects, to see how space is  being used. I will use a stored procedure that will show the effects of inserts on a table  with various PCTFREE/PCTUSED settings followed by a series of updates to the same  data. This will illustrate how these settings can affect the number of blocks available on the  FREELIST (which ultimately will affect how space is used, how many rows are migrated  and so on). These scripts are illustrative; they donʹt tell you what to set the values to, they  can be used by you to figure out how Oracle is treating your blocks given various types of  updates. They are templates that you will have to modify in order to effectively use them.  I started by creating a test table:  tkyte@TKYTE816> create table t ( x int, y char(1000) default ʹxʹ );        Table created.  It is a very simple table but for illustrative purposes will serve nicely. By using the CHAR  type, Iʹve ensured every row with a non‐null value for Y will be 1,000 bytes long. I should  Expert one-on-one Oracle 261 be able to ʹguessʹ how things will work given a specific block size. Now for the routine to  measure FREELIST and block usage:   tkyte@TKYTE816> create or replace procedure measure_usage    2  as    3      l_free_blks                 number;    4      l_total_blocks              number;    5      l_total_bytes               number;    6      l_unused_blocks             number;    7      l_unused_bytes              number;    8      l_LastUsedExtFileId         number;    9      l_LastUsedExtBlockId        number;   10      l_LAST_USED_BLOCK           number;   11   12      procedure get_data   13      is   14      begin   15          dbms_space.free_blocks   16          ( segment_owner     =>  USER,   17            segment_name      => ʹTʹ,   18            segment_type      => ʹTABLEʹ,   19            FREELIST_group_id => 0,   20            free_blks         => l_free_blks );   21   22          dbms_space.unused_space   23          ( segment_owner     => USER,   24            segment_name      => ʹTʹ,   25            segment_type      => ʹTABLEʹ,   26            total_blocks      => l_total_blocks,   27            total_bytes       => l_total_bytes,   28            unused_blocks     => l_unused_blocks,   29            unused_bytes      => l_unused_bytes,   30            LAST_USED_EXTENT_FILE_ID => l_LastUsedExtFileId,   31            LAST_USED_EXTENT_BLOCK_ID => l_LastUsedExtBlockId,   32            LAST_USED_BLOCK => l_last_used_block ) ;   33   34   35        dbms_output.put_line( L_free_blks || ʹ on FREELIST, ʹ ||   36                             to_number(l_total_blocks‐l_unused_blocks‐1 ) ||   37                             ʹ used by tableʹ );   38      end;   39  begin   40      for i in 0 .. 10  Expert one-on-one Oracle 262  41      loop   42          dbms_output.put( ʹinsert ʹ || to_char(i,ʹ00ʹ) || ʹ ʹ );   43          get_data;   44          insert into t (x) values ( i );   45          commit ;   46      end loop;   47   48   49      for i in 0 .. 10   50      loop   51          dbms_output.put( ʹupdate ʹ || to_char(i,ʹ00ʹ) || ʹ ʹ );   52          get_data;   53          update t set y = null where x = i;   54          commit;   55      end loop;   56  end;   57  /        Procedure created.  Here we use two routines in the DBMS_SPACE package that tell us how many blocks are  on a segmentʹs FREELIST, how many blocks are allocated to the table, unused blocks and  so on. We can use this information to tell ourselves how many of the blocks that have been  used by the table (below the high water mark of the table) are on the FREELIST. I then  insert 10 rows into the table with a non‐Null Y. Then I come back and update Y to Null  row by row. Given that I have an 8 KB block size, with a default PCTFREE of 10 and a  default PCTUSED of 40, I would expect that seven rows should fit nicely on the block (the  calculation below is done without considering the block/row overhead):   (2+1)bytes for X + (1000+2)bytes for Y = 1005  1005 bytes/row * 7 rows = 7035  8192 ‐ 7035 bytes (blocksize) = 1157 bytes        1157 bytes are leftover, insufficient for another row plus 800+ bytes (10% of the  block)  Now, since 10 percent of the 8 KB block is about 800 + bytes, we know we cannot fit  another row onto that block. If we wanted to, we could calculate the block header exactly,  here we will just guess that it is less then 350 + bytes (1157 ‐ 800 = 357). That gives us room  for seven rows per block.  Next estimate how many updates it will take to put a block back on the FREELIST. Here,  we know the block must be less than 40 percent used ‐ that is only a maximum of 3,275  Expert one-on-one Oracle 263 bytes can be in use to get back onto the free list. We would expect then that if each  UPDATE gives back 1,000 bytes, it would take about four UPDATEs to put a block back  on the FREELIST. Well, lets see how well I did:   tkyte@TKYTE816> exec measure_usage;  insert  00 0 on FREELIST, 0 used by table  insert  01 1 on FREELIST, 1 used by table  insert  02 1 on FREELIST, 1 used by table  insert  03 1 on FREELIST, 1 used by table  insert  04 1 on FREELIST, 1 used by table  insert  05 1 on FREELIST, 1 used by table  insert  06 1 on FREELIST, 1 used by table  insert  07 1 on FREELIST, 1 used by table ‐‐ between the 7th and 8th rows  insert  08 1 on FREELIST, 2 used by table    we added another block ʹin useʹ  insert  09 1 on FREELIST, 2 used by table  insert  10 1 on FREELIST, 2 used by table  update  00 1 on FREELIST, 2 used by table  update  01 1 on FREELIST, 2 used by table  update  02 1 on FREELIST, 2 used by table  update  03 1 on FREELIST, 2 used by table  update  04 2 on FREELIST, 2 used by table ‐‐ the 4th update put another  update  05 2 on FREELIST, 2 used by table    block back on the free list  update  06 2 on FREELIST, 2 used by table  update  07 2 on FREELIST, 2 used by table  update  08 2 on FREELIST, 2 used by table  update  09 2 on FREELIST, 2 used by table  update  10 2 on FREELIST, 2 used by table        PL/SQL procedure successfully completed.  Sure enough, after seven INSERTs, another block is added to the table. Likewise, after four  UPDATEs, the blocks on the FREELIST increase from 1 to 2 (both blocks are back on the  FREELIST, available for INSERTs). If we drop and recreate the table T with different  settings and then measure it again, we get the following:   tkyte@TKYTE816> create table t ( x int, y char(1000) default ʹxʹ ) pctfree 10    2   pctused 80;        Table created.        tkyte@TKYTE816> exec measure_usage;  insert  00 0 on FREELIST, 0 used by table  insert  01 1 on FREELIST, 1 used by table  Expert one-on-one Oracle 264 insert  02 1 on FREELIST, 1 used by table  insert  03 1 on FREELIST, 1 used by table  insert  04 1 on FREELIST, 1 used by table  insert  05 1 on FREELIST, 1 used by table  insert  06 1 on FREELIST, 1 used by table  insert  07 1 on FREELIST, 1 used by table  insert  08 1 on FREELIST, 2 used by table  insert  09 1 on FREELIST, 2 used by table  insert  10 1 on FREELIST, 2 used by table  update  00 1 on FREELIST, 2 used by table  update  01 2 on FREELIST, 2 used by table ‐‐ first update put a block  update  02 2 on FREELIST, 2 used by table    back on the free list due to the  update  03 2 on FREELIST, 2 used by table    much higher pctused  update  04 2 on FREELIST, 2 used by table  update  05 2 on FREELIST, 2 used by table  update  06 2 on FREELIST, 2 used by table  update  07 2 on FREELIST, 2 used by table  update  08 2 on FREELIST, 2 used by table  update  09 2 on FREELIST, 2 used by table  update  10 2 on FREELIST, 2 used by table        PL/SQL procedure successfully completed.  We can see the effect of increasing PCTUSED here. The very first UPDATE had the effect  of putting the block back on the FREELIST. That block can be used by another INSERT  again that much faster.  Does that mean you should increase your PCTUSED? No, not necessarily. It depends on  how your data behaves over time. If your application goes through cycles of:  1. Adding data (lots of INSERTs) followed by,   2. UPDATEs ‐ Updating the data causing the rows to grow and shrink.   3. Go back to adding data.   I might never want a block to get put onto the FREELIST as a result of an update. Here we  would want a very low PCTUSED, causing a block to go onto a FREELIST only after all of  the row data has been deleted. Otherwise, some of the blocks that have rows that are  temporarily ʹshrunkenʹ would get newly inserted rows if PCTUSED was set high. Then,  when we go to update the old and new rows on these blocks; there wonʹt be enough room  for them to grow and they migrate.  In summary, PCTUSED and PCTFREE are crucial. On one hand you need to use them to  avoid too many rows from migrating, on the other hand you use them to avoid wasting  Expert one-on-one Oracle 265 too much space. You need to look at your objects, describe how they will be used, and  then you can come up with a logical plan for setting these values. Rules of thumb may  very well fail us on these settings; they really need to be set based on how you use it. You  might consider (and remember high and low are relative terms):  • High PCTFREE, Low PCTUSED ‐ For when you insert lots of data that will be  updated and the updates will increase the size of the rows frequently. This reserves  a lot of space on the block after inserts (high PCTFREE) and makes it so that the  block must almost be empty before getting back onto the free list (low PCTUSED).   • Low PCTFREE, High PCTUSED ‐ If you tend to only ever INSERT or DELETE from  the table or if you do UPDATE, the UPDATE tends to shrink the row in size.   INITIAL, NEXT, and PCTINCREASE  These are storage parameters that define the size of the INITIAL and subsequent extents  allocated to a table and the percentage by which the NEXT extent should grow. For  example, if you use an INITIAL extent of 1 MB, a NEXT extent of 2 MB, and a  PCTINCREASE of 50 ‐ your extents would be:  1. 1 MB.   2. 2 MB.   3. 3 MB (150 percent of 2).   4. 4.5 MB (150 percent of 3).   and so on. I consider these parameters to be obsolete. The database should be using locally  managed tablespaces with uniform extent sizes exclusively. In this fashion the INITIAL  extent is always equal to the NEXT extent size and there is no such thing as  PCTINCREASE ‐ a setting that only causes fragmentation in a tablespace.  In the event you are not using locally managed tablespaces, my recommendation is to  always set INITIAL = NEXT and PCTINCREASE to ZERO. This mimics the allocations you  would get in a locally managed tablespace. All objects in a tablespace should use the same  extent allocation strategy to avoid fragmentation.  MINEXTENTS and MAXEXTENTS  These settings control the number of extents an object may allocate for itself. The setting  for MINEXTENTS tells Oracle how many extents to allocate to the table initially. For  example, in a locally managed tablespace with uniform extent sizes of 1 MB, a  MINEXTENTS setting of 10 would cause the table to have 10 MB of storage allocated to it.  MAXEXTENTS is simply an upper bound on the possible number of extents this object  may acquire. If you set MAXEXTENTS to 255 in that same tablespace, the largest the table  Expert one-on-one Oracle 266 would ever get to would be 255 MB in size. Of course, if there is not sufficient space in the  tablespace to grow that large, the table will not be able to allocate these extents.  LOGGING and NOLOGGING  Normally objects are created in a LOGGING fashion, meaning all operations performed  against them that can generate redo will generate it. NOLOGGING allows certain  operations to be performed against that object without the generation of redo.  NOLOGGING only affects only a few specific operations such as the initial creation of the  object, or direct path loads using SQLLDR, or rebuilds (see the SQL Language Reference  Manual for the database object you are working with to see which operations apply).  This option does not disable redo log generation for the object in general; only for very  specific operations. For example, if I create a table as SELECT NOLOGGING and then,  INSERT INTO THAT_TABLE VALUES ( 1 ), the INSERT will be logged, but the table  creation would not have been.   INITRANS and MAXTRANS  Each block in an object has a block header. Part of this block header is a transaction table,  entries will be made in the transaction table to describe which transactions have what  rows/elements on the block locked. The initial size of this transaction table is specified by  the INITRANS setting for the object. For tables this defaults to 1 (indexes default to 2).  This transaction table will grow dynamically as needed up to MAXTRANS entries in size  (given sufficient free space on the block that is). Each allocated transaction entry consumes  23 bytes of storage in the block header.  Heap Organized Table  A heap organized table is probably used 99 percent (or more) of the time in applications,  although that might change over time with the advent of index organized tables, now that  index organized tables can themselves be indexed. A heap organized table is the type of  table you get by default when you issue the CREATE TABLE statement. If you want any  other type of table structure, you would need to specify that in the CREATE statement  itself.  A heap is a classic data structure studied in computer science. It is basically, a big area of  space, disk, or memory (disk in the case of a database table, of course), which is managed  in an apparently random fashion. Data will be placed where it fits best, not in any specific  sort of order. Many people expect data to come back out of a table in the same order it was  put into it, but with a heap, this is definitely not assured. In fact, rather the opposite is  guaranteed; the rows will come out in a wholly unpredictable order. This is quite easy to  demonstrate. I will set up a table, such that in my database I can fit one full row per block  Expert one-on-one Oracle 267 (I am using an 8 KB block size). You do not need to have the case where you only have one  row per block I am just taking advantage of that to demonstrate a predictable sequence of  events. The following behavior will be observed on tables of all sizes, in databases with  any blocksize:   tkyte@TKYTE816> create table t    2  ( a int,    3    b varchar2(4000) default rpad(ʹ*ʹ,4000,ʹ*ʹ),    4    c varchar2(3000) default rpad(ʹ*ʹ,3000,ʹ*ʹ)    5  )    6  /        Table created.        tkyte@TKYTE816> insert into t (a) values ( 1);  1 row created.        tkyte@TKYTE816> insert into t (a) values ( 2);        1 row created.        tkyte@TKYTE816> insert into t (a) values ( 3);        1 row created.        tkyte@TKYTE816> delete from t where a = 2 ;        1 row deleted.        tkyte@TKYTE816> insert into t (a) values ( 4);        1 row created.        tkyte@TKYTE816> select a from t;                 A  ‐‐‐‐‐‐‐‐‐‐           1           4           3  Adjust columns B and C to be appropriate for your block size if you would like to  reproduce this. For example, if you have a 2 KB block size, you do not need column C, and  Expert one-on-one Oracle 268 column B should be a VARCHAR2(1500) with a default of 1500 asterisks. Since data is  managed in a heap in a table like this, as space becomes available, it will be reused. A full  scan of the table will retrieve the data as it hits it, never in the order of insertion. This is a  key concept to understand about database tables; in general, they are inherently  unordered collections of data. You should also note that we do not need to use a DELETE  in order to observe the above ‐ I could achieve the same results using only INSERTs. If I  insert a small row, followed by a very large row that will not fit on the block with the  small row, and then a small row again, I may very well observe that the rows come out by  default in the order ʹsmall row, small row, large rowʹ. They will not be retrieved in the  order of insertion. Oracle will place the data where it fits, not in any order by date or  transaction.  If your query needs to retrieve data in order of insertion, we must add a column to that  table that we can use to order the data when retrieving it. That column could be a number  column for example that was maintained with an increasing sequence (using the Oracle  SEQUENCE object). We could then approximate the insertion order using ʹselect by  orderingʹ on this column. It will be an approximation because the row with sequence  number 55 may very well have committed before the row with sequence 54, therefore it  was officially ʹfirstʹ in the database.  So, you should just think of a heap organized table as a big, unordered collection of rows.  These rows will come out in a seemingly random order and depending on other options  being used (parallel query, different optimizer modes and so on), may come out in a  different order with the same query. Do not ever count on the order of rows from a query  unless you have an ORDER BY statement on your query!  That aside, what is important to know about heap tables? Well, the CREATE TABLE  syntax spans almost 40 pages in the SQL reference manual provided by Oracle so there are  lots of options that go along with them. There are so many options that getting a hold on  all of them is pretty difficult. The ʹwire diagramsʹ (or ʹtrain trackʹ diagrams) alone take  eight pages to cover. One trick I use to see most of the options available to me in the create  table statement for a given table, is to create the table as simply as possible, for example:   tkyte@TKYTE816> create table t    2  ( x int primary key ,    3    y date,    4    z clob )    5  /        Table created.  Then using the standard export and import utilities (see Chapter 8 on Import and Export),  weʹll export the definition of it and have import show us the verbose syntax:  Expert one-on-one Oracle 269 exp userid=tkyte/tkyte tables=t  imp userid=tkyte/tkyte full=y indexfile=t.sql  Iʹll now find that T.SQL contains my CREATE table statement in its most verbose form,  Iʹve formatted it a bit for easier reading but otherwise it is straight from the DMP file  generated by export:   CREATE TABLE ʺTKYTEʺ.ʺTʺ  (ʺXʺ NUMBER(*,0), ʺYʺ DATE, ʺZʺ CLOB)  PCTFREE 10 PCTUSED 40  INITRANS 1 MAXTRANS 255  LOGGING STORAGE(INITIAL 32768 NEXT 32768                  MINEXTENTS 1 MAXEXTENTS 4096                  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1                  BUFFER_POOL DEFAULT                  )  TABLESPACE ʺTOOLSʺ  LOB (ʺZʺ) STORE AS (TABLESPACE ʺTOOLSʺ                      ENABLE STORAGE IN ROW CHUNK 8192                      PCTVERSION 10 NOCACHE                      STORAGE(INITIAL 32768 NEXT 32768                              MINEXTENTS 1 MAXEXTENTS 4096                              PCTINCREASE 0                              FREELISTS 1 FREELIST GROUPS 1                              BUFFER_POOL DEFAULT)) ;        ALTER TABLE ʺTKYTEʺ.ʺTʺ  ADD PRIMARY KEY (ʺXʺ)  USING INDEX  PCTFREE 10 INITRANS 2 MAXTRANS 255  STORAGE(INITIAL 32768 NEXT 32768          MINEXTENTS 1 MAXEXTENTS 4096          PCTINCREASE 0          FREELISTS 1 FREELIST GROUPS 1          BUFFER_POOL DEFAULT)  TABLESPACE ʺTOOLSʺ ENABLE ;  The nice thing about the above is that it shows many of the options for my CREATE  TABLE statement. I just have to pick data types and such; Oracle will produce the verbose  version for me. I can now customize this verbose version, perhaps changing the ENABLE  STORAGE IN ROW to DISABLE STORAGE IN ROW ‐ this would disable the stored of the  LOB data in the row with the structured data, causing it to be stored in another segment. I  use this trick myself all of the time to save the couple minutes of confusion I would  Expert one-on-one Oracle 270 otherwise have if I tried to figure this all out from the huge wire diagrams. I can also use  this to learn what options are available to me on the CREATE TABLE statement under  different circumstances.  This is how I figure out what is available to me as far as the syntax of the CREATE TABLE  goes ‐ in fact I use this trick on many objects. Iʹll have a small testing schema, create ʹbare  bonesʹ objects in that schema, export using OWNER = THAT_SCHEMA, and do the  import. A review of the generated SQL file shows me what is available.  Now that we know how to see most of the options available to us on a given CREATE  TABLE statement, what are the important ones we need to be aware of for heap tables? In  my opinion they are:  • FREELISTS ‐ every table manages the blocks it has allocated in the heap on a  FREELIST. A table may have more then one FREELIST. If you anticipate heavy  insertion into a table by many users, configuring more then one FREELIST can  make a major positive impact on performance (at the cost of possible additional  storage). Refer to the previous discussion and example above (in the section  FREELISTS) for the sort of impact this setting can have on performance.   • PCTFREE ‐ a measure of how full a block can be made during the INSERT process.  Once a block has less then the ʹPCTFREEʹ space left on it, it will no longer be a  candidate for insertion of new rows. This will be used to control row migrations  caused by subsequent updates and needs to be set based on how you use the table.   • PCTUSED ‐ a measure of how empty a block must become, before it can be a  candidate for insertion again. A block that has less then PCTUSED space used is a  candidate for insertion of new rows. Again, like PCTFREE, you must consider how  you will be using your table in order to set this appropriately.   • INITRANS ‐ the number of transaction slots initially allocated to a block. If set too  low (defaults to 1) this can cause concurrency issues in a block that is accessed by  many users. If a database block is nearly full and the transaction list cannot be  dynamically expanded ‐ sessions will queue up waiting for this block as each  concurrent transaction needs a transaction slot. If you believe you will be having  many concurrent updates to the same blocks, you should consider increasing this  value   Note: LOB data that is stored out of line in the LOB segment does not make use of the  PCTFREE/PCTUSED parameters set for the table. These LOB blocks are managed  differently. They are always filled to capacity and returned to the FREELIST only when  completely empty.  These are the parameters you want to pay particularly close attention to. I find that the rest  of the storage parameters are simply not relevant any more. As I mentioned earlier in the  Expert one-on-one Oracle 271 chapter, we should use locally managed tablespaces, and these do not utilize the  parameters PCTINCREASE, NEXT, and so on.  Index Organized Tables  Index organized tables (IOTs) are quite simply a table stored in an index structure.  Whereas a table stored in a heap is randomly organized, data goes wherever there is  available space, data in an IOT is stored and sorted by primary key. IOTs behave just like a  ʹregularʹ table does as far as your application is concerned; you use SQL to access it as  normal. They are especially useful for information retrieval (IR), spatial, and OLAP  applications.  What is the point of an IOT? One might ask the converse actually; what is the point of a  heap‐organized table? Since all tables in a relational database are supposed to have a  primary key anyway, isnʹt a heap organized table just a waste of space? We have to make  room for both the table and the index on the primary key of the table when using a heap  organized table. With an IOT, the space overhead of the primary key index is removed, as  the index is the data, the data is the index. Well, the fact is that an index is a complex data  structure that requires a lot of work to manage and maintain. A heap on the other hand is  trivial to manage by comparison. There are efficiencies in a heap‐organized table over an  IOT. That said, there are some definite advantages to IOTs over their counterpart the heap.  For example, I remember once building an inverted list index on some textual data (this  predated the introduction of interMedia and related technologies). I had a table full of  documents. I would parse the documents and find words within the document. I had a  table that then looked like this:  create table keywords  ( word  varchar2(50),    position   int,    doc_id int,    primary key(word,position,doc_id)  );  Here I had a table that consisted solely of columns of the primary key. I had over 100  percent overhead; the size of my table and primary key index were comparable (actually  the primary key index was larger since it physically stored the row ID of the row it  pointed to whereas a row ID is not stored in the table ‐ it is inferred). I only used this table  with a WHERE clause on the WORD or WORD and POSITION columns. That is, I never  used the table; I only used the index on the table. The table itself was no more then  overhead. I wanted to find all documents containing a given word (or ʹnearʹ another word  and so on). The table was useless, it just slowed down the application during maintenance  of the KEYWORDS table and doubled the storage requirements. This is a perfect  application for an IOT.  Expert one-on-one Oracle 272 Another implementation that begs for an IOT is a code lookup table. Here you might have  ZIP_CODE to STATE lookup for example. You can now do away with the table and just  use the IOT itself. Anytime you have a table, which you access via its primary key  frequently it is a candidate for an IOT.  Another implementation that makes good use of IOTs is when you want to build your  own indexing structure. For example, you may want to provide a case insensitive search  for your application. You could use function‐based indexes (see Chapter 7 on Indexes for  details on what this is). However, this feature is available with Enterprise and Personal  Editions of Oracle only. Suppose you have the Standard Edition, one way to provide a  case insensitive, keyword search would be to ʹroll your ownʹ function‐based index. For  example, suppose you wanted to provide a case‐insensitive search on the ENAME column  of the EMP table. One approach would be to create another column, ENAME_UPPER, in  the EMP table and index that column. This shadow column would be maintained via a  trigger. If you didnʹt like the idea of having the extra column in the table, you can just  create your own function‐based index, with the following:   tkyte@TKYTE816> create table emp as select * from scott.emp;        Table created.        tkyte@TKYTE816> create table upper_ename    2  ( x$ename, x$rid,    3    primary key (x$ename,x$rid)    4  )    5  organization index    6  as    7  select upper(ename), rowid from emp    8  /  Table created.        tkyte@TKYTE816> create or replace trigger upper_ename    2  after insert or update or delete on emp    3  for each row    4  begin    5      if (updating and (:old.ename||ʹxʹ <> :new.ename||ʹxʹ))    6      then    7          delete from upper_ename    8           where x$ename = upper(:old.ename)    9             and x$rid = :old.rowid;   10   11          insert into upper_ename   12          (x$ename,x$rid) values  Expert one-on-one Oracle 273  13          ( upper(:new.ename), :new.rowid );   14      elsif (inserting)   15      then   16          insert into upper_ename   17          (x$ename,x$rid) values   18          ( upper(:new.ename), :new.rowid );   19      elsif (deleting)   20      then   21          delete from upper_ename   22           where x$ename = upper(:old.ename)   23             and x$rid = :old.rowid;   24      end if;   25  end;   26  /        Trigger created.        tkyte@TKYTE816> update emp set ename = initcap(ename);        14 rows updated.        tkyte@TKYTE816> commit;        Commit complete.  Now, the table UPPER_ENAME is in effect our case‐insensitive index, much like a  function‐based index would be. We must explicitly use this ʹindexʹ, Oracle doesnʹt know  about it. The following shows how you might use this ʹindexʹ to UPDATE, SELECT, and  DELETE data from the table:   tkyte@TKYTE816> update    2  (    3  select ename, sal    4    from emp    5   where emp.rowid in ( select upper_ename.x$rid    6                          from upper_ename    7                         where x$ename = ʹKINGʹ )    8  )    9  set sal = 1234   10  /        1 row updated.  tkyte@TKYTE816> select ename, empno, sal  Expert one-on-one Oracle 274   2    from emp, upper_ename    3   where emp.rowid = upper_ename.x$rid    4     and upper_ename.x$ename = ʹKINGʹ    5  /        ENAME           EMPNO        SAL  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  King             7839       1234        tkyte@TKYTE816> delete from    2  (    3  select ename, empno    4    from emp    5   where emp.rowid in ( select upper_ename.x$rid    6                          from upper_ename    7                         where x$ename = ʹKINGʹ )    8  )    9  /        1 row deleted.  We can either use an IN or a JOIN when selecting. Due to ʹkey preservationʹ rules, we  must use the IN when updating or deleting. A side note on this method, since it involves  storing a row ID: our index organized table, as would any index, must be rebuilt if we do  something that causes the row IDs of the EMP table to change ‐ such as exporting and  importing EMP or using the ALTER TABLE MOVE command on it.  Finally, when you want to enforce co‐location of data or you want data to be physically  stored in a specific order, the IOT is the structure for you. For users of Sybase and SQL  Server, this is when you would have used a clustered index, but it goes one better. A  clustered index in those databases may have up to a 110 percent overhead (similar to my  KEYWORDS table example above). Here, we have a 0 percent overhead since the data is  stored only once. A classic example of when you might want this physically co‐located  data would be in a parent/child relationship. Letʹs say the EMP table had a child table:   tkyte@TKYTE816> create table addresses    2  ( empno     number(4) references emp(empno) on delete cascade,    3    addr_type varchar2(10),    4    street    varchar2(20),    5    city      varchar2(20),    6    state     varchar2(2),    7    zip       number,    8    primary key (empno,addr_type)  Expert one-on-one Oracle 275   9  )   10  ORGANIZATION INDEX   11  /        Table created.  Having all of the addresses for an employee (their home address, work address, school  address, previous address, and so on) physically located near each other will reduce the  amount of I/O you might other wise have to perform when joining EMP to ADDRESSES.  The logical I/O would be the same, the physical I/O could be significantly less. In a heap  organized table, each employee address might be in a physically different database block  from any other address for that employee. By storing the addresses organized by EMPNO  and ADDR_TYPE ‐ weʹve ensured that all addresses for a given employee are ʹnearʹ each  other.  The same would be true if you frequently use BETWEEN queries on a primary or unique  key. Having the data stored physically sorted will increase the performance of those  queries as well. For example, I maintain a table of stock quotes in my database. Every day  we gather together the stock ticker, date, closing price, days high, days low, volume, and  other related information. We do this for hundreds of stocks. This table looks like:   tkyte@TKYTE816> create table stocks    2  ( ticker      varchar2(10),    3    day         date,    4    value       number,    5    change      number,    6    high        number,    7    low         number,    8    vol         number,    9    primary key(ticker,day)   10  )   11  organization index   12  /        Table created.  We frequently look at one stock at a time ‐ for some range of days (computing a moving  average for example). If we were to use a heap organized table, the probability of two  rows for the stock ticker ORCL existing on the same database block are almost zero. This is  because every night, we insert the records for the day for all of the stocks. That fills up at  least one database block (actually many of them). Therefore, every day we add a new  ORCL record but it is on a block different from every other ORCL record already in the  table. If we query:   Expert one-on-one Oracle 276 Select * from stocks   where ticker = ʹORCLʹ     and day between sysdate and sysdate ‐ 100;  Oracle would read the index and then perform table access by row ID to get the rest of the  row data. Each of the 100 rows we retrieve would be on a different database block due to  the way we load the table ‐ each would probably be a physical I/O. Now consider that we  have this in an IOT. That same query only needs to read the relevant index blocks and it  already has all of the data. Not only is the table access removed but all of the rows for  ORCL in a given range of dates are physically stored ʹnearʹ each other as well. Less logical  I/O and less physical I/O is incurred.  Now we understand when we might want to use index organized tables and how to use  them. What we need to understand next is what are the options with these tables? What  are the caveats? The options are very similar to the options for a heap‐organized table.  Once again, weʹll use EXP/IMP to show us the details. If we start with the three basic  variations of the index organized table:   tkyte@TKYTE816> create table t1    2  (  x int primary key,    3     y varchar2(25),    4     z date    5  )    6  organization index;        Table created.        tkyte@TKYTE816> create table t2    2  (  x int primary key,    3     y varchar2(25),    4     z date    5  )    6  organization index    7  OVERFLOW;        Table created.        tkyte@TKYTE816> create table t3    2  (  x int primary key,    3     y varchar2(25),    4     z date    5  )    6  organization index  Expert one-on-one Oracle 277   7  overflow INCLUDING y;        Table created.  Weʹll get into what OVERFLOW and INCLUDING do for us but first, letʹs look at the  detailed SQL required for the first table above:   CREATE TABLE ʺTKYTEʺ.ʺT1ʺ  (ʺXʺ NUMBER(*,0),   ʺYʺ VARCHAR2(25),   ʺZʺ DATE,   PRIMARY KEY (ʺXʺ) ENABLE  )  ORGANIZATION INDEX  NOCOMPRESS  PCTFREE 10  INITRANS 2 MAXTRANS 255  LOGGING  STORAGE ( INITIAL 32768            NEXT 32768            MINEXTENTS 1 MAXEXTENTS 4096            PCTINCREASE 0            FREELISTS 1            FREELIST GROUPS 1            BUFFER_POOL DEFAULT          )  TABLESPACE ʺTOOLSʺ  PCTTHRESHOLD 50 ;  It introduces two new options, NOCOMPRESS and PCTTHRESHOLD, weʹll take a look at  those in a moment. You might have noticed that something is missing from the above  CREATE TABLE syntax; there is no PCTUSED clause but there is a PCTFREE. This is  because an index is a complex data structure, not randomly organized like a heap; data  must go where it ʹbelongsʹ. Unlike a heap where blocks are sometimes available for inserts,  blocks are always available for new entries in an index. If the data belongs on a given  block because of its values, it will go there regardless of how full or empty the block is.  Additionally, PCTFREE is used only when the object is created and populated with data in  an index structure. It is not used like it is used in the heap‐organized table. PCTFREE will  reserve space on a newly created index, but not for subsequent operations on it for much  the same reason as why PCTUSED is not used at all. The same considerations for  FREELISTs we had on heap organized tables apply in whole to IOTs.  Expert one-on-one Oracle 278 Now, onto the newly discovered option NOCOMPRESS. This is an option available to  indexes in general. It tells Oracle to store each and every value in an index entry (do not  compress). If the primary key of the object was on columns A, B, and C, every combination  of A, B, and C would physically be stored. The converse to NOCOMPRESS is COMPRESS  N where N is an integer, which represents the number of columns to compress. What this  does is remove repeating values, factors them out at the block level, so that the values of A  and perhaps B that repeat over and over are no longer physically stored. Consider for  example a table created like this:   tkyte@TKYTE816> create table iot    2  ( owner, object_type, object_name,    3    primary key(owner,object_type,object_name)    4  )    5  organization index    6  NOCOMPRESS    7  as    8  select owner, object_type, object_name from all_objects    9  /        Table created.  It you think about it, the value of OWNER is repeated many hundreds of times. Each  schema (OWNER) tends to own lots of objects. Even the value pair of OWNER,  OBJECT_TYPE repeats many times; a given schema will have dozens of tables, dozens of  packages, and so on. Only all three columns together do not repeat. We can have Oracle  suppress these repeating values. Instead of having an index block with values:  Sys,table,t1  Sys,table,t2  Sys,table,t3  Sys,table,t4  Sys,table,t5  Sys,table,t6  Sys,table,t7  Sys,table,t8  …  …  …  …  Sys,table,t100  Sys,table,t101  Sys,table,t102  Sys,table,t103  We could use COMPRESS 2 (factor out the leading two columns) and have a block with:  Sys,table  t1  t2  t3  t4  t5  …  …  …  t103  t104  …  t300  t301  t302  t303  That is, the values SYS and TABLE appear once and then the third column is stored. In this  fashion, we can get many more entries per index block than we could otherwise. This does  Expert one-on-one Oracle 279 not decrease concurrency or functionality at all. It takes slightly more CPU horsepower,  Oracle has to do more work to put together the keys again. On the other hand, it may  significantly reduce I/O and allows more data to be cached in the buffer cache ‐ since we  get more data per block. That is a pretty good trade off. Weʹll demonstrate the savings by  doing a quick test of the above CREATE TABLE as SELECT with NOCOMPRESS,  COMPRESS 1, and COMPRESS 2. Weʹll start with a procedure that shows us the space  utilization of an IOT easily:   tkyte@TKYTE816> create or replace    2  procedure show_iot_space    3  ( p_segname in varchar2 )    4  as    5      l_segname                   varchar2(30);    6      l_total_blocks              number;    7      l_total_bytes               number;    8      l_unused_blocks             number;    9      l_unused_bytes              number;   10      l_LastUsedExtFileId         number;   11      l_LastUsedExtBlockId        number;   12      l_last_used_block           number;   13  begin   14      select ʹSYS_IOT_TOP_ʹ || object_id   15        into l_segname   16        from user_objects   17       where object_name = upper(p_segname);   18   19      dbms_space.unused_space   20      ( segment_owner     => user,   21        segment_name      => l_segname,   22        segment_type      => ʹINDEXʹ,   23        total_blocks      => l_total_blocks,   24        total_bytes       => l_total_bytes,   25        unused_blocks     => l_unused_blocks,   26        unused_bytes      => l_unused_bytes,   27        LAST_USED_EXTENT_FILE_ID => l_LastUsedExtFileId,   28        LAST_USED_EXTENT_BLOCK_ID => l_LastUsedExtBlockId,   29        LAST_USED_BLOCK => l_last_used_block );   30   31      dbms_output.put_line   32      ( ʹIOT used ʹ || to_char(l_total_blocks‐l_unused_blocks) );   33  end;   34  /        Expert one-on-one Oracle 280 Procedure created.  And now weʹll create our IOT without compression:  tkyte@TKYTE816> create table iot    2  ( owner, object_type, object_name,    3    primary key(owner,object_type,object_name)    4  )    5  organization index    6  NOCOMPRESS    7  as    8  select owner, object_type, object_name from all_objects    9  order by owner, object_type, object_name   10  /        Table created.        tkyte@TKYTE816> set serveroutput on  tkyte@TKYTE816> exec show_iot_space( ʹiotʹ );  IOT used 135        PL/SQL procedure successfully completed.  If you are working these examples as we go along, I would expect that you see a different  number, something other than 135. It will be dependent on your block size and the  number of objects in your data dictionary. We would expect this number to decrease  however in the next example:  tkyte@TKYTE816> create table iot    2  ( owner, object_type, object_name,    3    primary key(owner,object_type,object_name)    4  )    5  organization index    6  compress 1    7  as    8  select owner, object_type, object_name from all_objects    9  order by owner, object_type, object_name   10  /        Table created.  tkyte@TKYTE816> exec show_iot_space( ʹiotʹ );  IOT used 119        Expert one-on-one Oracle 281 PL/SQL procedure successfully completed.  So that IOT is about 12 percent smaller then the first one; we can do better by compressing  it even more:  tkyte@TKYTE816> create table iot    2  ( owner, object_type, object_name,    3    primary key(owner,object_type,object_name)    4  )    5  organization index    6  compress 2    7  as    8  select owner, object_type, object_name from all_objects    9  order by owner, object_type, object_name   10  /        Table created.        tkyte@TKYTE816> exec show_iot_space( ʹiotʹ );  IOT used 91        PL/SQL procedure successfully completed.  The COMPRESS 2 index is about a third smaller then the uncompressed IOT. Your  mileage will vary but the results can be fantastic.  The above example points out an interesting fact with IOTs. They are tables, but only in  name. Their segment is truly an index segment. In order to show the space utilization I  had to convert the IOT table name into its underlying index name. In these examples, I  allowed the underlying index name be generated for me; it defaults to  SYS_IOT_TOP_ where OBJECT_ID is the internal object id assigned to the table.  If I did not want these generated names cluttering my data dictionary, I can easily name  them:   tkyte@TKYTE816> create table iot    2  ( owner, object_type, object_name,    3    constraint iot_pk primary key(owner,object_type,object_name)    4  )    5  organization index    6  compress 2    7  as    8  select owner, object_type, object_name from all_objects    9  /  Expert one-on-one Oracle 282       Table created.  Normally, it is considered a good practice to name your objects explicitly like this. It  typically provides more meaning to the actual use of the object than a name like  SYS_IOT_TOP_1234 does.  I am going to defer discussion of the PCTTHRESHOLD option at this point as it is related  to the next two options for IOTs; OVERFLOW and INCLUDING. If we look at the full SQL  for the next two sets of tables T2 and T3, we see the following:   CREATE TABLE ʺTKYTEʺ.ʺT2ʺ  (ʺXʺ NUMBER(*,0),   ʺYʺ VARCHAR2(25),   ʺZʺ DATE,   PRIMARY KEY (ʺXʺ) ENABLE  )  ORGANIZATION INDEX  NOCOMPRESS  PCTFREE 10  INITRANS 2 MAXTRANS 255  LOGGING  STORAGE ( INITIAL 32768 NEXT 32768 MINEXTENTS 1 MAXEXTENTS 4096            PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT )  TABLESPACE ʺTOOLSʺ  PCTTHRESHOLD 50  OVERFLOW           PCTFREE 10                      PCTUSED 40           INITRANS 1           MAXTRANS 255           LOGGING           STORAGE ( INITIAL 32768 NEXT 32768 MINEXTENTS 1 MAXEXTENTS 4096                     PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1                     BUFFER_POOL DEFAULT )  TABLESPACE ʺTOOLSʺ ;  CREATE TABLE  ʺTKYTEʺ.ʺT3ʺ  (ʺXʺ NUMBER(*,0),   ʺYʺ VARCHAR2(25),   ʺZʺ DATE,   PRIMARY KEY (ʺXʺ) ENABLE  )  Expert one-on-one Oracle 283 ORGANIZATION INDEX  NOCOMPRESS  PCTFREE 10  INITRANS 2  MAXTRANS 255  LOGGING  STORAGE(INITIAL 32768 NEXT 32768 MINEXTENTS 1 MAXEXTENTS 4096          PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT )  TABLESPACE ʺTOOLSʺ  PCTTHRESHOLD 50  INCLUDING ʺYʺ  OVERFLOW PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 LOGGING           STORAGE ( INITIAL 32768 NEXT 32768 MINEXTENTS 1 MAXEXTENTS 4096                     PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1                     BUFFER_POOL DEFAULT )  TABLESPACE ʺTOOLSʺ ;  So, now we have PCTTHRESHOLD, OVERFLOW, and INCLUDING left to discuss. These  three items are intertwined with each other and their goal is to make the index leaf blocks  (the blocks that hold the actual index data) able to efficiently store data. An index typically  is on a subset of columns. You will generally find many more times the number of rows on  an index block than you would on a heap table block. An index counts on being able to get  many rows per block, Oracle would spend large amounts of time maintaining an index  otherwise, as each INSERT or UPDATE would probably cause an index block to split in  order to accommodate the new data.   The OVERFLOW clause allows you to setup another segment where the row data for the  IOT can overflow onto when it gets too large. Notice that an OVERFLOW reintroduces the  PCTUSED clause to an IOT. PCTFREE and PCTUSED have the same meanings for an  OVERFLOW segment as they did for a heap table. The conditions for using an overflow  segment can be specified in one of two ways:  • PCTTHRESHOLD ‐ When the amount of data in the row exceeds that percentage of  the block, the trailing columns of that row will be stored in the overflow. So, if  PCTTHRESHOLD was 10 percent and your block size was 8 KB, any row that was  greater then about 800 bytes in length would have part of it stored elsewhere ‐ off  the index block.   • INCLUDING ‐ All of the columns in the row up to and including the one specified  in the INCLUDING clause, are stored on the index block, the remaining columns  are stored in the overflow.   Given the following table with a 2 KB block size:  Expert one-on-one Oracle 284 ops$tkyte@ORA8I.WORLD> create table iot    2  (  x    int,    3     y    date,    4     z    varchar2(2000),    5     constraint iot_pk primary key (x)    6  )    7  organization index    8  pctthreshold 10    9  overflow   10  /        Table created.  Graphically, it could look like this:    The gray boxes are the index entries, part of a larger index structure (in the Chapter 7 on  Indexes, youʹll see a larger picture of what an index looks like). Briefly, the index structure  is a tree, and the leaf blocks (where the data is stored), are in effect a doubly‐linked list to  make it easier to traverse the nodes in order once you have found where you want to start  at in the index. The white box represents an OVERFLOW segment. This is where data that  exceeds our PCTTHRESHOLD setting will be stored. Oracle will work backwards from  the last column up to but not including the last column of the primary key to find out  what columns need to be stored in the overflow segment. In this example, the number  column X and the date column Y will always fit in the index block. The last column, Z, is  of varying length. When it is less than about 190 bytes or so (10 percent of a 2 KB block is  about 200 bytes, add in 7 bytes for the date and 3 to 5 for the number), it will be stored on  the index block. When it exceeds 190 bytes, Oracle will store the data for Z in the overflow  segment and set up a pointer to it.  Expert one-on-one Oracle 285 The other option is to use the INCLUDING clause. Here you are stating explicitly what  columns you want stored on the index block and which should be stored in the overflow.  Given a create table like this:   ops$tkyte@ORA8I.WORLD> create table iot    2  (  x    int,    3     y    date,    4     z    varchar2(2000),    5     constraint iot_pk primary key (x)    6  )    7  organization index    8  including y    9  overflow   10  /        Table created.  We can expect to find:    In this situation, regardless of the size of the data stored in it, Z will be stored ʹout of lineʹ  in the overflow segment.   Which is better then, PCTTHRESHOLD, INCLUDING, or some combination of both? It  depends on your needs. If you have an application that always, or almost always, uses the  first four columns of a table, and rarely accesses the last five columns, this sounds like an  application for using INCLUDING. You would include up to the fourth column and let  the other five be stored out of line. At runtime, if you need them, they will be retrieved in  much the same way as a migrated or chained row would be. Oracle will read the ʹheadʹ of  the row, find the pointer to the rest of the row, and then read that. If on the other hand,  you cannot say that you almost always access these columns and hardly ever access those  columns, you would be giving some consideration to PCTTHRESHOLD. Setting the  PCTTHRESHOLD is easy once you determine the number of rows you would like to store  per index block on average. Suppose you wanted 20 rows per index block. Well, that  Expert one-on-one Oracle 286 means each row should be 1/20th (5 percent) then. Your PCTTHRESHOLD would be five;  each chunk of the row that stays on the index leaf block should consume no more then 5  percent of the block.  The last thing to consider with IOTs is indexing. You can have an index on an index, as  long as the primary index is an IOT. These are called secondary indexes. Normally an  index contains the physical address of the row it points to, the row ID. An IOT secondary  index cannot do this; it must use some other way to address the row. This is because a row  in an IOT can move around a lot and it does not ʹmigrateʹ in the way a row in a heap  organized table would. A row in an IOT is expected to be at some position in the index  structure, based on its primary key; it will only be moving because the size and shape of  the index itself is changing. In order to accommodate this, Oracle introduced a logical row  ID. These logical row IDs are based on the IOTʹs primary key. They may also contain a  ʹguessʹ as to the current location of the row (although this guess is almost always wrong  after a short while, data in an IOT tends to move). An index on an IOT is slightly less  efficient then an index on a regular table. On a regular table, an index access typically  requires the I/O to scan the index structure and then a single read to read the table data.  With an IOT there are typically two scans performed, one on the secondary structure and  the other on the IOT itself. That aside, indexes on IOTs provide fast and efficient access to  the data in the IOT using columns other then the primary key.  Index Organized Tables Wrap‐up  Getting the right mix of data on the index block versus data in the overflow segment is the  most critical part of the IOT set up. Benchmark various scenarios with different overflow  conditions. See how it will affect your INSERTs, UPDATEs, DELETEs, and SELECTs. If  you have a structure that is built once and read frequently, stuff as much of the data onto  the index block as you can. If you frequently modify the structure, you will have to come  to some balance between having all of the data on the index block (great for retrieval)  versus reorganizing data in the index frequently (bad for modifications). The FREELIST  consideration you had for heap tables applies to IOTs as well. PCTFREE and PCTUSED  play two roles in an IOT. PCTFREE is not nearly as important for an IOT as for a heap  table and PCTUSED doesnʹt come into play normally. When considering an OVERFLOW  segment however, PCTFREE and PCTUSED have the same interpretation as they did for a  heap table; set them for an overflow segment using the same logic you would for a heap  table.  Index Clustered Tables  I generally find peoples understanding of what a cluster is in Oracle to be inaccurate.  Many people tend to confuse this with a SQL Server or Sybase ʹclustered indexʹ. They are  not. A cluster is a way to store a group of tables that share some common column(s) in the  Expert one-on-one Oracle 287 same database blocks and to store related data together on the same block. A clustered  index in SQL Server forces the rows to be stored in sorted order according to the index key,  they are similar to an IOT described above. With a cluster, a single block of data may  contain data from many tables. Conceptually, you are storing the data ʹpre‐joinedʹ. It can  also be used with single tables. Now you are storing data together grouped by some  column. For example, all of the employees in department 10 will be stored on the same  block (or as few blocks as possible, if they all donʹt fit). It is not storing the data sorted ‐  that is the role of the IOT. It is storing the data clustered by some key, but in a heap. So,  department 100 might be right next to department 1, and very far away (physically on disk)  from departments 101 and 99.   Graphically, you might think of it as I have depicted below. On the left‐hand side we are  using conventional tables. EMP will be stored in its segment. DEPT will be stored on its  own. They may be in different files, different tablespaces, and are definitely in separate  extents. On the right‐hand side, we see what would happen if we clustered these two  tables together. The square boxes represent database blocks. We now have the value 10  factored out and stored once. Then, all of the data from all of the tables in the cluster for  department 10 is stored in that block. If all of the data for department 10 does not fit on the  block, then additional blocks will be chained to the original block to contain the overflow,  very much in the same fashion as the overflow blocks for an IOT:    So, letʹs look at how you might go about creating a clustered object. Creating a cluster of  tables in it is straightforward. The definition of the storage of the object (PCTFREE,  PCTUSED, INITIAL, and so on) is associated with the CLUSTER, not the tables. This  makes sense since there will be many tables in the cluster, and they each will be on the  same block. Having different PCTFREEs would not make sense. Therefore, a CREATE  CLUSTER looks a lot like a CREATE TABLE with a small number of columns (just the  cluster key columns):   tkyte@TKYTE816> create cluster emp_dept_cluster    2  ( deptno number(2) )    3  size 1024    4  /        Cluster created.  Expert one-on-one Oracle 288 Here we have created an index cluster (the other type being a hash cluster; weʹll look at  that below). The clustering column for this cluster will be the DEPTNO column, the  columns in the tables do not have to be called DEPTNO, but they must be a NUMBER(2),  to match this definition. I have, on the cluster definition, a SIZE 1024 option. This is used  to tell Oracle that we expect about 1,024 bytes of data to be associated with each cluster  key value. Oracle will use that to compute the maximum number of cluster keys that could  fit per block. Given that I have an 8 KB block size, Oracle will fit up to seven cluster keys  (but maybe less if the data is larger then expected) per database block. This is, the data for  the departments 10, 20, 30, 40, 50, 60, 70 would tend to go onto one block, as soon as you  insert department 80 a new block will be used. That does not mean that the data is stored  in a sorted manner, it just means that if you inserted the departments in that order, they  would naturally tend to be put together. If you inserted the departments in the following  order: 10, 80, 20, 30, 40, 50, 60, and then 70, the final department, 70, would tend to be on  the newly added block. As weʹll see below, both the size of the data and the order in which  the data is inserted will affect the number of keys we can store per block.   The size parameter therefore controls the maximum number of cluster keys per block. It is  the single largest influence on the space utilization of your cluster. Set the size too high  and youʹll get very few keys per block and youʹll use more space then you need. Set the  size too low and youʹll get excessive chaining of data, which offsets the purpose of the  cluster to store all of the data together on a single block. It is the important parameter for a  cluster.  Now, for the cluster index on our cluster. We need to index the cluster before we can put  data in it. We could create tables in the cluster right now, but I am going to create and  populate the tables simultaneously and we need a cluster index before we can have any  data. The cluster indexʹs job is to take a cluster key value and return the block address of  the block that contains that key. It is a primary key in effect where each cluster key value  points to a single block in the cluster itself. So, when you ask for the data in department 10,  Oracle will read the cluster key, determine the block address for that and then read the  data. The cluster key index is created as follows:  tkyte@TKYTE816> create index emp_dept_cluster_idx    2  on cluster emp_dept_cluster    3  /        Index created.  It can have all of the normal storage parameters of an index and can be stored in another  tablespace. It is just a regular index, one that happens to index into a cluster and can also  include an entry for a completely null value (see Chapter 7 on Indexes for the reason why  this is interesting to note). Now we are ready to create our tables in the cluster:  Expert one-on-one Oracle 289 tkyte@TKYTE816> create table dept    2  ( deptno number(2) primary key,    3    dname  varchar2(14),    4    loc       varchar2(13)    5  )    6  cluster emp_dept_cluster(deptno)    7  /        Table created.        tkyte@TKYTE816> create table emp    2  ( empno number primary key,    3    ename varchar2(10),    4    job      varchar2(9),    5    mgr      number,    6    hiredate date,    7    sal      number,    8    comm     number,    9    deptno number(2) references dept(deptno)   10  )   11  cluster emp_dept_cluster(deptno)   12  /        Table created.  Here the only difference from a ʹnormalʹ table is that I used the CLUSTER keyword and  told Oracle which column of the base table will map to the cluster key in the cluster itself.  We can now load them up with the initial set of data:   tkyte@TKYTE816> begin    2          for x in ( select * from scott.dept )    3          loop    4                  insert into dept    5                  values ( x.deptno, x.dname, x.loc );    6                  insert into emp    7                  select *    8                    from scott.emp    9                   where deptno = x.deptno;   10          end loop;   11  end;   12  /        PL/SQL procedure successfully completed.  Expert one-on-one Oracle 290 You might be asking yourself ʹWhy didnʹt we just insert all of the DEPT data and then all  of the EMP data or vice‐versa, why did we load the data DEPTNO by DEPTNO like that?ʹ  The reason is in the design of the cluster. I was simulating a large, initial bulk load of a  cluster. If I had loaded all of the DEPT rows first ‐ we definitely would have gotten our 7  keys per block (based on the SIZE 1024 setting we made) since the DEPT rows are very  small, just a couple of bytes. When it came time to load up the EMP rows, we might have  found that some of the departments had many more than 1,024 bytes of data. This would  cause excessive chaining on those cluster key blocks. By loading all of the data for a given  cluster key at the same time, we pack the blocks as tightly as possible and start a new  block when we run out of room. Instead of Oracle putting up to seven cluster key values  per block, it will put as many as can fit. A quick example will show the difference between  the two approaches. What I will do is add a large column to the EMP table; a CHAR(1000).  This column will be used to make the EMP rows much larger then they are now. We will  load the cluster tables in two ways ‐ once weʹll load up DEPT and then load up EMP. The  second time weʹll load by department number ‐ a DEPT row and then all the EMP rows  that go with it and then the next DEPT. Weʹll look at the blocks each row ends up on, in  the given case, to see which one best achieves the goal of co‐locating the data by DEPTNO.  In this example, our EMP table looks like:   create table emp  ( empno number primary key,    ename varchar2(10),    job   varchar2(9),    mgr   number,    hiredate date,    sal   number,    comm  number,    deptno number(2) references dept(deptno),    data   char(1000) default ʹ*ʹ  )  cluster emp_dept_cluster(deptno)  /  When we load the data into the DEPT and the EMP tables we see that many of the EMP  rows are not on the same block as the DEPT row anymore (DBMS_ROWID is a supplied  package useful for peeking at the contents of a row ID):   tkyte@TKYTE816> insert into dept    2  select * from scott.dept    3  /        4 rows created. tkyte@TKYTE816> insert into emp    2  select emp.*, ʹ*ʹ from scott.emp  Expert one-on-one Oracle 291   3  /        14 rows created.        tkyte@TKYTE816> select dbms_rowid.rowid_block_number(dept.rowid) dept_rid,    2         dbms_rowid.rowid_block_number(emp.rowid) emp_rid,    3             dept.deptno    4   from emp, dept    5   where emp.deptno = dept.deptno    6  /          DEPT_RID    EMP_RID DEPTNO  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐          10         12     10          10         11     10          10         11     10          10         10     20          10         10     20          10         12     20          10         11     20          10         11     20          10         10     30          10         10     30          10         10     30          10         10     30          10         11     30          10         11     30        14 rows selected.  More then half of the EMP rows are not on the block with the DEPT row. Loading the data  using the cluster key instead of the table key, we get:   tkyte@TKYTE816> begin    2      for x in ( select * from scott.dept )    3      loop    4          insert into dept    5          values ( x.deptno, x.dname, x.loc );    6          insert into emp    7          select emp.*, ʹxʹ    8            from scott.emp    9           where deptno = x.deptno;   10      end loop;  Expert one-on-one Oracle 292  11  end;   12  /        PL/SQL procedure successfully completed.        tkyte@TKYTE816> select dbms_rowid.rowid_block_number(dept.rowid) dept_rid,    2         dbms_rowid.rowid_block_number(emp.rowid) emp_rid,    3             dept.deptno    4   from emp, dept    5   where emp.deptno = dept.deptno    6  /          DEPT_RID    EMP_RID DEPTNO  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐          11         11     30          11         11     30          11         11     30          11         11     30          11         11     30          11         11     30          12         12     10          12         12     10          12         12     10          12         12     20          12         12     20          12         12     20          12         10     20          12         10     20        14 rows selected.  Most of the EMP rows are on the same block as the DEPT rows are. This example was  somewhat contrived in that I woefully undersized the SIZE parameter on the cluster to  make a point, but the approach suggested is correct for an initial load of a cluster. It will  ensure that if for some of the cluster keys you exceed the estimated SIZE, you will still end  up with most of the data clustered on the same block. If you load a table at a time, you will  not.  This only applies to the initial load of a cluster ‐ after that, you would use it as your  transactions deem necessary, you will not adapt you application to work specifically with  a cluster.  Expert one-on-one Oracle 293 Here is a bit of puzzle to amaze and astound your friends with. Many people mistakenly  believe a row ID uniquely identifies a row in a database, that given a row ID I can tell you  what table the row came from. In fact, you cannot. You can and will get duplicate row IDs  from a cluster. For example, after executing the above you should find:  tkyte@TKYTE816> select rowid from emp    2  intersect    3  select rowid from dept;        ROWID  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  AAAGB0AAFAAAAJyAAA  AAAGB0AAFAAAAJyAAB  AAAGB0AAFAAAAJyAAC  AAAGB0AAFAAAAJyAAD  Every row ID assigned to the rows in DEPT has been assigned to the rows in EMP as well.  That is because it takes a table and row ID to uniquely identify a row. The row ID pseudo  column is unique only within a table.  I also find that many people believe the cluster object to be an esoteric object that no one  really uses. Everyone just uses normal tables. The fact is, that you use clusters every time  you use Oracle. Much of the data dictionary is stored in various clusters. For example:   sys@TKYTE816> select cluster_name, table_name from user_tables    2  where cluster_name is not null    3  order by 1    4  /        CLUSTER_NAME                   TABLE_NAME  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  C_COBJ#                        CCOL$                                 CDEF$  C_FILE#_BLOCK#                 SEG$                                 UET$  C_MLOG#                        MLOG$                                 SLOG$  C_OBJ#                         ATTRCOL$                                 COL$                                 COLTYPE$                                 CLU$                                 ICOLDEP$                                 LIBRARY$  Expert one-on-one Oracle 294                                LOB$                                 VIEWTRCOL$                                 TYPE_MISC$                                 TAB$                                 REFCON$                                 NTAB$                                 IND$                                 ICOL$  C_OBJ#_INTCOL#                 HISTGRM$  C_RG#                          RGCHILD$                                 RGROUP$  C_TOID_VERSION#                ATTRIBUTE$                                 COLLECTION$                                 METHOD$                                 RESULT$                                 TYPE$                                 PARAMETER$  C_TS#                          FET$                                 TS$  C_USER#                        TSQ$                                 USER$        33 rows selected.  As can be seen, most of the object related data is stored in a single cluster (the C_OBJ#  cluster), 14 tables all together sharing the same block. It is mostly column‐related  information stored there, so all of the information about the set of columns of a table or  index is stored physically on the same block. This makes sense; when Oracle parses a  query, it wants to have access to the data for all of the columns in the referenced table. If  this data was spread all over the place, it would take a while to get it together. Here it is on  a single block typically, and readily available.  When would you use a cluster? It is easier perhaps to describe when not to use them:  • Clusters may negatively impact the performance of DML ‐ If you anticipate the  tables in the cluster to be modified heavily, you must be aware that an index cluster  will have certain negative performance side effects. It takes more work to manage  the data in a cluster.   • Full scans of tables in clusters are affected ‐ Instead of just having to full scan the  data in your table, you have to full scan the data for (possibly) many tables. There is  more data to scan through. Full scans will take longer.   Expert one-on-one Oracle 295 • If you believe you will frequently need to TRUNCATE and load the table ‐ Tables in  clusters cannot be truncated, that is obvious since the cluster stores more then one  table on a block, we must delete the rows in a cluster table.   So, if you have data that is mostly read (that does not mean ʹnever writtenʹ, it is perfectly  OK to modify cluster tables) and read via indexes, either the cluster key index or other  indexes you put on the tables in the cluster, and join this information together frequently,  a cluster would be appropriate. Look for tables that are logically related and always used  together, like the people who designed the Oracle data dictionary when they clustered all  column‐related information together.  Index Clustered Tables Wrap‐up  Clustered tables give you the ability to physically ʹpre‐joinʹ data together. You use clusters  to store related data from many tables on the same database block. Clusters can help read  intensive operations that always join data together or access related sets of data (for  example, everyone in department 10). They will reduce the number of blocks that Oracle  must cache; instead of keeping 10 blocks for 10 employees in the same department, they  will be put in one block and therefore would increase the efficiency of your buffer cache.  On the downside, unless you can calculate your SIZE parameter setting correctly, clusters  may be inefficient with their space utilization and can tend to slow down DML heavy  operations.  Hash Cluster Tables  Hash clustered tables are very similar in concept to the index cluster described above with  one main exception. The cluster key index is replaced with a hash function. The data in the  table is the index, there is no physical index. Oracle will take the key value for a row, hash  it using either an internal function or one you supply, and use that to figure out where the  data should be on disk. One side effect of using a hashing algorithm to locate data  however, is that you cannot range scan a table in a hash cluster without adding a  conventional index to the table. In an index cluster above, the query:  select * from emp where deptno between 10 and 20  would be able to make use of the cluster key index to find these rows. In a hash cluster,  this query would result in a full table scan unless you had an index on the DEPTNO  column. Only exact equality searches may be made on the hash key without using an  index that supports range scans.  In a perfect world, with little to no collisions in the hashing algorithm, a hash cluster will  mean we can go straight from a query to the data with one I/O. In the real world, there  Expert one-on-one Oracle 296 will most likely be collisions and row chaining periodically, meaning weʹll need more then  one I/O to retrieve some of the data.  Like a hash table in a programming language, hash tables in the database have a fixed  ʹsizeʹ. When you create the table, you must determine the number of hash keys your table  will have, forever. That does not limit the amount of rows you can put in there.   Below, we can see a graphical representation of a hash cluster with table EMP created in it.  When the client issues a query that uses the hash cluster key in the predicate, Oracle will  apply the hash function to determine which block the data should be in. It will then read  that one block to find the data. If there have been many collisions or the SIZE parameter to  the CREATE CLUSTER was underestimated, Oracle will have allocated overflow blocks  that are chained off of the original block.    When you create a hash cluster, you will use the same CREATE CLUSTER statement you  used to create the index cluster with different options. Weʹll just be adding a HASHKEYs  option to it to specify the size of the hash table. Oracle will take your HASHKEYS values  and round it up to the nearest prime number, the number of hash keys will always be a  prime. Oracle will then compute a value based on the SIZE parameter multiplied by the  modified HASHKEYS value. It will then allocate at least that much space in bytes for the  cluster. This is a big difference from the index cluster above, which dynamically allocates  space, as it needs it. A hash cluster pre‐allocates enough space to hold  (HASHKEYS/trunc(blocksize/SIZE)) bytes of data. So for example, if you set your SIZE to  1,500 bytes and you have a 4 KB block size, Oracle will expect to store 2 keys per block. If  you plan on having 1,000 HASHKEYs, Oracle will allocate 500 blocks.  It is interesting to note that unlike a conventional hash table in a computer language, it is  OK to have hash collisions, in fact, it is desirable in many cases. If you take the same  DEPT/EMP example from above, you could set up a hash cluster based on the DEPTNO  column. Obviously, many rows will hash to the same value ‐ you expect them to (they  have the same DEPTNO), this is what the cluster is about in some respects, clustering like  data together. This is why Oracle asks you to specify the HASHKEYs (how many  department numbers do you anticipate over time) and SIZE (what is the size of the data  that will be associated with each department number). It allocates a hash table to hold  Expert one-on-one Oracle 297 HASHKEY number of departments of SIZE bytes each. What you do want to avoid is  unintended hash collisions. It is obvious that if you set the size of the hash table to 1,000  (really 1,009 since the hash table size is always a prime number and Oracle rounds up for  us) and put 1,010 departments in the table, there will be at least one collision (two different  departments hashing to the same value). Unintended hash collisions are to be avoided as  they add overhead and increase the probability of row chaining occurring.  In order to see what sort of space hash clusters take, weʹll write a small utility stored  procedure SHOW_SPACE that weʹll use in this chapter and in the next chapter on Indexes.  This routine just uses the DBMS_SPACE routines weʹve seen in part above to display  space used by objects in the database:   tkyte@TKYTE816> create or replace    2  procedure show_space    3  ( p_segname in varchar2,    4    p_owner   in varchar2 default user,    5    p_type    in varchar2 default ʹTABLEʹ,    6    p_partition in varchar2 default NULL )    7  as    8      l_free_blks                 number;    9   10      l_total_blocks              number;   11      l_total_bytes               number;   12      l_unused_blocks             number;   13      l_unused_bytes              number;   14      l_LastUsedExtFileId         number;   15      l_LastUsedExtBlockId        number;   16      l_last_used_block           number;   17      procedure p( p_label in varchar2, p_num in number )   18      is   19      begin   20          dbms_output.put_line( rpad(p_label,40,ʹ.ʹ) ||   21                                p_num );   22      end;   23  begin   24      dbms_space.free_blocks   25      ( segment_owner     => p_owner,   26        segment_name      => p_segname,   27        segment_type      => p_type,   28            partition_name    => p_partition,   29        freelist_group_id => 0,   30        free_blks         => l_free_blks );   31  Expert one-on-one Oracle 298  32      dbms_space.unused_space   33      ( segment_owner     => p_owner,   34        segment_name      => p_segname,   35        segment_type      => p_type,   36            partition_name    => p_partition,   37        total_blocks      => l_total_blocks,   38        total_bytes       => l_total_bytes,   39        unused_blocks     => l_unused_blocks,   40        unused_bytes      => l_unused_bytes,   41        last_used_extent_file_id => l_LastUsedExtFileId,   42        last_used_extent_block_id => l_LastUsedExtBlockId,   43        last_used_block => l_last_used_block );   44   45      p( ʹFree Blocksʹ, l_free_blks );   46      p( ʹTotal Blocksʹ, l_total_blocks );   47      p( ʹTotal Bytesʹ, l_total_bytes );   48      p( ʹUnused Blocksʹ, l_unused_blocks );   49      p( ʹUnused Bytesʹ, l_unused_bytes );   50      p( ʹLast Used Ext FileIdʹ, l_LastUsedExtFileId );   51      p( ʹLast Used Ext BlockIdʹ, l_LastUsedExtBlockId );   52      p( ʹLast Used Blockʹ, l_last_used_block );   53  end;   54  /        Procedure created.  Now if I issue a CREATE CLUSTER statement, such as the following, we can see the  storage it allocated:   tkyte@TKYTE816> create cluster hash_cluster    2  ( hash_key number )    3  hashkeys 1000    4  size 8192    5  /        Cluster created.        tkyte@TKYTE816> exec show_space( ʹHASH_CLUSTERʹ, user, ʹCLUSTERʹ )  Free Blocks.............................0  Total Blocks............................1016  Total Bytes.............................8323072  Unused Blocks...........................6  Unused Bytes............................49152  Expert one-on-one Oracle 299 Last Used Ext FileId....................5  Last Used Ext BlockId...................889  Last Used Block.........................2        PL/SQL procedure successfully completed.  I can see that the total number of blocks allocated to the table is 1,016. Six of these blocks  are unused (free). One block goes to table overhead, to manage the extents. Therefore,  there are 1,009 blocks under the high water mark of this object, and these are used by the  cluster. 1,009 just happens to be the next largest prime over 1,000 and since my block size  is 8 KB we can see that Oracle did in fact allocate (8192 * 1009) blocks. This figure is a little  higher than this, due to the way extents are rounded and/or by using locally managed  tablespaces with uniformly‐sized extents.  This points out one of the issues with hash clusters you need to be aware of. Normally, if I  create an empty table, the number of blocks under the high water mark for that table is 0.  If I full scan it, it reaches the high water mark and stops. With a hash cluster, the tables  will start out big and will take longer to create as Oracle must initialize each block, an  action that normally takes place as data is added to the table. They have the potential to  have data in their first block and their last block, with nothing in between. Full scanning a  virtually empty hash cluster will take as long as full scanning a full hash cluster. This is  not necessarily a bad thing; you built the hash cluster to have very fast access to the data  by a hash key lookup. You did not build it to full scan it frequently.  Now I can start placing tables into the hash cluster in the same fashion I did with index  clusters. For example:  tkyte@TKYTE816> create table hashed_table    2  ( x number, data1 varchar2(4000), data2 varchar2(4000) )    3  cluster hash_cluster(x);        Table created.  To see the difference a hash cluster can make, I set up a small test. I created a hash cluster,  loaded some data up in it, copied this data to a ʹregularʹ table with a conventional index on  it and then I did 100,000 random reads on each table (the same ʹrandomʹ reads on each).  Using SQL_TRACE and TKPROF (more on these tools in Chapter 10 on Tuning Strategies  and Tools), I was able to determine the performance characteristics of each. Below is the set  up I performed followed by the analysis of it:   tkyte@TKYTE816> create cluster hash_cluster    2  ( hash_key number )    3  hashkeys 50000  Expert one-on-one Oracle 300   4  size 45    5  /        Cluster created.        tkyte@TKYTE816> create table emp    2  cluster hash_cluster(empno)    3  as    4  select rownum empno, ename, job, mgr, hiredate, sal, comm, deptno    5    from scott.emp    6   where 1=0    7  /        Table created.  I created the hash cluster with a SIZE of 45 bytes. This is because I determined the average  row size for a row in my table would be about 45 bytes (I analyzed the SCOTT.EMP table  to determine this). I then created an empty table in that cluster that resembles the  SCOTT.EMP table. The one modification was to select ROWNUM instead of EMPNO so  the table I created was made with a NUMBER instead of NUMBER(4) column. I wanted  more than 9,999 rows in this table; I was going for about 50,000. Next I filled up the table  and created the ʹconventional cloneʹ of it:   tkyte@TKYTE816> declare    2          l_cnt   number;    3          l_empno number default 1;    4  begin    5          select count(*) into l_cnt from scott.emp;    6    7          for x in ( select * from scott.emp )    8          loop    9             for i in 1 .. trunc(50000/l_cnt)+1   10             loop   11                    insert into emp values   12                    ( l_empno, x.ename, x.job, x.mgr, x.hiredate, x.sal,   13                      x.comm, x.deptno );   14                    l_empno := l_empno+1;   15             end loop;   16          end loop;   17          commit;   18  end;   19  /        Expert one-on-one Oracle 301 PL/SQL procedure successfully completed.        tkyte@TKYTE816> create table emp_reg    2  as    3  select * from emp;        Table created.        tkyte@TKYTE816> alter table emp_reg add constraint emp_pk primary key(empno);        Table altered.  Now, all I needed was some ʹrandomʹ data to pick rows from each of the tables with:  tkyte@TKYTE816> create table random ( x int );        Table created.        tkyte@TKYTE816> begin    2          for i in 1 .. 100000    3          loop    4                  insert into random values    5                  ( mod(abs(dbms_random.random),50000)+1 );    6          end loop;    7  end;    8  /        PL/SQL procedure successfully completed.  Now we are ready to do a test:  tkyte@TKYTE816> alter session set sql_trace=true;        Session altered.        tkyte@TKYTE816> select count(ename)    2    from emp, random    3   where emp.empno = random.x;        COUNT(ENAME)  ‐‐‐‐‐‐‐‐‐‐‐‐        100000        Expert one-on-one Oracle 302 tkyte@TKYTE816> select count(ename)    2    from emp_reg, random    3   where emp_reg.empno = random.x;        COUNT(ENAME)  ‐‐‐‐‐‐‐‐‐‐‐‐        100000  I knew the optimizer would FULL SCAN random in both cases since there is no other  access method available for that table. I was counting on it doing a nested loops join to the  EMP and EMP_REG table (which it did). This did 100,000 random reads into the two  tables. The TKPROF report shows me:   select count(ename)    from emp, random   where emp.empno = random.x        call     count       cpu    elapsed  disk      query    current        rows  ‐‐‐‐‐‐‐ ‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐‐‐  Parse        1      0.00       0.00     0          0          2           0  Execute      1      0.00       0.00     0          0          0           0  Fetch        2      3.44       3.57    13     177348          4           1  ‐‐‐‐‐‐‐ ‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐‐‐  total        4      3.44       3.57    13     177348          6           1  Misses in library cache during parse: 1  Optimizer goal: CHOOSE  Parsing user id: 66        Rows     Row Source Operation  ‐‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐        1  SORT AGGREGATE   100000   NESTED LOOPS   100001    TABLE ACCESS FULL RANDOM   100000    TABLE ACCESS HASH EMP        ***************************************************************************        select count(ename)    from emp_reg, random   where emp_reg.empno = random.x  call     count       cpu    elapsed  disk      query    current        rows  ‐‐‐‐‐‐‐ ‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐‐‐  Parse        1      0.01       0.01     0          1          3           0  Expert one-on-one Oracle 303 Execute      1      0.00       0.00     0          0          0           0  Fetch        2      1.80       6.26   410     300153          4           1  ‐‐‐‐‐‐‐ ‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐‐‐  total        4      1.81       6.27   410     300154          7           1        Misses in library cache during parse: 1  Optimizer goal: CHOOSE  Parsing user id: 66        Rows     Row Source Operation  ‐‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐        1  SORT AGGREGATE   100000   NESTED LOOPS   100001    TABLE ACCESS FULL RANDOM   100000    TABLE ACCESS BY INDEX ROWID EMP_REG   200000     INDEX UNIQUE SCAN (object id 24743)  The points of interest here are:  • The hash cluster did significantly less I/O (query column). This is what we had  anticipated. The query simply took the random numbers, performed the hash on  them, and went to the block. The hash cluster has to do at least one I/O to get the  data. The conventional table with an index had to perform index scans followed by  a table access by row ID to get the same answer. The indexed table has to do at least  two I/Os to get the data.   • The hash cluster query took significantly more CPU. This too could be anticipated.  The act of performing a hash is very CPU‐intensive. The act of performing an index  lookup is I/O‐intensive.   • The hash cluster query had a better elapsed time. This will vary. On my system (a  single user laptop for this test; slow disks but I own the CPU), I was not CPU‐ bound ‐ I was disk‐bound. Since I had exclusive access to the CPU, the elapsed time  for the hash cluster query was very close to the CPU time. On the other hand, the  disks on my laptop were not the fastest. I spent a lot of time waiting for I/O.   This last point is the important one. When working with computers, it is all about  resources and their utilization. If you are I/O bound and perform queries that do lots of  keyed reads like I did above, a hash cluster may improve performance. If you are already  CPU‐bound, a hash cluster will possibly decrease performance since it needs more CPU  horsepower. This is one of the major reasons why rules of thumb do not work on real  world systems ‐ what works for you might not work for others in similar but different  conditions.  Expert one-on-one Oracle 304 There is a special case of a hash cluster and that is a ʹsingle tableʹ hash cluster. This is an  optimized version of the general hash cluster weʹve already looked at. It supports only one  table in the cluster at a time (you have to DROP the existing table in a single table hash  cluster before you can create another). Additionally, if there is a one to one mapping  between hash keys and data rows, the access to the rows is somewhat faster as well. These  hash clusters are designed for those occasions when you want to access a table by primary  key and do not care to cluster other tables with it. If you need fast access to an employee  record by EMPNO ‐ a single table hash cluster might be called for. I did the above test on a  single table hash cluster as well and found the performance to be even better than just a  hash cluster. I went a step further with this example however and took advantage of the  fact that Oracle will allow me to write my own specialized hash function (instead of using  the default one provided by Oracle). You are limited to using only the columns available  in the table and may only use the Oracle built‐in functions (no PL/SQL code for example)  when writing these hash functions. By taking advantage of the fact that EMPNO is a  number between 1 and 50,000 in the above example ‐ I made my ʹhash functionʹ simply be  the EMPNO column itself. In this fashion, I am guaranteed to never have a hash collision.  Putting it all together, weʹll create a single table hash cluster with my own hash function  via:   tkyte@TKYTE816> create cluster single_table_hash_cluster    2  ( hash_key INT )    3  hashkeys 50000    4  size 45    5  single table    6  hash is HASH_KEY    7  /        Cluster created.  Weʹve simply added the key words SINGLE TABLE to make it a single table hash cluster.  Our HASH IS function is simply the HASH_KEY cluster key in this case. This is a SQL  function, we could have used trunc(mod(hash_key/324+278,555)/abs(hash_key+1))if we  wanted (not that this is a good hash function, it just demonstrates that you can use a  complex function there if you wish). Then, we create our table in that cluster:   tkyte@TKYTE816> create table single_table_emp    2  ( empno INT ,    3    ename varchar2(10),    4    job      varchar2(9),    5    mgr      number,    6    hiredate date,    7    sal      number,    8    comm     number,  Expert one-on-one Oracle 305   9    deptno number(2)   10  )   11  cluster single_table_hash_cluster(empno)   12  /        Table created.  and load it up with the EMP data from before:   tkyte@TKYTE816> insert into single_table_emp    2  select * from emp;        50008 rows created.  After running the same query we did for the other two tables, we discover from the  TKPROF report that:   select count(ename)    from single_table_emp, random   where single_table_emp.empno = random.x  call     count       cpu    elapsed  disk      query    current        rows  ‐‐‐‐‐‐‐ ‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐‐‐  Parse        1      0.00       0.00     0          0          0           0  Execute      1      0.00       0.00     0          0          0           0  Fetch        2      3.29       3.44   127     135406          4           1  ‐‐‐‐‐‐‐ ‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐‐‐  total        4      3.29       3.44   127     135406          4           1        Misses in library cache during parse: 0  Optimizer goal: CHOOSE  Parsing user id: 264        Rows     Row Source Operation  ‐‐‐‐‐‐‐  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐        1  SORT AGGREGATE   100000   NESTED LOOPS   100001    TABLE ACCESS FULL RANDOM   100000    TABLE ACCESS HASH SINGLE_TABLE_EMP  This query processed three quarters of the number of blocks that the other hash cluster did.  This is due to some combination of using our own hash function that assured us of no  collisions and using a single table hash cluster.  Expert one-on-one Oracle 306 Hash Clusters Wrap‐up  That is the ʹnuts and boltsʹ of a hash cluster. They are similar in concept to the index  cluster above with the exception that a cluster index is not used. The data is the index in  this case. The cluster key is hashed into a block address and the data is expected to be  there. The important things to really understand are that:  • The hash cluster is allocated right from the beginning. Oracle will take your  HASHKEYS/trunc(blocksize/SIZE) and will allocate that space right away. As soon  as the first table is put in that cluster, any full scan will hit every allocated block.  This is different from every other table in this respect.   • The number of HASHKEYs in a hash cluster is a fixed size. You cannot change the  size of the hash table without a rebuild of the cluster. This does not in any way limit  the amount of data you can store in this cluster, it simply limits the number of  unique hash keys that can be generated for this cluster. That may affect  performance due to unintended hash collisions if it was set too low.   • Range scanning on the cluster key is not available. Predicates such as WHERE  cluster_key BETWEEN 50 AND 60 cannot use the hashing algorithm. There are an  infinite number of possible values between 50 and 60 ‐ the server would have to  generate them all in order to hash each one and see if there was any data there. This  is not possible. The cluster will be full scanned if you use a range on a cluster key  and have not indexed it using a conventional index.   Hash clusters are suitable when:  • You know with a good degree of accuracy how many rows the table will have over  its life, or if you have some reasonable upper bound. Getting the size of the  HASHKEYs and SIZE parameters right is crucial to avoid a rebuild. If the life of the  table is short (for example, a data mart/data warehouse), this is easy.   • DML, especially inserts, is light. Updates do not introduce significant overhead,  unless you update the HASHKEY, which would not be a good idea. That would  cause the row to migrate.   • You access the data by the HASHKEY value constantly. For example, you have a  table of parts, and part number accesses these parts. Lookup tables are especially  appropriate for hash clusters.   Nested Tables  Nested tables are part of the Object Relational Extensions to Oracle. A nested table, one of  the two collection types in Oracle, is very similar to a child table in a traditional  parent/child table pair in the relational model. It is an unordered set of data elements, all  of the same data type, which could either be a built‐in data type or an object data type. It  Expert one-on-one Oracle 307 goes one step further however, since it is designed to give the illusion that each row in the  parent table has its own child table. If there are 100 rows in the parent table, then there are  virtually 100 nested tables. Physically, there is only the single parent and the single child  table. There are large syntactic and semantic differences between nested tables and  parent/child tables as well, and weʹll look at those in this section.  There are two ways to use nested tables. One is in your PL/SQL code as a way to extend  the PL/SQL language. We cover this technique in Chapter 20 Using Object Relational  Features. The other is as a physical storage mechanism, for persistent storage of collections.  I personally use them in PL/SQL all of the time but very infrequently as a permanent  storage mechanism.  What I am going to do in this section is briefly introduce the syntax to create, query, and  modify nested tables. Then we will look at some of the implementation details, what is  important to know about how Oracle really stores them.  Nested Tables Syntax  The creation of a table with a nested table is fairly straightforward, it is the syntax for  manipulating them that gets a little complex. I will use the simple EMP and DEPT tables to  demonstrate. We are familiar with that little data model which is implemented relationally  as:   tkyte@TKYTE816> create table dept    2  (deptno number(2) primary key,    3   dname     varchar2(14),    4   loc       varchar2(13)    5  );  Table created.        tkyte@TKYTE816> create table emp    2  (empno       number(4) primary key,    3   ename       varchar2(10),    4   job         varchar2(9),    5   mgr         number(4) references emp,    6   hiredate    date,    7   sal         number(7, 2),    8   comm        number(7, 2),    9   deptno      number(2) references dept   10  );        Table created.  Expert one-on-one Oracle 308 with primary and foreign keys. We will do the equivalent implementation using a nested  table for the EMP table:   tkyte@TKYTE816> create or replace type emp_type    2  as object    3  (empno       number(4),    4   ename       varchar2(10),    5   job         varchar2(9),    6   mgr         number(4),    7   hiredate    date,    8   sal         number(7, 2),    9   comm        number(7, 2)   10  );   11  /        Type created.        tkyte@TKYTE816> create or replace type emp_tab_type    2  as table of emp_type    3  /        Type created.  In order to create a table with a nested table, we need a nested table type. The above code  creates a complex object type EMP_TYPE and a nested table type of that called  EMP_TAB_TYPE. In PL/SQL, this will be treated much like an array would. In SQL, it will  cause a physical nested table to be created. Here is the simple CREATE TABLE statement  that uses it:   tkyte@TKYTE816> create table dept_and_emp    2  (deptno number(2) primary key,    3   dname     varchar2(14),    4   loc       varchar2(13),    5   emps      emp_tab_type    6  )    7  nested table emps store as emps_nt;        Table created.        tkyte@TKYTE816> alter table emps_nt add constraint emps_empno_unique    2             unique(empno)    3  /        Expert one-on-one Oracle 309 Table altered.  The important part of this create table is the inclusion of the column EMPS of  EMP_TAB_TYPE and the corresponding NESTED TABLE EMPS STORE AS EMPS_NT.  This created a real physical table EMPS_NT separate from, and in addition to, the table  DEPT_AND_EMP. I added a constraint on the EMPNO column directly on the nested  table in order to make the EMPNO unique as it was in our original relational model. I  cannot implement our full data model. However, there is the self‐referencing constraint:   tkyte@TKYTE816> alter table emps_nt add constraint mgr_fk    2  foreign key(mgr) references emps_nt(empno);  alter table emps_nt add constraint mgr_fk  *  ERROR at line 1:  ORA‐30730: referential constraint not allowed on nested table column  This will simply not work. Nested tables do not support referential integrity constraints as  they cannot reference any other table, even itself. So, weʹll just skip that for now. Now, letʹs  populate this table with the existing EMP and DEPT data:   tkyte@TKYTE816> insert into dept_and_emp    2  select dept.*,    3     CAST( multiset( select empno, ename, job, mgr, hiredate, sal, comm    4                       from SCOTT.EMP    5                       where emp.deptno = dept.deptno ) AS emp_tab_type )    6    from SCOTT.DEPT    7  /        4 rows created.  There are two things to notice here:  • Only ʹfourʹ rows were created. There are really only four rows in the  DEPT_AND_EMP table. The 14 EMP rows donʹt really exist independently.   • The syntax is getting pretty exotic. CAST and MULTISET ‐ syntax most people have  never used. You will find lots of exotic syntax when dealing with object relational  components in the database. The MULTISET keyword is used to tell Oracle the  subquery is expected to return more then one row (subqueries in a SELECT list  have previously been limited to returning 1 row). The CAST is used to instruct  Oracle to treat the returned set as a collection type ‐ in this case we CAST the  MULTISET to be a EMP_TAB_TYPE. CAST is a general purpose routine not limited  in use to collections ‐ for example if you wanted to fetch the EMPNO column from  Expert one-on-one Oracle 310 EMP as a VARCHAR2(20) instead of a NUMBER(4) type, you may query: select  cast( empno as VARCHAR2(20) ) e from emp;   We are now ready to query the data. Letʹs see what one row might look like:   tkyte@TKYTE816> select deptno, dname, loc, d.emps AS employees    2  from dept_and_emp d    3  where deptno = 10    4  /            DEPTNO DNAME          LOC           EMPLOYEES(EMPNO, ENAME, JOB, M  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐          10 ACCOUNTING     NEW YORK      EMP_TAB_TYPE(EMP_TYPE(7782,                                          ʹCLARKʹ, ʹMANAGERʹ, 7839,                                          ʹ09‐JUN‐81ʹ, 2450, NULL),                                          EMP_TYPE(7839, ʹKINGʹ,                                          ʹPRESIDENTʹ, NULL,                                          ʹ17‐NOV‐81ʹ, 5000, NULL),                                          EMP_TYPE(7934, ʹMILLERʹ,                                          ʹCLERKʹ, 7782, ʹ23‐JAN‐82ʹ,                                          1300, NULL))  All of the data is there, in a single column. Most applications, unless they are specifically  written for the object relational features, will not be able to deal with this particular  column. For example, ODBC doesnʹt have a way to deal with a nested table (JDBC, OCI,  Pro*C, PL/SQL, and most other APIs, and languages do). For those cases, Oracle provides  a way to un‐nest a collection and treats it much like a relational table. For example:  tkyte@TKYTE816> select d.deptno, d.dname, emp.*    2  from dept_and_emp D, table(d.emps) emp    3  /        DEPTNO DNAME       EMPNO ENAME      JOB         MGR HIREDATE    SAL  COMM  ‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐ ‐‐‐‐‐      10 ACCOUNTING   7782 CLARK      MANAGER    7839 09‐JUN‐81  2450      10 ACCOUNTING   7839 KING       PRESIDENT       17‐NOV‐81  5000      10 ACCOUNTING   7934 MILLER     CLERK      7782 23‐JAN‐82  1300      20 RESEARCH     7369 SMITH      CLERK      7902 17‐DEC‐80   800      20 RESEARCH     7566 JONES      MANAGER    7839 02‐APR‐81  2975      20 RESEARCH     7788 SCOTT      ANALYST    7566 09‐DEC‐82  3000      20 RESEARCH     7876 ADAMS      CLERK      7788 12‐JAN‐83  1100      20 RESEARCH     7902 FORD       ANALYST    7566 03‐DEC‐81  3000      30 SALES        7499 ALLEN      SALESMAN   7698 20‐FEB‐81  1600   300  Expert one-on-one Oracle 311     30 SALES        7521 WARD       SALESMAN   7698 22‐FEB‐81  1250   500      30 SALES        7654 MARTIN     SALESMAN   7698 28‐SEP‐81  1250  1400      30 SALES        7698 BLAKE      MANAGER    7839 01‐MAY‐81  2850      30 SALES        7844 TURNER     SALESMAN   7698 08‐SEP‐81  1500     0      30 SALES        7900 JAMES      CLERK      7698 03‐DEC‐81   950        14 rows selected.  We are able to cast the EMPS column as a table and it naturally did the join for us ‐ no join  conditions were needed. In fact, since our EMP type doesnʹt have the DEPTNO column,  there is nothing for us apparently to join on. Oracle takes care of that nuance for us.  So, how can we update the data? Letʹs say you want to give department 10 a $100 bonus.  You would code the following:   tkyte@TKYTE816> update    2    table( select emps    3             from dept_and_emp    4                    where deptno = 10    5             )    6  set comm = 100    7  /        3 rows updated.  Here is where the ʹvirtually a table for every rowʹ comes into play. In the SELECT  predicate shown earlier, it may not have been obvious that there was a table per row,  especially since the joins and such arenʹt there, it looks a little like ʹmagicʹ. The UPDATE  statement however shows that there is a table per row. We selected a discrete table to  UPDATE; this table has no name, only a query to identify it. If we use a query that does  not SELECT exactly one table, we will receive:   tkyte@TKYTE816> update    2    table( select emps    3             from dept_and_emp    4               where deptno = 1    5        )    6  set comm = 100    7  /  update  *  ERROR at line 1:  ORA‐22908: reference to NULL table value  Expert one-on-one Oracle 312             tkyte@TKYTE816> update    2    table( select emps    3             from dept_and_emp    4               where deptno > 1    5        )    6  set comm = 100    7 /    table( select emps           *  ERROR at line 2:  ORA‐01427: single‐row subquery returns more than one row  If you return less then one row (one nested table instance), the update fails. Normally an  update of zero rows is OK but not in this case, it returns an error the same as if you left the  table name off of the update. If you return more then one row (more then one nested table  instance), the update fails. Normally an update of many rows is perfectly OK. This shows  that Oracle considers each row in the DEPT_AND_EMP table to point to another table, not  just another set of rows as the relational model does. This is the semantic difference  between a nested table and a parent/child relational table. In the nested table model, there  is one table per parent row. In the relational model, there is one set of rows per parent row.  This difference can make nested tables somewhat cumbersome to use at times. Consider  this model we are using, which provides a very nice view of the data from the perspective  of single department. It is a terrible model if you want to ask questions like ʹwhat  department does KING work for?ʹ, ʹhow many accountants do I have working for me?ʹ,  and so on. These questions are best asked of the EMP relational table but in this nested  table model we can only access the EMP data via the DEPT data. We must always join, we  cannot query the EMP data alone. Well, we canʹt do it in a supported, documented method  ‐ we can use a trick (more on this trick later). If we needed to update every row in the  EMPS_NT, we would have to do 4 updates; once each for the rows in DEPT_AND_EMP to  update the virtual table associated with each row.  Another thing to consider is that when we updated the employee data for department 10,  we were semantically updating the EMPS column in the DEPT_AND_EMP table.  Physically, we understand there are two tables involved but semantically there is only one.  Even though we updated no data in the department table, the row that contains the nested  table we did modify is locked from update by other sessions. In a traditional parent/child  table relationship, this would not be the case.   These are the reasons why I tend to stay away from nested tables as a persistent storage  mechanism. It is the rare child table that is not queried standalone. In the above, the EMP  table should be a strong entity. It stands alone, and so, it needs to be queried alone. I find  Expert one-on-one Oracle 313 this to be the case almost all of the time. I tend to use nested tables via views on relational  tables. Weʹll investigate this in Chapter 20 on Using Object Relational Features.  So, now that we have seen how to update a nested table instance, inserting and deleting  are pretty straightforward. Letʹs add a row to the nested table instance department 10 and  remove a row from department 20:   tkyte@TKYTE816> insert into table    2  ( select emps from dept_and_emp where deptno = 10 )    3  values    4  ( 1234, ʹNewEmpʹ, ʹCLERKʹ, 7782, sysdate, 1200, null );        1 row created.        tkyte@TKYTE816> delete from table    2 ( select emps from dept_and_emp where deptno = 20 )    3  where ename = ʹSCOTTʹ;        1 row deleted.        tkyte@TKYTE816> select d.dname, e.empno, ename    2  from dept_and_emp d, table(d.emps) e    3  where d.deptno in ( 10, 20 );        DNAME               EMPNO ENAME  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  RESEARCH             7369 SMITH  RESEARCH             7566 JONES  RESEARCH             7876 ADAMS  RESEARCH             7902 FORD  ACCOUNTING           7782 CLARK  ACCOUNTING           7839 KING  ACCOUNTING           7934 MILLER  ACCOUNTING           1234 NewEmp        8 rows selected.  So, that is the basic syntax of how to query and modify nested tables. You will find many  times that you must un‐nest these tables as I have above, especially in queries, to make use  of them. Once you conceptually visualize the ʹvirtual table per rowʹ concept, working with  nested tables becomes much easier.  Expert one-on-one Oracle 314 Previously I stated: ʹWe must always join, we cannot query the EMP data aloneʹ but then  followed that up with a caveat: ʹyou can if you really need toʹ. It is undocumented and not  supported, so use it only as a last ditch method. Where it will come in most handy is if you  ever need to mass update the nested table (remember, we would have to do that through  the DEPT table with a join). There is an undocumented hint, NESTED_TABLE_GET_REFS,  used by EXP and IMP to deal with nested tables. It will also be a way to see a little more  about the physical structure of the nested tables. This magic hint is easy to discover after  you export a table with a nested table. I exported the table above, in order to get its ʹlargerʹ  definition from IMP. After doing the export, I found the following SQL in my shared pool  (V$SQL table):   SELECT /*+NESTED_TABLE_GET_REFS+*/ NESTED_TABLE_ID,SYS_NC_ROWINFO$  FROM  ʺTKYTEʺ.ʺEMPS_NTʺ  A simple query like SELECT SQL_TEXT FROM V$SQL WHERE UPPER(SQL_TEXT) LIKE  ʹ%EMP% found it for me. If you run this, youʹll get some ʹmagicʹ results:   tkyte@TKYTE816> SELECT /*+NESTED_TABLE_GET_REFS+*/    2         NESTED_TABLE_ID,SYS_NC_ROWINFO$    3  FROM ʺTKYTEʺ.ʺEMPS_NTʺ    4  /        NESTED_TABLE_ID                  SYS_NC_ROWINFO$(EMPNO, ENAME,  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  9A39835005B149859735617476C9A80E EMP_TYPE(7782, ʹCLARKʹ,                                   ʹMANAGERʹ, 7839, ʹ09‐JUN‐81ʹ,                                   2450, 100)        9A39835005B149859735617476C9A80E EMP_TYPE(7839, ʹKINGʹ,                                   ʹPRESIDENTʹ, NULL,                                   ʹ17‐NOV‐81ʹ, 5000, 100)  Well, this is somewhat surprising, if you describe this table:  tkyte@TKYTE816> desc emps_nt   Name                                Null?    Type   ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐   EMPNO                                        NUMBER(4)   ENAME                                        VARCHAR2(10)   JOB                                          VARCHAR2(9)   MGR                                          NUMBER(4)   HIREDATE                                     DATE  Expert one-on-one Oracle 315  SAL                                          NUMBER(7,2)   COMM                                         NUMBER(7,2)  These two columns donʹt even show up. They are part of the hidden implementation of  nested tables. The NESTED_TABLE_ID is really a foreign key to the parent table  DEPT_AND_EMP. DEPT_AND_EMP which actually has a hidden column in it that is  used to join to EMPS_NT. The SYS_NC_ROWINF$ ʹcolumnʹ is a magic column, it is more  of a function than a column. The nested table here is really an object table (it is made of an  object type) and SYS_NC_INFO$ is the internal way Oracle references the row as an object,  instead of referencing each of the scalar columns. Under the covers, all Oracle has done for  us is to implement a parent/child table with system generated primary and foreign keys. If  we dig a little further, we can query the ʹrealʹ data dictionary to see all of the columns in  the DEPT_AND_EMP table:   tkyte@TKYTE816> select name    2    from sys.col$    3   where obj# = ( select object_id    4                    from user_objects    5                   where object_name = ʹDEPT_AND_EMPʹ )    6  /        NAME  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  DEPTNO  DNAME  LOC  EMPS  SYS_NC0000400005$              tkyte@TKYTE816> select SYS_NC0000400005$ from dept_and_emp;        SYS_NC0000400005$  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  9A39835005B149859735617476C9A80E  A7140089B1954B39B73347EC20190D68  20D4AA0839FB49B0975FBDE367842E16  56350C866BA24ADE8CF9E47073C52296  The weird looking column name, SYS_NC0000400005$, is the system‐generated key placed  into the DEPT_AND_EMP table. If you dig even further you will find that Oracle has  placed a unique index on this column. Unfortunately however, it neglected to index the  NESTED_TABLE_ID in EMPS_NT. This column really needs to be indexed, as we are  Expert one-on-one Oracle 316 always joining from DEPT_AND_EMP to EMPS_NT. This is an important thing to  remember about nested tables if you use them with all of the defaults as I did above,  always index the NESTED_TABLE_ID in the nested tables!  Iʹve gotten off of the track though at this point. I was talking about how to treat the nested  table as if it were a real table. The NESTED_TABLE_GET_REFS hint does that for us. We  can use that like this:   tkyte@TKYTE816> select /*+ nested_table_get_refs */ empno, ename    2  from emps_nt where ename like ʹ%A%ʹ;             EMPNO ENAME  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐        7782 CLARK        7876 ADAMS        7499 ALLEN        7521 WARD        7654 MARTIN        7698 BLAKE        7900 JAMES  7 rows selected.        tkyte@TKYTE816> update /*+ nested_table_get_refs */ emps_nt    2  set ename = initcap(ename);        14 rows updated.        tkyte@TKYTE816> select /*+ nested_table_get_refs */ empno, ename    2  from emps_nt where ename like ʹ%a%ʹ;             EMPNO ENAME  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐        7782 Clark        7876 Adams        7521 Ward        7654 Martin        7698 Blake        7900 James  6 rows selected.  Again, this is not a documented supported feature. It may not work in all environments. It  has a specific functionality ‐ for EXP and IMP to work. This is the only environment it is  assured to work in. Use it at your own risk. Use it with caution though, and do not put it  Expert one-on-one Oracle 317 into production code. Use it for one‐off fixes of data or to see what is in the nested table  out of curiosity. The supported way to report on the data is to un‐nest it like this:   tkyte@TKYTE816> select d.deptno, d.dname, emp.*    2  from dept_and_emp D, table(d.emps) emp    3  /  This is what you should use in queries and production code.  Nested Table Storage  We have already seen some of the storage of the nested table structure. Weʹll take a more  in‐depth look at the structure created by Oracle by default, and what sort of control over  that we have. Working with the same create statement from above:  tkyte@TKYTE816> create table dept_and_emp    2  (deptno number(2) primary key,    3   dname     varchar2(14),    4   loc       varchar2(13),    5   emps      emp_tab_type    6  )    7  nested table emps store as emps_nt;        Table created.        tkyte@TKYTE816> alter table emps_nt add constraint emps_empno_unique    2             unique(empno)    3  /        Table altered.  We know that Oracle really creates a structure like this:  Expert one-on-one Oracle 318   The code created two real tables. The table we asked to have is there but it has an extra  hidden column (weʹll have one extra hidden column by default for each nested table  column in a table). It also created a unique constraint on this hidden column. Oracle  created the nested table for us ‐ EMPS_NT. This table has two hidden columns, one that is  not really a column, SYS_NC_ROWINFO$, but really a virtual column that returns all of  the scalar elements as an object. The other is the foreign key, called NESTED_TABLE_ID,  which can be joined back to the parent table. Notice the lack of an index on this column!  Finally, Oracle added an index on the DEPTNO column in the DEPT_AND_EMP table in  order to enforce the primary key. So, we asked for a table and got a lot more then we  bargained for. If you look at it, it is a lot like what you might create for a parent/child  relationship, but we would have used the existing primary key on DEPTNO as the foreign  key in EMPS_NT instead of generating a surrogate RAW(16) key.  If we look at the EXP/IMP dump of our nested table example, we see the following:   CREATE TABLE ʺTKYTEʺ.ʺDEPT_AND_EMPʺ  (ʺDEPTNOʺ NUMBER(2, 0),   ʺDNAMEʺ  VARCHAR2(14),   ʺLOCʺ    VARCHAR2(13),   ʺEMPSʺ ʺEMP_TAB_TYPEʺ)  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 LOGGING  STORAGE(INITIAL 131072 NEXT 131072          MINEXTENTS 1 MAXEXTENTS 4096          PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1          BUFFER_POOL DEFAULT)  TABLESPACE ʺUSERSʺ  NESTED TABLE ʺEMPSʺ     STORE AS ʺEMPS_NTʺ     RETURN AS VALUE  Expert one-on-one Oracle 319 The only new thing we notice here so far is the RETURN AS VALUE. It is used to describe  how the nested table is returned to a client application. By default, Oracle will return the  nested table by value to the client ‐ the actual data will be transmitted with each row. This  can also be set to RETURN AS LOCATOR meaning the client will get a pointer to the data,  not the data itself. If, and only if, the client de‐references this pointer will the data be  transmitted to it. So, if you believe the client will typically not look at the rows of a nested  table for each parent row, you can return a locator instead of the values, saving on the  network round trips. For example, if you have a client application that displays the lists of  departments and when the user double clicks on a department it shows the employee  information, you may consider using the locator. This is because the details are usually not  looked at ‐ it is the exception, not the rule.  So, what else can we do with the nested table? Firstly, the NESTED_TABLE_ID column  must be indexed. Since we always access the nested table from the parent to the child, we  really need that index. We can index that column using the create index but a better  solution is to use an index organized table to store the nested table. The nested table is  another perfect example of what an IOT is excellent for. It will physically store the child  rows co‐located by NESTED_TABLE_ID (so retrieving the table is done with less physical  I/O). It will remove the need for the redundant index on the RAW(16) column. Going one  step further, since the NESTED_TABLE_ID will be the leading column in the IOTʹs  primary key, we should also incorporate index key compression to suppress the  redundant NESTED_TABLE_ID s that would be there otherwise. In addition, we can  incorporate our UNIQUE and NOT NULL constraint on the EMPNO column into the  CREATE TABLE command. Therefore, if I take the above CREATE TABLE and modify it  slightly:   CREATE TABLE ʺTKYTEʺ.ʺDEPT_AND_EMPʺ  (ʺDEPTNOʺ NUMBER(2, 0),   ʺDNAMEʺ  VARCHAR2(14),   ʺLOCʺ    VARCHAR2(13),   ʺEMPSʺ ʺEMP_TAB_TYPEʺ)  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 LOGGING  STORAGE(INITIAL 131072 NEXT 131072          MINEXTENTS 1 MAXEXTENTS 4096          PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1          BUFFER_POOL DEFAULT)  TABLESPACE ʺUSERSʺ  NESTED TABLE ʺEMPSʺ     STORE AS ʺEMPS_NTʺ     ( (empno NOT NULL, unique (empno), primary key(nested_table_id,empno))       organization index compress 1 )     RETURN AS VALUE  /  Expert one-on-one Oracle 320 and now we get the following set of objects. Instead of having a conventional table  EMP_NT, we now have an IOT EMPS_NT as signified by the index structure overlaid on  the table below:    Where the EMPS_NT is an IOT using compression, this should take less storage than the  original default nested table and it has the index we badly need.  Nested Tables Wrap‐up  I do not use nested tables as a permanent storage mechanism myself, and this is for the  following reasons:  • The overhead of the RAW(16) columns that are added. Both the parent and child  table will have this extra column. The parent table will have an extra 16 byte RAW  for each nested table column it has. Since the parent table typically already has a  primary key (DEPTNO in my examples) it would make sense to use this in the child  tables, not a system generated key.   • The overhead of the unique constraint on the parent table, when it already typically  has a unique constraint.   • The nested table is not easily used by itself, without using unsupported constructs  (NESTED_TABLE_GET_REFS). It can be un‐nested for queries but not mass  updates.   I do use nested tables heavily as a programming construct and in views. This is where I  believe they are in their element and in Chapter 20 on Using Object Relational Features we  see how to exploit them in this fashion. As a storage mechanism, I would much prefer  creating the parent/child tables myself. After creating the parent/child tables we can in fact  create a view that makes it appear as if we had a real nested table. That is, we can achieve  all of the advantages of the nested table construct without incurring the overhead. Again  Expert one-on-one Oracle 321 in Chapter 20 Using Object Relational Features weʹll take a detailed look at how to  accomplish this.  If you do use them as a storage mechanism, be sure to make the nested table an index  organized table to avoid the overhead of an index on the NESTED_TABLE_ID and the  nested table itself. See the section above on IOTs for advice on setting them up with  overflow segments and other options. If you do not use an IOT, make sure then to create  an index on the NESTED_TABLE_ID column in the nested table to avoid full scanning it to  find the child rows.  Temporary Tables  Temporary tables are used to hold intermediate resultsets, either for the duration of a  transaction or a session. The data held in a temporary table is only ever visible to the  current session ‐ no other session will ever see any other sessionʹs data, even if the current  session COMMITs the data. Multi‐user concurrency is not an issue with regards to  temporary tables either, one session can never block another session by using a temporary  table. Even if we ʹlockʹ the temporary table, it will not prevent other sessions using their  temporary table As we observed in Chapter 3 on Redo and Rollback, temporary tables  generate significantly less REDO then regular tables would. However, since they must  generate UNDO information for the data they contain, they will generate some amount of  REDO Log. UPDATEs and DELETEs will generate the largest amount; INSERTs and  SELECTs the least amount.  Temporary tables will allocate storage from the currently logged in users temporary  tablespace, or if they are accessed from a definers rights procedure, the temporary  tablespace of the owner of that procedure will be used. A global temporary table is really  just a template for the table itself. The act of creating a temporary table involves no storage  allocation; no INITIAL extent is allocated, as it would be for a non‐temporary table. Rather,  at runtime when a session first puts data into the temporary table a temporary segment for  that session will be created at that time. Since each session gets its own temporary segment,  (not just an extent of an existing segment) every user might be allocating space for their  temporary table in different tablespaces. USER1 might have their temporary tablespace set  to TEMP1 ‐ their temporary tables will be allocated from this space. USER2 might have  TEMP2 as their temporary tablespace and their temporary tables will be allocated there.  Oracleʹs temporary tables are similar to temporary tables in other relational databases with  the main exception being that they are ʹstaticallyʹ defined. You create them once per  database, not once per stored procedure in the database. They always exist ‐ they will be in  the data dictionary as objects, but will always appear empty until your session puts data  into them. The fact that they are statically defined allows us to create views that reference  temporary tables, to create stored procedures that use static SQL to reference them, and so  on.  Expert one-on-one Oracle 322 Temporary tables may be session‐based (data survives in the table across commits but not a  disconnect/reconnect). They may also be transaction‐based (data disappears after a  commit). Here is an example showing the behavior of both. I used the SCOTT.EMP table  as a template:   tkyte@TKYTE816> create global temporary table temp_table_session    2  on commit preserve rows    3  as    4  select * from scott.emp where 1=0    5  /        Table created.  The ON COMMIT PRESERVE ROWS clause makes this a session‐based temporary table.  Rows will stay in this table until my session disconnects or I physically remove them via a  DELETE or TRUNCATE. Only my session can see these rows; no other session will ever  see ʹmyʹ rows even after I COMMIT:   tkyte@TKYTE816> create global temporary table temp_table_transaction    2  on commit delete rows    3  as    4  select * from scott.emp where 1=0    5  /        Table created.  The ON COMMIT DELETE ROWS makes this a transaction‐based temporary table. When  your session commits, the rows disappear. The rows will disappear by simply giving back  the temporary extents allocated to our table ‐ there is no overhead involved in the  automatic clearing of temporary tables. Now, letʹs look at the differences between the two  types:   tkyte@TKYTE816> insert into temp_table_session select * from scott.emp;        14 rows created.        tkyte@TKYTE816> insert into temp_table_transaction select * from scott.emp;        14 rows created.  Weʹve just put 14 rows into each temp table and this shows we can ʹseeʹ them:  tkyte@TKYTE816> select session_cnt, transaction_cnt    2    from ( select count(*) session_cnt from temp_table_session ),  Expert one-on-one Oracle 323   3         ( select count(*) transaction_cnt from temp_table_transaction );        SESSION_CNT TRANSACTION_CNT  ‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐           14              14        tkyte@TKYTE816> commit;  Since weʹve committed, weʹll see the session‐based rows but not the transaction‐based  rows:   tkyte@TKYTE816> select session_cnt, transaction_cnt    2    from ( select count(*) session_cnt from temp_table_session ),    3         ( select count(*) transaction_cnt from temp_table_transaction );        SESSION_CNT TRANSACTION_CNT  ‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐           14               0        tkyte@TKYTE816> disconnect  Disconnected from Oracle8i Enterprise Edition Release 8.1.6.0.0 ‐ Production  With the Partitioning option  JServer Release 8.1.6.0.0 ‐ Production  tkyte@TKYTE816> connect tkyte/tkyte  Connected.  Since weʹve started a new session, weʹll see no rows in either table:  tkyte@TKYTE816> select session_cnt, transaction_cnt    2    from ( select count(*) session_cnt from temp_table_session ),    3         ( select count(*) transaction_cnt from temp_table_transaction );        SESSION_CNT TRANSACTION_CNT  ‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐            0               0  If you have experience of temporary tables in SQL Server and/or Sybase, the major  consideration for you is that instead of executing select x, y, z into #temp from some_table  to dynamically create and populate a temporary table, you will:  • Once per database, create all of your TEMP tables as a global temporary table. This  will be done as part of the application install, just like creating your permanent  tables.   Expert one-on-one Oracle 324 • In your procedures simply insert into temp (x,y,z) select x,y,y from some_table.   Just to drive home the point, the goal here is to not create tables in your stored procedures  at runtime. That is not the proper way to do this in Oracle. DDL is an expensive operation,  we want to avoid doing that at runtime. The temporary tables for an application should be  created during the application installation never at run‐time.  Temporary tables can have many of the attributes of a permanent table. They may have  triggers, check constraints, indexes, and so on. Features of permanent tables that they do  not support include:  • They cannot have referential integrity constraints ‐ they can neither be the target of a  foreign key, nor may they have a foreign key defined on them.   • They cannot have VARRAY or NESTED TABLE type columns.   • They cannot be indexed organized tables.   • They cannot be in an index or hash cluster.   • They cannot be partitioned.   • They cannot have statistics generated via the ANALYZE table command.   One of the drawbacks of a temporary table in any database, is the fact that the optimizer  has no real statistics on it. When using the Cost‐Based Optimizer (CBO), valid statistics  are vital to the optimizerʹs success (or failure). In the absence of statistics, the optimizer  will make guesses as to the distribution of data, the amount of data, the selectivity of an  index. When these guesses are wrong, the query plans generated for queries that make  heavy use of temporary tables could be less than optimal. In many cases, the correct  solution is to not use a temporary table at all, but rather to use an INLINE VIEW (for an  example of an INLINE VIEW refer to the last SELECT we ran above ‐ it has two of them)  in its place. In this fashion, Oracle will have access to all of the relevant statistics for a table  and can come up with an optimal plan.  I find many times people use temporary tables because they learned in other databases  that joining too many tables in a single query is a ʹbad thingʹ. This is a practice that must be  unlearned for Oracle development. Rather then trying to out‐smart the optimizer and  breaking what should be a single query into three or four queries that store their sub  results into temporary tables and then joining the temporary tables, you should just code a  single query that answers the original question. Referencing many tables in a single query  is OK; the temporary table crutch is not needed in Oracle for this purpose.  In other cases however, the use of a temporary table in a process is the correct approach.  For example, I recently wrote a Palm Sync application to synchronize the date book on a  Palm Pilot with calendar information stored in Oracle. The Palm gives me a list of all  records that have been modified since the last hot synchronization. I must take these  records and compare them against the live data in the database, update the database  Expert one-on-one Oracle 325 records and then generate a list of changes to be applied to the Palm. This is a perfect  example of when a temporary table is very useful. I used a temporary table to store the  changes from the Palm in the database. I then ran a stored procedure that bumps the palm  generated changes against the live (and very large) permanent tables to discover what  changes need to be made to the Oracle data and then to find the changes that need to come  from Oracle back down to the Palm. I have to make a couple of passes on this data, first I  find all records that were modified only on the Palm and make the corresponding changes  in Oracle. I then find all records that were modified on both the Palm and my database  since the last synchronization and rectify them. Then I find all records that were modified  only on the database and place their changes into the temporary table. Lastly, the Palm  sync application pulls the changes from the temporary table and applies them to the Palm  device itself. Upon disconnection, the temporary data goes away.  The issue I encountered however is that because the permanent tables were analyzed, the  CBO was being used. The temporary table had no statistics on it (you can analyze the  temporary table but no statistics are gathered) and the CBO would ʹguessʹ many things  about it. I, as the developer, knew the average number of rows you might expect, the  distribution of the data, the selectivity of the indexes and so on. I needed a way to inform  the optimizer of these better guesses. The DBMS_STATS package is a great way to do this.  Since the ANALYZE command does not collect statistics on a temporary table, we must  use a manual process to populate the data dictionary with representative statistics for our  temporary tables. For example, if on average the number of rows in the temporary table  will be 500, the average row size will be 100 bytes and the number of blocks will be 7, we  could simply use:   tkyte@TKYTE816> begin    2     dbms_stats.set_table_stats( ownname => USER,    3                                 tabname => ʹTʹ,    4                                 numrows => 500,    5                                 numblks => 7,    6                                 avgrlen => 100 );    7  end;    8  /        PL/SQL procedure successfully completed.  tkyte@TKYTE816> select table_name, num_rows, blocks, avg_row_len    2               from user_tables    3              where table_name = ʹTʹ;        TABLE_NAME                       NUM_ROWS     BLOCKS AVG_ROW_LEN  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐  T                                     500          7         100  Expert one-on-one Oracle 326 Now, the optimizer wonʹt use its best guess, it will use our best guess for this information.  Going a step further, we can use Oracle to set the statistics to an even greater level of detail.  The following example shows the use of a temporary table with the CBO. The query plan  generated without statistics is sub‐optimal; the CBO chose to use an index when it should  not have. It did that because it assumed default information about index selectivity,  number of rows in the table and number of rows to be returned and such. What I did to  correct this was to drop the temporary table for a moment, create a permanent table of the  same name and structure and populated it with representative data. I then analyzed this  table as thoroughly as I wanted to (I could generate histograms and so on as well) and  used DBMS_STATS to export the statistics for this permanent table. I then dropped the  permanent table and recreated my temporary table. All I needed to do then was import  my representative statistics and the optimizer did the right thing:   tkyte@TKYTE816> create global temporary table temp_all_objects    2  as    3  select * from all_objects where 1=0    4  /        Table created.        tkyte@TKYTE816> create index temp_all_objects_idx on temp_all_objects(object_id)    2  /        Index created.        tkyte@TKYTE816> insert into temp_all_objects    2  select * from all_objects where rownum < 51    3  /        50 rows created.        tkyte@TKYTE816> set autotrace on explain  tkyte@TKYTE816> select /*+ ALL_ROWS */ object_type, count(*)    2    FROM temp_all_objects    3   where object_id < 50000    4    group by object_type    5  /        OBJECT_TYPE          COUNT(*)  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  JAVA CLASS                 50              Expert one-on-one Oracle 327 Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=HINT: ALL_ROWS (Cost=13 Card=409     1    0   SORT (GROUP BY) (Cost=13 Card=409 Bytes=9816)     2    1     TABLE ACCESS (BY INDEX ROWID) OF ʹTEMP_ALL_OBJECTSʹ (Cost=10     3    2       INDEX (RANGE SCAN) OF ʹTEMP_ALL_OBJECTS_IDXʹ (NON‐UNIQUE)        tkyte@TKYTE816> set autotrace off  This shows that the CBO did the wrong thing. Any time you access more than 10‐20  percent of the table, you should not use an index. Here, we accessed 100 percent of the  table; in fact the table is so small that using the index in this case is not buying us anything  at all. Here is how to give the optimizer the information it needs to develop the correct  plan:  tkyte@TKYTE816> drop table temp_all_objects;        Table dropped.        tkyte@TKYTE816> create table temp_all_objects    2  as    3  select * from all_objects where 1=0    4  /        Table created.        tkyte@TKYTE816> create index temp_all_objects_idx on temp_all_objects(object_id)    2  /        Index created.        tkyte@TKYTE816> insert into temp_all_objects    2  select * from all_objects where rownum < 51;        50 rows created.        tkyte@TKYTE816> analyze table temp_all_objects compute statistics;        Table analyzed.        tkyte@TKYTE816> analyze table temp_all_objects compute statistics for all    2  indexes;        Expert one-on-one Oracle 328 Table analyzed.  What I have done is created a permanent table that looks just like the temporary table. I  populated it with representative data. That is the tricky part here; you must carefully  consider what you put into this table when you analyze it. You will be overriding the  optimizerʹs best guess with this data so you had better be giving it better data than it can  make up itself. In some cases, it might be enough to just set the table or index statistics  manually, as I did above to inform the CBO as the to the cardinality, and range of values.  In other cases, you may need to add many pieces of information to the data dictionary in  order to give the CBO the data it needs. Instead of manually adding this data, we can let  Oracle do the work for us. The method below gets all of the information that you can set  easily:   tkyte@TKYTE816> begin    2      dbms_stats.create_stat_table( ownname => USER,    3                                    stattab => ʹSTATSʹ );    4    5      dbms_stats.export_table_stats( ownname => USER,    6                                     tabname => ʹTEMP_ALL_OBJECTSʹ,    7                                     stattab => ʹSTATSʹ );    8      dbms_stats.export_index_stats( ownname => USER,    9                                     indname => ʹTEMP_ALL_OBJECTS_IDXʹ,   10                                     stattab => ʹSTATSʹ );   11  end;   12  /        PL/SQL procedure successfully completed.        tkyte@TKYTE816> drop table temp_all_objects;  Table dropped.        tkyte@TKYTE816> create global temporary table temp_all_objects    2  as    3  select * from all_objects where 1=0    4  /        Table created.        tkyte@TKYTE816> create index temp_all_objects_idx on temp_all_objects(object_id)    2  /        Index created.        Expert one-on-one Oracle 329 tkyte@TKYTE816> begin    2      dbms_stats.import_table_stats( ownname => USER,    3                                     tabname => ʹTEMP_ALL_OBJECTSʹ,    4                                     stattab => ʹSTATSʹ );    5      dbms_stats.import_index_stats( ownname => USER,    6                                     indname => ʹTEMP_ALL_OBJECTS_IDXʹ,    7                                     stattab => ʹSTATSʹ );    8  end;    9  /        PL/SQL procedure successfully completed.  Weʹve just put statistics in our temporary table, based on our representative result set. The  CBO will now use this to make decisions about the plans based on that table as evidenced  by the next query:   tkyte@TKYTE816> insert into temp_all_objects    2  select * from all_objects where rownum < 51    3  /        50 rows created.        tkyte@TKYTE816> set autotrace on  tkyte@TKYTE816> select /*+ ALL_ROWS */ object_type, count(*)    2    FROM temp_all_objects    3   where object_id < 50000    4    group by object_type    5  /        OBJECT_TYPE          COUNT(*)  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐  JAVA CLASS                 50  Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=HINT: ALL_ROWS (Cost=3 Card=1 Bytes=14)     1    0   SORT (GROUP BY) (Cost=3 Card=1 Bytes=14)     2    1     TABLE ACCESS (FULL) OF ʹTEMP_ALL_OBJECTSʹ (Cost=1 Card=50  Temporary Table Wrap‐up  Temporary tables can be useful in an application where you need to temporarily store a  set of rows to be processed against other tables, either for a session or a transaction. They  are not meant to be used as a means to take a single larger query and ʹbreak it upʹ into  Expert one-on-one Oracle 330 smaller result sets that would be joined back together (which seems to be the most popular  use of temporary tables in other databases). In fact, you will find in almost all cases that a  single query broken up into smaller temporary table queries, performs more slowly in  Oracle than the single query would have. Iʹve seen this behavior time and time again,  when given the opportunity to write the series of INSERTs into temporary tables as  SELECTs in the form of one large query, it goes much faster.  Temporary tables generate a minimum amount of REDO, however, they still generate  some REDO and there is no way to disable that. The REDO is generated for the rollback  data and in most typical uses will be negligible. If you only INSERT and SELECT from  temporary tables, the amount of REDO generated will not be noticeable. Only if you  DELETE or UPDATE a temporary table heavily will you see large amounts of redo  generated.  Statistics used by the CBO cannot be generated on a temporary table, however, a better  guess set of statistics may be set on a temporary table using the DBMS_STATS package.  You may either set a few of the relevant statistics such as the number of rows, average row  length, and so on, or you may use a permanent table populated with representative data to  generate a complete set. One word of caution, make sure your guess is better than the  default guess, otherwise the query plans that the CBO generates will be even worse than  before.  Object Tables  We have already seen a partial example of an object table above with nested tables. An  object table is a table that is created based on a TYPE, not as a collection of columns.  Normally, a CREATE TABLE would look like this:   create table t ( x int, y date, z varchar2(25);  An object table creation statement looks more like this:  create table t of Some_Type;  The attributes (columns) of t are derived from the definition of SOME_TYPE. Letʹs look at  a quick example involving a couple of types and review the resulting data structures:   tkyte@TKYTE816> create or replace type address_type    2  as object    3  ( city    varchar2(30),    4    street  varchar2(30),    5    state   varchar2(2),    6    zip     number  Expert one-on-one Oracle 331   7  )    8  /        Type created.        tkyte@TKYTE816> create or replace type person_type    2  as object    3  ( name             varchar2(30),    4    dob              date,    5    home_address     address_type,    6    work_address     address_type    7  )    8  /        Type created.        tkyte@TKYTE816> create table people of person_type    2  /        Table created.        tkyte@TKYTE816> desc people   Name              Null?    Type   ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐   NAME                       VARCHAR2(30)   DOB                        DATE   HOME_ADDRESS               ADDRESS_TYPE   WORK_ADDRESS               ADDRESS_TYPE  So, in a nutshell thatʹs all there is to it. You create some type definitions and then you can  create tables of that type. The table appears to have four columns representing the four  attributes of the PERSON_TYPE we created. We are at the point where we can now  perform DML on the object table to create and query data:   tkyte@TKYTE816> insert into people values ( ʹTomʹ, ʹ15‐mar‐1965ʹ,    2  address_type( ʹRestonʹ, ʹ123 Main Streetʹ, ʹVaʹ, ʹ45678ʹ ),    3  address_type( ʹRedwoodʹ, ʹ1 Oracle Wayʹ, ʹCaʹ, ʹ23456ʹ ) );        1 row created.        tkyte@TKYTE816> select * from people;        NAME DOB       HOME_ADDRESS(CITY, S WORK_ADDRESS(CI  Expert one-on-one Oracle 332 ‐‐‐‐ ‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  Tom  15‐MAR‐65 ADDRESS_TYPE(ʹReston   ADDRESS_TYPE(ʹR                 ʹ, ʹ123 Main          edwoodʹ, ʹ1                 Streetʹ, ʹVaʹ,       Oracle Wayʹ,                 45678)               ʹCaʹ, 23456)        tkyte@TKYTE816> select name, p.home_address.city from people p;        NAME HOME_ADDRESS.CITY  ‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  Tom  Reston  You are starting to see some of the object syntax necessary to deal with object types. For  example, in the INSERT statement we had to wrap the HOME_ADDRESS and  WORK_ADDRESS with a CAST. We cast the scalar values to be of an ADDRESS_TYPE.  Another way of saying this is that we create an ADDRESS_TYPE instance for that row by  using the default constructor for the ADDRESS_TYPE object.  Now, as far as the external face of the table is concerned, there are four columns in our  table. By now, after seeing the hidden magic that took place for the nested tables, we can  probably guess that there is something else going on. Oracle stores all object relational  data in plain old relational tables ‐ at the end of the day it is all in rows and columns. If we  dig into the ʹrealʹ data dictionary, we can see what this table really looks like:  tkyte@TKYTE816> select name, segcollength    2    from sys.col$    3   where obj# = ( select object_id    4                    from user_objects    5                   where object_name = ʹPEOPLEʹ )    6  /        NAME                 SEGCOLLENGTH  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐  SYS_NC_OID$                    16  SYS_NC_ROWINFO$                 1  NAME                           30  DOB                             7  HOME_ADDRESS                    1  SYS_NC00006$                   30  SYS_NC00007$                   30  SYS_NC00008$                    2  SYS_NC00009$                   22  WORK_ADDRESS                    1  Expert one-on-one Oracle 333 SYS_NC00011$                   30  SYS_NC00012$                   30  SYS_NC00013$                    2  SYS_NC00014$                   22        14 rows selected.  This looks quite different from what describe tells us. Apparently, there are 14 columns in  this table, not 4. In this case they are:  • SYS_NC_OID$ ‐ This is the system‐generated object ID of the table. It is a unique  RAW(16) column. It has a unique constraint on it ‐ there is a corresponding unique  index created on it as well.   • SYS_NC_ROWINFO ‐ This is the same ʹmagicʹ function as we observed with the  nested table. If we select that from the table it returns the entire row as a single  column.:  • tkyte@TKYTE816> select sys_nc_rowinfo$ from people;  •       • SYS_NC_ROWINFO$(NAME, DOB, HOME_ADDRESS(CITY, STREET, STATE,  ZIP),…  •  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  • PERSON_TYPE(ʹTomʹ, ʹ15‐MAR‐65ʹ, ADDRESS_TYPE(ʹLeesburgʹ, ʹ1234 Main  Streetʹ, ʹVaʹ,  • 20175), ADDRESS_TYPE(ʹRestonʹ, ʹ1910 Oracle Wayʹ, ʹVaʹ, 20190))  • NAME, DOB ‐ These are the scalar attributes of our object table. They are stored  much as you would expect, as regular columns   • HOME_ADDRESS, WORK_ADDRESS ‐ These are ʹmagicʹ functions as well, they  return the collection of columns they represent as a single object. These consume no  real space except to signify NULL or NOT NULL for the entity.   • SYS_NCnnnnn$ ‐ These are the scalar implementations of our embedded object  types. Since the PERSON_TYPE had the ADDRESS_TYPE embedded in it, Oracle  needed to make room to store them in the appropriate type of columns. The  system‐generated names are necessary since a column name must be unique and  there is nothing stopping us from using the same object type more then once as we  did here. If the names were not generated, we would have ended up with the ZIP  column twice.   So, just like with the nested table, there is lots going on here. A pseudo primary key of 16  bytes was added, there are virtual columns, and an index created for us. We can change  the default behavior with regards to the value of the object identifier assigned to an object,  as weʹll see in a moment. First, letʹs look at the full verbose SQL that would generate our  table for us, again this was generated using EXP/IMP:   Expert one-on-one Oracle 334 CREATE TABLE ʺTKYTEʺ.ʺPEOPLEʺ  OF ʺPERSON_TYPEʺ OID ʹ36101E4C6B7E4F7E96A8A6662518965Cʹ  OIDINDEX (PCTFREE 10 INITRANS 2 MAXTRANS 255            STORAGE(INITIAL 131072 NEXT 131072                    MINEXTENTS 1 MAXEXTENTS 4096                    PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1                    BUFFER_POOL DEFAULT)  TABLESPACE ʺUSERSʺ)  PCTFREE 10 PCTUSED 40  INITRANS 1 MAXTRANS 255  LOGGING STORAGE(INITIAL 131072 NEXT 131072                  MINEXTENTS 1 MAXEXTENTS 4096                  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1                  BUFFER_POOL DEFAULT) TABLESPACE ʺUSERSʺ  /        ALTER TABLE ʺTKYTEʺ.ʺPEOPLEʺ MODIFY  (ʺSYS_NC_OID$ʺ DEFAULT SYS_OP_GUID())  /  This gives us a little more insight into what is actually taking place here. We see the  OIDINDEX clause clearly now and we see a reference to the SYS_NC_OID$ column. This  is the hidden primary key of the table. The function SYS_OP_GUID, is the same as the  function SYS_GUID. They both return a globally unique identifier that is a 16 byte RAW  field.   The OID ʹʹ syntax is not documented in the Oracle documentation. All  this is doing is ensuring that during an EXP and subsequent IMP, the underlying type  PERSON_TYPE is in fact the same type. This will prevent an error that would occur if you:  1. Created the PEOPLE table.   2. Exported it.   3. Dropped it and the underlying PERSON_TYPE.   4. Created a new PERSON_TYPE with different attributes.   5. Imported the old PEOPLE data.   Obviously, this export cannot be imported into the new structure ‐ it will not fit. This  check prevents that from occurring. You can refer to the Chapter 8 on Import and Export  later on for guidelines regarding import and export and more details on object tables.  If you remember, I mentioned that we can change the behavior of the object identifier  assigned to an object instance. Instead of having the system generate a pseudo primary  key for us, we can use the natural key of an object. At first, this might appear self defeating  Expert one-on-one Oracle 335 ‐ the SYS_NC_OID$ will still appear in the table definition in SYS.COL$, and in fact it, will  appear to consume massive amounts of storage as compared to the system generated  column. Once again however, there is ʹmagicʹ at work here. The SYS_NC_OID$ column for  an object table that is based on a primary key and not system generated, is a virtual column  and consumes no real storage on disk. Here is an example that shows what happens in the  data dictionary and shows that there is no physical storage consume for the  SYS_NC_OID$. Weʹll start with an analysis of the system generated OID table:   tkyte@TKYTE816> CREATE TABLE ʺTKYTEʺ.ʺPEOPLEʺ    2  OF ʺPERSON_TYPEʺ    3  /        Table created.        tkyte@TKYTE816> select name, type#, segcollength    2    from sys.col$    3   where obj# = ( select object_id    4                    from user_objects    5                   where object_name = ʹPEOPLEʹ )    6     and name like ʹSYS\_NC\_%ʹ escape ʹ\ʹ    7  /        NAME                                TYPE# SEGCOLLENGTH  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐  SYS_NC_OID$                            23           16  SYS_NC_ROWINFO$                       121            1        tkyte@TKYTE816> insert into people(name)    2  select rownum from all_objects;        21765 rows created.        tkyte@TKYTE816> analyze table people compute statistics;  Table analyzed.        tkyte@TKYTE816> select table_name, avg_row_len from user_object_tables;        TABLE_NAME                     AVG_ROW_LEN  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐  PEOPLE                                  25  Expert one-on-one Oracle 336 So, we see here that the average row length is 25 bytes, 16 bytes for the SYS_NC_OID$ and  9 bytes for the NAME. Now, letʹs do the same thing, but use a primary key on the NAME  column as the object identifier:   tkyte@TKYTE816> CREATE TABLE ʺTKYTEʺ.ʺPEOPLEʺ    2  OF ʺPERSON_TYPEʺ    3  ( constraint people_pk primary key(name) )    4  object identifier is PRIMARY KEY    5  /        Table created.        tkyte@TKYTE816> select name, type#, segcollength    2    from sys.col$    3   where obj# = ( select object_id    4                    from user_objects    5                   where object_name = ʹPEOPLEʹ )    6     and name like ʹSYS\_NC\_%ʹ escape ʹ\ʹ    7  /        NAME                                TYPE# SEGCOLLENGTH  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐  SYS_NC_OID$                            23           81  SYS_NC_ROWINFO$                       121            1        According to this, instead of a small 16 byte column, we have a large 81 byte column! In  reality, there is no data stored in there. It will be Null. The system will generate a unique  ID based on the object table, its underlying type and the value in the row itself. We can see  this in the following:   tkyte@TKYTE816> insert into people (name)    2  values ( ʹHello World!ʹ );        1 row created.        tkyte@TKYTE816> select sys_nc_oid$ from people p;        SYS_NC_OID$  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  7129B0A94D3B49258CAC926D8FDD6EEB0000001726010001000100290000  0000000C07001E0100002A00078401FE000000140C48656C6C6F20576F72  6C6421000000000000000000000000000000000000  Expert one-on-one Oracle 337       tkyte@TKYTE816> select utl_raw.cast_to_raw( ʹHello World!ʹ ) data    2  from dual;        DATA  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  48656C6C6F20576F726C6421        tkyte@TKYTE816> select utl_raw.cast_to_varchar2(sys_nc_oid$) data    2  from people;        DATA  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  Hello World!  If we select out the SYS_NC_OID$ column and inspect the HEX dump of the string we  inserted, we see that the row data itself is embedded in the object ID. Converting the object  id into a VARCHAR2, we can just confirm that visually. Does that mean our data is stored  twice with a lot of overhead with it? Actually it can not:   tkyte@TKYTE816> insert into people(name)    2  select rownum from all_objects;        21766 rows created.        tkyte@TKYTE816> analyze table people compute statistics;        Table analyzed.        tkyte@TKYTE816> select table_name, avg_row_len from user_object_tables;        TABLE_NAME                     AVG_ROW_LEN  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐  PEOPLE                                   8  The average row length is only 8 bytes now. The overhead of storing the system‐generated  key is gone and the 81 bytes you might think we are going to have isnʹt really there. Oracle  synthesizes the data upon selecting from the table.  Now for an opinion. The object relational components (nested tables, object tables) are  primarily what I call ʹsyntactic sugarʹ. They are always translated into ʹgood oldʹ relational  rows and columns. I prefer not to use them as physical storage mechanisms personally.  There are too many bits of ʹmagicʹ happening ‐ side effects that are not clear. You get  Expert one-on-one Oracle 338 hidden columns, extra indexes, surprise pseudo columns, and so on. This does not mean that  the object relational components are a waste of time, on the contrary really. I use them in  PL/SQL constantly. I use them with object views. I can achieve the benefits of a nested  table construct (less data returned over the network for a master detail relationship,  conceptually easier to work with, and so on) without any of the physical storage concerns.  That is because I can use object views to synthesize my objects from my relational data.  This solves most all of my concerns with object tables/nested tables in that the physical  storage is dictated by me, the join conditions are setup by me, and the tables are available  as relational tables (which is what many third party tools and applications will demand)  naturally. The people who require an object view of relational data can have it and the  people who need the relational view have it. Since object tables are really relational tables  in disguise, we are doing the same thing Oracle does for us behind the scenes, only we can  do it more efficiently, since we donʹt have to do it generically as they do. For example,  using the types defined above I could just as easily use the following:   tkyte@TKYTE816> create table people_tab    2  (  name        varchar2(30) primary key,    3     dob         date,    4     home_city   varchar2(30),    5     home_street varchar2(30),    6     home_state  varchar2(2),    7     home_zip    number,    8     work_city   varchar2(30),    9     work_street varchar2(30),   10     work_state  varchar2(2),   11     work_zip    number   12  )   13  /        Table created.        tkyte@TKYTE816> create view people of person_type    2  with object identifier (name)    3  as    4  select name, dob,    5    address_type(home_city,home_street,home_state,home_zip) home_adress,    6    address_type(work_city,work_street,work_state,work_zip) work_adress    7    from people_tab    8  /        View created.        tkyte@TKYTE816> insert into people values ( ʹTomʹ, ʹ15‐mar‐1965ʹ,  Expert one-on-one Oracle 339   2  address_type( ʹRestonʹ, ʹ123 Main Streetʹ, ʹVaʹ, ʹ45678ʹ ),    3  address_type( ʹRedwoodʹ, ʹ1 Oracle Wayʹ, ʹCaʹ, ʹ23456ʹ ) );        1 row created.  However I achieve very much the same effect, I know exactly what is stored, how it is  stored, and where it is stored. For more complex objects we may have to code INSTEAD  OF triggers on the Object Views to allow for modifications through the view.  Object Table Wrap‐up  Object tables are used to implement an object relational model in Oracle. A single object  table will create many physical database objects typically, and add additional columns to  your schema to manage everything. There is some amount of ʹmagicʹ associated with  object tables. Object Views allow you to take advantage of the syntax and semantics of  ʹobjectsʹ while at the same time retaining complete control over the physical storage of the  data and allowing for relational access to the underlying data. In that fashion, you can  achieve the best of both the relational and object relational worlds.  Summary  Hopefully, after reading this chapter you have come to the conclusion that not all tables  are created equal. Oracle provides a rich variety of table types that you can exploit. In this  chapter, we have covered many of the salient aspects of tables in general and the many  different table types Oracle provides for us to use.   We began by looking at some terminology and storage parameters associated with tables.  We looked at the usefulness of FREELISTs in a multi‐user environment where a table is  frequently inserted/updated by many people simultaneously. We investigated the  meaning of PCTFREE and PCTUSED, and developed some guidelines for setting them  correctly.  Then we got into the different types of tables starting with the common heap. The heap  organized table is by far the most commonly used table in most Oracle applications and is  the default table type. We moved onto index organized tables, the ability store your table  data in an index. We saw how these are applicable for various uses such as lookup tables  and inverted lists where a heap table would just be a redundant copy of the data. Later,  we saw how they can really be useful when mixed with other table types, specifically the  nested table type.  We looked at cluster objects of which Oracle has two kinds; Index and Hash. The goals of  the cluster are twofold:  Expert one-on-one Oracle 340 • To give us the ability to store data from many tables together ‐ on the same  database block(s); and   • To give us the ability to force like data to be stored physically ʹtogetherʹ based on  some cluster key ‐ in this fashion all of the data for department 10 (from many  tables) may be stored together.   These features allow us to access this related data very quickly, with minimal physical I/O  that might otherwise be needed to pull together all of the data. We observed the main  differences between index clusters and hash clusters and discussed when each would be  appropriate (and when they would not).  Next, we moved onto Nested Tables. We reviewed the syntax, semantics, and usage of  these types of tables. We saw how they are in a fact a system generated and maintained  parent/child pair of tables and discovered how Oracle physically does this for us. We  looked at using different table types for Nested tables which by default use a heap based  table. We found that there would probably never be a reason not to use an IOT instead of a  heap table for nested tables.  Then we looked into the ins and outs of Temporary tables; looking at how to create them,  where they get their storage from, and the fact that they introduce no concurrency related  issues at runtime. We explored the differences between session level and transaction level  temporary table. We discussed the appropriate method for using temporary tables in an  Oracle database.   This section closed up with a look into the workings of object tables. Like nested tables, we  discovered there is a lot going on under the covers with object tables in Oracle. We  discussed how object views on top of relational tables can give us the functionality of an  object table while at the same time giving us access to the underlying relational data easily;  a topic we will look at in more detail in Chapter 20 on Using Object Relational Features.  Expert one-on-one Oracle 341 Chapter 7: Indexes  Overview  Indexing is a crucial aspect of your application design and development. Too many  indexes and the performance of DML will suffer. Too few indexes and the performance of  queries (including inserts, updates and deletes) will suffer. Finding the right mix is critical  to your application performance.  I find frequently that indexes are an afterthought in application development. I believe  that this is the wrong approach. From the very beginning, if you understand how the data  will be used, you should be able to come up with the representative set of indexes you will  use in your application. Too many times the approach seems to be to throw the application  out there and then see where indexes are needed. This implies you have not taken the time  to understand how the data will be used and how many rows you will ultimately be  dealing with. Youʹll be adding indexes to this system forever as the volume of data grows  over time (reactive tuning). Youʹll have indexes that are redundant, never used, and this  wastes not only space but also computing resources. A few hours at the start, spent  properly considering when and how to index your data will save you many hours of  ʹtuningʹ further down the road (note that I said ʹit willʹ, not ʹit mightʹ).  The basic remit of this chapter is to give an overview of the indexes available for use in  Oracle and discuss when and where you might use them. This chapter will differ from  others in this book in terms of its style and format. Indexing is a huge topic ‐ you could  write an entire book on the subject. Part of the reason for this is that it bridges the  developer and DBA roles. The developer must be aware of them, how they apply to their  applications, when to use them (and when not to use them), and so on. The DBA is  concerned with the growth of an index, the degree of fragmentation within an index and  other physical properties. We will be tackling indexes mainly from the standpoint of their  practical use in applications (we will not deal specifically with index fragmentation, and  so on). The first half of this chapter represents the basic knowledge I believe you need in  order to make intelligent choices about when to index and what type of index to use. The  second answers some of the most frequently asked questions about indexes.   The various examples in this book require different base releases of Oracle. When a  specific feature requires Oracle 8i Enterprise or Personal Edition Iʹll specify that. Many of  the B*Tree indexing examples require Oracle 7.0 and later versions. The bitmap indexing  examples require Oracle 7.3.3 or later versions (Enterprise or Personal edition). Function‐ based indexes and application domain indexes require Oracle 8i Enterprise or Personal  Editions. The section Frequently Asked Questions applies to all releases of Oracle.  Expert one-on-one Oracle 342 An Overview of Oracle Indexes  Oracle provides many different types of indexes for us to use. Briefly they are as follows:  • B*Tree Indexes ‐ These are what I refer to as ʹconventionalʹ indexes. They are by far  the most common indexes in use in Oracle, and most other databases. Similar in  construct to a binary tree, they provide fast access, by key, to an individual row or  range of rows, normally requiring few reads to find the correct row. The B*Tree  index has several ʹsubtypesʹ:  Index Organized Tables ‐ A table stored in a B*Tree structure. We discussed these  in some detail Chapter 4, Tables. That section also covered the physical storage of  B*Tree structures, so we will not cover that again here.  B*Tree Cluster Indexes ‐ These are a slight variation of the above index. They are  used to index the cluster keys (see Index Clustered Tables in Chapter 6) and will not  be discussed again in this chapter. They are used not to go from a key to a row, but  rather from a cluster key to the block that contains the rows related to that cluster  key.  Reverse Key Indexes ‐ These are B*Tree indexes where the bytes in the key are  ʹreversedʹ. This is used to more evenly distribute index entries throughout an index  that is populated with increasing values. For example, if I am using a sequence to  generate a primary key, the sequence will generate values like 987500, 987501,  987502, and so on. Since these values are sequential, they would all tend to go the  same block in the index, increasing contention for that block. With a reverse key  index, Oracle would be indexing: 205789, 105789, 005789 instead. These values will  tend to be ʹfar awayʹ from each other in the index and would spread out the inserts  into the index over many blocks.  Descending Indexes ‐ In the future, descending indexes will not be notable as a  special type of index. However, since they are brand new with Oracle 8i they  deserve a special look. Descending indexes allow for data to be sorted from ʹbigʹ to  ʹsmallʹ (descending) instead of small to big (ascending) in the index structure. Weʹll  take a look at why that might be important and how they work.   • Bitmap Indexes ‐ Normally in a B*Tree, there is a one‐to‐one relationship between  an index entry and a row ‐ an index entry points to a row. With a bitmap index, a  single index entry uses a bitmap to point to many rows simultaneously. They are  appropriate for low cardinality data (data with few distinct values) that is mostly  read‐only. A column that takes on three values: Y, N, and NULL, in a table of one  million rows might be a good candidate for a bitmap index. Bitmap indexes should  Expert one-on-one Oracle 343 never be considered in an OLTP database for concurrency related issues (which  weʹll discuss in due course).   • Function‐based indexes ‐ These are B*Tree or Bitmap indexes that store the  computed result of a function on a rows column(s) ‐ not the column data itself.  These may be used to speed up queries of the form: SELECT * FROM T WHERE  FUNCTION(DATABASE_COLUMN) = SOME_VALUE since the value  FUNCTION(DATABASE_COLUMN) has already been computed and stored in the  index.   • Application Domain Indexes ‐ These are indexes you build and store yourself,  either in Oracle or perhaps even outside of Oracle. You will tell the optimizer how  selective your index is, how costly it is to execute and the optimizer will decide  whether or not to use your index, based on that information. The interMedia text  index is an example of an application domain index; it is built using the same tools  you may use to build your own index.   • interMedia Text Indexes ‐ This is a specialized index built into Oracle to allow for  keyword searching of large bodies of text. Weʹll defer a discussion of these until the  Chapter 17 on interMedia.   As you can see, there are many types of indexes to choose from. What I would like to do in  the following sections is to present some technical details on how they work and when  they should be used. I would like to stress again that we will not be covering certain DBA‐ related topics. For example, we will not cover the mechanics of an online rebuild, but  rather will concentrate on practical application‐related details.  B*Tree Indexes  B*Tree, or what I call ʹconventionalʹ, indexes are the most commonly used type of indexing  structure in the database. They are similar in implementation to a binary search tree. Their  goal is to minimize the amount of time Oracle spends searching for data. Loosely speaking,  if you have an index on a number column then the structure might look like this:    Expert one-on-one Oracle 344 The lowest level blocks in the tree, called leaf nodes, contain every indexed key and a row  ID (rid in the picture) that points to the row it is indexing. The interior blocks, above the  leaf nodes, are known as branch blocks. They are used to navigate through the structure.  For example, if we wanted to find the value 42 in the index, we would start at the top of  the tree and go to the right. We would inspect that block and discover we needed to go to  the block in the range ʹless than 50 to 40ʹ. This block would be the leaf block and would  point us to the rows that contained the number 42. It is interesting to note that the leaf  nodes of the index are actually a doubly linked list. Once we find out where to ʹstartʹ in the  leaf nodes ‐ once we have found that first value‐ doing an ordered scan of values (also  known as an index range scan) is very easy. We donʹt have to navigate the structure any  more; we just go forward through the leaf nodes. That makes solving a predicate, such as  the following, pretty simple:   where x between 20 and 30  Oracle finds the first index block that contains 20 and then just walks horizontally,  through the linked list of leaf nodes until, it finally hits a value that is greater than 30.  There really is no such thing as a non‐unique index in a B*Tree. In a non‐unique index,  Oracle simply adds the rowʹs row ID to the index key to make it unique. In a unique index,  as defined by you, Oracle does not add the row ID to the index key. In a non‐unique index,  we will find that the data is sorted first by index key values (in the order of the index key),  and then by row ID. In a unique index, the data is sorted by the index key values only.  One of the properties of a B*Tree is that all leaf blocks should be at the same level in the  tree, although technically, the difference in height across the tree may vary by one. This  level is also known as the height of the index, meaning that all the nodes above the leaf  nodes only point to lower, more specific nodes, while entries in the leaf nodes point to  specific row IDs, or a range of row IDs. Most B*Tree indexes will have a height of 2 or 3,  even for millions of records. This means that it will take, in general, 2 or 3 reads to find  your key in the index ‐ which is not too bad. Another property is that the leaves, are self‐ balanced, in other words, all are at the same level, and this is mostly true. There are some  opportunities for the index to have less than perfect balance, due to updates and deletes.  Oracle will try to keep every block in the index from about three‐quarters to completely  full although, again, DELETEs and UPDATEs can skew this as well. In general, the B*Tree  is an excellent general purpose indexing mechanism that works well for large and small  tables and experiences little, if any, degradation as the size of the underlying table grows,  as long as the tree does not get skewed.  One of the interesting things you can do with a B*Tree index is to ʹcompressʹ it. This is not  compression in the same manner that zip files are compressed; rather this is compression  that removes redundancies from concatenated indexes. We covered this in some detail in  the section Index Organized Tables in Chapter 6, but we will take a brief look at it again here.  Expert one-on-one Oracle 345 The basic concept behind a key compressed index is that every entry is broken into two  pieces ‐ a ʹprefixʹ and ʹsuffixʹ component. The prefix is built on the leading columns of the  concatenated index and would have many repeating values. The suffix is the trailing  columns in the index key and is the unique component of the index entry within the prefix.  For example, weʹll create a table and index and measure itʹs space without compression ‐  then, weʹll recreate the index with index key compression enabled and see the difference:  Note This example calls on the show_space procedure given in Chapter 6 on Tables tkyte@TKYTE816> create table t    2  as    3  select * from all_objects    4  /        Table created.        tkyte@TKYTE816> create index t_idx on    2  t(owner,object_type,object_name);        Index created.        tkyte@TKYTE816>  tkyte@TKYTE816> exec show_space(ʹT_IDXʹ,user,ʹINDEXʹ)  Free Blocks.............................0  Total Blocks............................192  Total Bytes.............................1572864  Unused Blocks...........................35  Unused Bytes............................286720  Last Used Ext FileId....................6  Last Used Ext BlockId...................649  Last Used Block.........................29        PL/SQL procedure successfully completed.  The index allocated 192 blocks and has 35 that contain no data (157 blocks in total use). We  could realize that the OWNER component is repeated many times. A single index block  will have dozens of entries like this:  Expert one-on-one Oracle 346   We could factor the repeated OWNER column out of this, resulting in a block that looks  more like this:    Here the owner name appears once on the leaf blocks ‐ not once per repeated entry. If we  recreate that index using compression with the leading column:   tkyte@TKYTE816> drop index t_idx;  Index dropped.        tkyte@TKYTE816> create index t_idx on    2  t(owner,object_type,object_name)    3  compress 1;  Index created.        tkyte@TKYTE816> exec show_space(ʹT_IDXʹ,user,ʹINDEXʹ)  Free Blocks.............................0  Total Blocks............................192  Total Bytes.............................1572864  Unused Blocks...........................52  Unused Bytes............................425984  Last Used Ext FileId....................6  Last Used Ext BlockId...................649  Last Used Block.........................12        PL/SQL procedure successfully completed.  Expert one-on-one Oracle 347 We can see this reduced the size of the overall index structure from 157 used blocks to 140,  a reduction of about 10 percent. We can go further, compressing the leading two columns.  This will result in blocks where both the OWNER and the OBJECT_TYPE are factored out  to the block level:    Now, when we use compression on the leading two columns:  tkyte@TKYTE816> drop index t_idx;  Index dropped.        tkyte@TKYTE816> create index t_idx on    2  t(owner,object_type,object_name)    3  compress 2;  Index created.        tkyte@TKYTE816>  tkyte@TKYTE816> exec show_space(ʹT_IDXʹ,user,ʹINDEXʹ)  Free Blocks.............................0  Total Blocks............................128  Total Bytes.............................1048576  Unused Blocks...........................15  Unused Bytes............................122880  Last Used Ext FileId....................6  Last Used Ext BlockId...................585  Last Used Block.........................49        PL/SQL procedure successfully completed.  Expert one-on-one Oracle 348 This index is 113 blocks, about thirty percent smaller then the original index. Depending  on how repetitious your data is, this could really add up. Now, you do not get this  compression for free. The compressed index structure is now more complex then it used to  be. Oracle will spend more time processing the data in this structure, both while  maintaining the index during modifications as well as when you search the index during a  query. What we are doing here is trading off increased CPU time for reduced I/O time.  Our block buffer cache will be able to hold more index entries than before, our cache‐hit  ratio might go up, our physical I/Os should go down but it will take a little more CPU  horse power to process the index and also increases the chance of block contention. Just as  in our discussion of the hash cluster, where it took more CPU to retrieve a million random  rows but half the I/O, we must be aware of the trade‐off. If you are currently CPU‐bound,  adding compressed key indexes may slow down your processing. On the other hand, if  you are I/O bound, using them may speed things up.  Reverse Key Indexes  Another feature of a B*Tree index is the ability to ʹreverseʹ their keys. At first, you might  ask yourself, ʹWhy would I want to do that?ʹ They were designed for a specific  environment, for a specific issue. They were implemented to reduce contention on index  leaf blocks in an Oracle Parallel Server (OPS) environment.  Note We discussed OPS in Chapter 2, Architecture. It is a configuration of Oracle where multiple instances can mount and open the same  database. If two instances need to modify the same block of data simultaneously, they will  share the block by flushing it to disk so that the other instance can read it. This activity is  known as ʹpingingʹ. Pinging is something to be avoided when using OPS but will be  virtually unavoidable if you have a conventional B*Tree index, this is on a column whose  values are generated by a sequence number. Everyone will be trying to modify the left  hand side of the index structure as they insert new values (see the figure at start of the  section on B*Tree Indexes that shows ʹhigher valuesʹ in the index go to the left, lower values  to the right). In an OPS environment, modifications to indexes on columns populated by  sequences are focused on a small set of leaf blocks. Reversing the keys of the index allows  insertions to be distributed across all the leaf keys in the index, though it tends to make the  index much less efficiently packed.  A reverse key index will simply reverse the bytes of each column in an index key. If we  consider the numbers 90101, 90102, 90103,and look at their internal representation using  the Oracle DUMP function, we will find they are represented as:   tkyte@TKYTE816> select 90101, dump(90101,16) from dual    2  union all    3  select 90102, dump(90102,16) from dual  Expert one-on-one Oracle 349   4  union all    5  select 90103, dump(90103,16) from dual    6  /             90101 DUMP(90101,16)  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐       90101 Typ=2 Len=4: c3,a,2,2       90102 Typ=2 Len=4: c3,a,2,3       90103 Typ=2 Len=4: c3,a,2,4  Each one is four bytes in length and only the last byte is different. These numbers would  end up right next to each other in an index structure. If we reverse their bytes however,  Oracle will insert:   tkyte@TKYTE816> select 90101, dump(reverse(90101),16) from dual    2  union all    3  select 90102, dump(reverse(90102),16) from dual    4  union all    5  select 90103, dump(reverse(90103),16) from dual    6  /       90101 DUMP(REVERSE(90101),1  ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐       90101 Typ=2 Len=4: 2,2,a,c3       90102 Typ=2 Len=4: 3,2,a,c3       90103 Typ=2 Len=4: 4,2,a,c3  The numbers will end up ʹfar awayʹ from each other. This reduces the number of instances  going after the same block (the leftmost block) and reduces the amount of pinging going  on. One of the drawbacks to a reverse key index is that you cannot utilize it in all of the  cases where a regular index can be applied. For example, in answering the following  predicate, a reverse key index on x would not be useful:   where x > 5  The data in the index is not sorted before it is stored, hence the range scan will not work.  On the other hand, some range scans can be done on a reverse key index. If I have a  concatenated index on X, Y, the following predicate will be able to make use of the reverse  key index and will ʹrange scanʹ it:   where x = 5  This is because the bytes for X are reversed and then the bytes for Y are reversed. Oracle  does not reverse the bytes of X || Y, but rather stores reverse(X) || reverse(Y). This means  Expert one-on-one Oracle 350 all of the values for X = 5 will be stored together, so Oracle can range scan that index to  find them all.  Descending Indexes  Descending Indexes are a new feature of Oracle 8i that extend the functionality of a B*Tree  index. They allow for a column to be stored sorted from ʹbigʹ to ʹsmallʹ in the index instead  of ascending. Prior releases of Oracle have always supported the DESC (descending)  keyword, but basically ignored it ‐ it had no effect on how the data was stored or used in  the index. In Oracle 8i however, it changes the way the index is created and used.  Oracle has had the ability to read an index backwards for quite a while, so you may be  wondering why this feature is relevant. For example, if we used the table T from above  and queried:   tkyte@TKYTE816> select owner, object_type    2  from t    3  where owner between ʹTʹ and ʹZʹ    4  and object_type is not null    5  order by owner DESC, object_type DESC    6  /  46 rows selected.              Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=46 Bytes=644)     1    0   INDEX (RANGE SCAN DESCENDING) OF ʹT_IDXʹ (NON‐UNIQUE)...  It is shown that Oracle will just read the index backwards, there is no final sort step in this  plan, the data is sorted. Where this descending index feature comes into play however, is  when you have a mixture of columns and some are sorted ASC (ascending) and some  DESC (descending). For example:   tkyte@TKYTE816> select owner, object_type    2  from t    3  where owner between ʹTʹ and ʹZʹ    4  and object_type is not null    5  order by owner DESC, object_type ASC    6  /  46 rows selected.              Expert one-on-one Oracle 351 Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=46 Bytes=644)     1    0   SORT (ORDER BY) (Cost=4 Card=46 Bytes=644)     2    1     INDEX (RANGE SCAN) OF ʹT_IDXʹ (NON‐UNIQUE) (Cost=2 Card=  Oracle isnʹt able to use the index we have in place on (OWNER, OBJECT_TYPE,  OBJECT_NAME) anymore to sort the data. It could have read it backwards to get the data  sorted by OWNER DESC but it needs to read it ʹforwardsʹ to get OBJECT_TYPE sorted  ASC. Instead, it collected together all of the rows and then sorted. Enter the DESC index:   tkyte@TKYTE816> create index desc_t_idx on t(owner DESC, object_type ASC )    2  /  Index created.        tkyte@TKYTE816> select owner, object_type    2  from t    3  where owner between ʹTʹ and ʹZʹ    4  and object_type is not null    5  order by owner DESC, object_type ASC    6  /  46 rows selected.        Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=46 Bytes=644)     1    0   INDEX (RANGE SCAN) OF ʹDESC_T_IDXʹ (NON‐UNIQUE)...  Now, once more, we are able to read the data sorted, there is no extra sort step at the end  of the plan. It should be noted that unless your compatible init.ora parameter is set to 8.1.0  or higher, the DESC option on the create index will be silently ignored ‐ no warning or  error will be produced as this was the default behavior in prior releases.  When should you use a B*Tree Index?  Not being a big believer in ʹrules of thumbʹ (there are exceptions to every rule), I donʹt  have any rules of thumb for when to use (or not to use) a B*Tree index. To demonstrate  why I donʹt have any rules of thumb for this case, Iʹll present two equally valid ones:  • Only use B*Tree to index columns if you are going to access a very small percentage  of the rows in the table via the index   • Use a B*Tree index if you are going to process many rows of a table and the index  can be used instead of the table.   Expert one-on-one Oracle 352 These rules seem to offer conflicting advice, but in reality, they do not ‐ they just cover two  extremely different cases. There are two ways to use an index:  1. As the means to access rows in a table. You will read the index to get to a row in the  table. Here you want to access a very small percentage of the rows in the table.   2. As the means to answer a query. The index contains enough information to answer  the entire query ‐ we will not have to go to the table at all. The index will be used as  a ʹthinnerʹ version of the table.   The first case above says if you have a table T (using the same table T from above) and you  have a query plan looks like this:   tkyte@TKYTE816> set autotrace traceonly explain        tkyte@TKYTE816> select owner, status    2    from T    3   where owner = USER;        Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=CHOOSE     1    0   TABLE ACCESS (BY INDEX ROWID) OF ʹTʹ     2    1     INDEX (RANGE SCAN) OF ʹT_IDXʹ (NON‐UNIQUE)  You should be accessing a very small percentage of this table. The issue to look at here is  the INDEX (RANGE SCAN) followed by the TABLE ACCESS BY INDEX ROWID. What  this means is that Oracle will read the index and then, for each index entry, it will perform  a database block read (logical or physical I/O) to get the row data. This is not the most  efficient method if you are going to have to access a large percentage of the rows in T via  the index (below we will define what a large percentage might be).  On the other hand, if the index can be used instead of the table, you can process 100  percent (or any percentage in fact) of the rows via the index. This is rule of thumb number  two. You might use an index just to create a ʹthinnerʹ version of a table (in sorted order to  boot).  The following query demonstrates this concept:  tkyte@TKYTE816> select count(*)    2    from T    3   where owner = USER;        Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  Expert one-on-one Oracle 353    0      SELECT STATEMENT Optimizer=CHOOSE     1    0   SORT (AGGREGATE)     2    1     INDEX (RANGE SCAN) OF ʹT_IDXʹ (NON‐UNIQUE)  Here, only the index was used to answer the query ‐ it would not matter now what  percentage of rows we were accessing, we used the index only. We can see from the plan  that the underlying table was never accessed; we simply scanned the index structure itself.   It is important to understand the difference between the two concepts. When we have to  do a TABLE ACCESS BY INDEX ROWID, we must ensure we are accessing only a small  percentage of the total rows in the table. If we access too high a percentage of the rows  (larger then somewhere between 1 and 20 percent of the rows), it will take longer than just  full scanning the table. With the second type of query above, where the answer is found  entirely in the index, we have a different story. We read an index block and pick up many  ʹrowsʹ to process, we then go onto the next index block, and so on ‐ we never go to the  table. There is also a fast full scan we can perform on indexes to make this even faster in  certain cases. A fast full scan is when the database reads the index blocks in no particular  order ‐ it just starts reading them. It is no longer using the index as an index, but even  more like a table at that point. Rows do not come out ordered by index entries from a fast  full scan.  In general, a B*Tree index would be placed on columns that I use frequently in the  predicate of a query, and expect some small fraction of the data from the table to be  returned. On a ʹthinʹ table, a table with few or small columns, this fraction may be very  small. A query that uses this index should expect to retrieve 2 to 3 percent or less of the  rows to be accessed in the table. On a ʹfatʹ table, a table with many columns or a table with  very wide columns, this fraction might go all of the way up to 20‐25 percent of the table.  This advice doesnʹt always seem to make sense to everyone immediately; it is not intuitive,  but it is accurate. An index is stored sorted by index key. The index will be accessed in  sorted order by key. The blocks that are pointed to are stored randomly in a heap.  Therefore, as we read through an index to access the table, we will perform lots of  scattered, random I/O. By scattered, I mean that the index will tell us to read block 1, block  1000, block 205, block 321, block 1, block 1032, block 1, and so on ‐ it wonʹt ask us to read  block 1, then 2, then 3 in a consecutive manner. We will tend to read and re‐read blocks in  a very haphazard fashion. Doing this, single block I/O can be very slow.  As a simplistic example of this, letʹs say we are reading that ʹthinʹ table via an index and  we are going to read 20 percent of the rows. Assume we have 100,000 rows in the table.  Twenty percent of that is 20,000 rows. If the rows are about 80 bytes apiece in size, on a  database with an 8 KB block size, we will find about 100 rows per block. That means the  table has approximately 1000 blocks. From here, the math is very easy. We are going to  read 20,000 rows via the index; this will mean 20,000 TABLE ACCESS BY ROWID  operations. We will process 20,000 table blocks in order to execute this query. There are  Expert one-on-one Oracle 354 only about 1000 blocks in the entire table however! We would end up reading and  processing each block in the table on average 20 times! Even if we increased the size of the  row by an order of magnitude to 800 bytes per row, 10 rows per block, we now have  10,000 blocks in the table. Index accesses for 20,000 rows would cause us to still read each  block on average two times. In this case, a full table scan will be much more efficient than  using an index, as it only has to touch each block once. Any query that used this index to  access the data, would not be very efficient until it accesses on average less then 5 percent  of the data for the 800 byte column (then we access about 5000 blocks) and even less for  the 80 byte column (about 0.5 percent or less).  Of course, there are factors that change these calculations. Suppose you have a table where  the rows have a primary key populated by a sequence. As data is added to the table, rows  with sequential sequence numbers are in general ʹnextʹ to each other. The table is naturally  clustered, in order, by the primary key, (since the data is added in more or less that order).  It will not be strictly clustered in order by the key of course (we would have to use an IOT  to achieve that), but in general rows with primary keys that are close in value will be  ʹcloseʹ together in physical proximity. Now when you issue the query:  select * from T where primary_key between :x and :y  The rows you want are typically located on the same blocks. In this case an index range  scan may be useful even if it accesses a large percentage of rows, simply because the  database blocks that we need to read and re‐read will most likely be cached, since the data  is co‐located. On the other hand, if the rows are not co‐located, using that same index may  be disastrous for performance. A small demonstration will drive this fact home. Weʹll start  with a table that is pretty much ordered by its primary key:  tkyte@TKYTE816> create table colocated ( x int, y varchar2(2000) ) pctfree 0;  Table created.        tkyte@TKYTE816> begin    2    for i in 1 .. 100000    3    loop    4           insert into colocated values ( i, rpad(dbms_random.random,75,ʹ*ʹ) );    5    end loop;    6  end;    7  /  PL/SQL procedure successfully completed.        tkyte@TKYTE816> alter table colocated    2  add constraint colocated_pk primary key(x);  Table altered.  Expert one-on-one Oracle 355 This is a table fitting the description we laid out above ‐ about 100 rows/block in my 8 KB  database. In this table there is a very good chance that the rows with x = 1, 2, 3 are on the  same block. Now, weʹll take this table and purposely ʹdisorganizeʹ it. In the COLOCATED  table above, we created the Y column with a leading random number ‐ weʹll use that fact  to ʹdisorganizeʹ the data ‐ so it will definitely not be ordered by primary key anymore:   tkyte@TKYTE816> create table disorganized nologging pctfree 0    2  as    3  select x, y from colocated ORDER BY y    4  /  Table created.        tkyte@TKYTE816> alter table disorganized    2 add constraint disorganized_pk primary key(x);  Table altered.  Arguably, these are the same tables ‐ it is a relational database, physical organization plays  no bearing on how things work (at least thatʹs what they teach in theoretical database  courses). In fact, the performance characteristics of these two tables are different as ʹnight  and dayʹ. Given the same exact query:   tkyte@TKYTE816> select * from COLOCATED where x between 20000 and 40000;  20001 rows selected.  Elapsed: 00:00:01.02        Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=CHOOSE     1    0   TABLE ACCESS (BY INDEX ROWID) OF ʹCOLOCATEDʹ     2    1     INDEX (RANGE SCAN) OF ʹCOLOCATED_PKʹ (UNIQUE)        Statistics  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐            0  recursive calls            0  db block gets         2909  consistent gets          258  physical reads            0  redo size      1991367  bytes sent via SQL*Net to client       148387  bytes received via SQL*Net from client         1335  SQL*Net roundtrips to/from client            0  sorts (memory)            0  sorts (disk)  Expert one-on-one Oracle 356       20001  rows processed        tkyte@TKYTE816> select * from DISORGANIZED where x between 20000 and 40000;  20001 rows selected.  Elapsed: 00:00:23.34        Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=CHOOSE     1    0   TABLE ACCESS (BY INDEX ROWID) OF ʹDISORGANIZEDʹ     2    1     INDEX (RANGE SCAN) OF ʹDISORGANIZED_PKʹ (UNIQUE)        Statistics  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐            0  recursive calls            0  db block gets        21361  consistent gets         1684  physical reads            0  redo size      1991367  bytes sent via SQL*Net to client       148387  bytes received via SQL*Net from client         1335  SQL*Net roundtrips to/from client            0  sorts (memory)            0  sorts (disk)        20001  rows processed  I think this is pretty incredible. What a difference physical data layout can make! To  summarize the results:  Table  Elapsed Time  Logical I/O  Colocated  1.02 seconds  2,909  Disorganized  23.34 seconds  21,361  In my database using an 8 KB block size ‐ these tables had 1,088 total blocks apiece. The  query against disorganized table bears out the simple math we did above ‐ we did 20,000  plus logical I/Os. We processed each and every block 20 times!! On the other hand, the  physically COLOCATED data took the logical I/Os way down. Here is the perfect example  of why rules of thumb are so hard to provide ‐ in one case, using the index works great, in  the other ‐ it stinks. Consider this the next time you dump data from your production  system and load it into development ‐ it may very well be part of the answer to the  question that typically comes up of ʹwhy is it running differently on this machine ‐ they  are identical?ʹ They are not identical.   Expert one-on-one Oracle 357 Just to wrap up this example ‐ letʹs look at what happens when we full scan the  disorganized table:  tkyte@TKYTE816> select /*+ FULL(DISORGANIZED) */ *    2  from DISORGANIZED    3  where x between 20000 and 40000;        20001 rows selected.        Elapsed: 00:00:01.42        Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=CHOOSE (Cost=162 Card=218 Bytes=2     1    0   TABLE ACCESS (FULL) OF ʹDISORGANIZEDʹ (Cost=162 Card=218 B        Statistics  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐            0  recursive calls           15  db block gets         2385  consistent gets          404  physical reads            0  redo size      1991367  bytes sent via SQL*Net to client       148387  bytes received via SQL*Net from client         1335  SQL*Net roundtrips to/from client            0  sorts (memory)            0  sorts (disk)        20001  rows processed  That shows that in this particular case ‐ due to the way the data is physically stored on  disk, the full scan is very appropriate. This begs the question ‐ so how can I accommodate  for this? The answer is ‐ use the cost based optimizer (CBO) and it will do it for you. The  above case so far has been executed in RULE mode since we never gathered any statistics.  The only time we used the cost based optimizer was when we hinted the full table scan  above and asked the cost based optimizer to do something specific for us. If we analyze  the tables, we can take a peek at some of the information Oracle will use to optimize the  above queries:   tkyte@TKYTE816> analyze table colocated    2  compute statistics    3  for table    4  for all indexes  Expert one-on-one Oracle 358   5  for all indexed columns    6  /  Table analyzed.        tkyte@TKYTE816> analyze table disorganized    2  compute statistics    3  for table    4  for all indexes    5  for all indexed columns    6  /  Table analyzed.  Now, weʹll look at some of the information Oracle will use. We are specifically going to  look at the CLUSTERING_FACTOR column found in the USER_INDEXES view. The  Oracle Reference Manual tells us this column has the following meaning, it:  Indicates the amount of order of the rows in the table based on the values of the index:  • If the value is near the number of blocks, then the table is very well ordered. In this  case, the index entries in a single leaf block tend to point to rows in the same data  blocks.   • If the value is near the number of rows, then the table is very randomly ordered. In  this case, it is unlikely that index entries in the same leaf block point to rows in the  same data blocks.   The CLUSTERING_FACTOR is an indication of how ordered the table is with respect to  the index itself, when we look at these indexes we find:   tkyte@TKYTE816> select a.index_name,    2             b.num_rows,    3             b.blocks,    4             a.clustering_factor    5    from user_indexes a, user_tables b    6  where index_name in (ʹCOLOCATED_PKʹ, ʹDISORGANIZED_PKʹ )    7    and a.table_name = b.table_name    8  /        INDEX_NAME                       NUM_ROWS     BLOCKS CLUSTERING_FACTOR  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  COLOCATED_PK                       100000       1063              1063  DISORGANIZED_PK                    100000       1064             99908  Expert one-on-one Oracle 359 The COLOCATED_PK is a classic ʹthe table is well orderedʹ example, whereas the  DISORGANIZE_PK is the classic ʹthe table is very randomly orderedʹ example. It is  interesting to see how this affects the optimizer now. If we attempted to retrieve 20,000  rows, Oracle will now choose a full table scan for both queries (retrieving 20 percent of the  rows via an index is not the optimal plan even for the very ordered table). However, if we  drop down to 10 percent of the table data:   tkyte@TKYTE816> select * from COLOCATED where x between 20000 and 30000;  10001 rows selected.        Elapsed: 00:00:00.11        Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=CHOOSE (Cost=129 Card=9996 Bytes=839664)     1    0   TABLE ACCESS (BY INDEX ROWID) OF ʹCOLOCATEDʹ (Cost=129 Card=9996     2    1     INDEX (RANGE SCAN) OF ʹCOLOCATED_PKʹ (UNIQUE) (Cost=22 Card=9996)        Statistics  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐            0  recursive calls            0  db block gets         1478  consistent gets          107  physical reads            0  redo size       996087  bytes sent via SQL*Net to client        74350  bytes received via SQL*Net from client          668  SQL*Net roundtrips to/from client            1  sorts (memory)            0  sorts (disk)        10001  rows processed        tkyte@TKYTE816> select * from DISORGANIZED where x between 20000 and 30000;  10001 rows selected.        Elapsed: 00:00:00.42        Execution Plan  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐     0      SELECT STATEMENT Optimizer=CHOOSE (Cost=162 Card=9996 Bytes=839664)     1    0   TABLE ACCESS (FULL) OF ʹDISORGANIZEDʹ (Cost=162 Card=9996        Statistics  Expert one-on-one Oracle 360 ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐            0  recursive calls           15  db block gets         1725  consistent gets          707  physical reads            0  redo size       996087  bytes sent via SQL*Net to client        74350  bytes received via SQL*Net from client          668  SQL*Net roundtrips to/from client            1  sorts (memory)            0  sorts (disk)        10001  rows processed  Here we have the same table structures ‐ same indexes, but different clustering factors.  The optimizer in this case chose an index access plan for the COLOCATED table and a full  scan access plan for the DISORGANIZED table.  The key point to this discussion is that indexes are not always the appropriate access  method. The optimizer may very well be correct in choosing to not use an index, as the  above example demonstrates. There are many factors that influence the use of an index by  the optimizer ‐ including physical data layout. You might run out and try to rebuild all of  your tables now to make all indexes have a good clustering factor but that most likely  would be a waste of time in most cases. It will affect cases where you do index range scans  of a large percentage of a table ‐ not a common case in my experience. Additionally, you  must keep in mind that in general the table will only have one index with a good clustering  factor! The data can only be sorted in one way. In the above example ‐ if I had another  index on the column Y ‐ it would be very poorly clustered in the COLOCATED table, but  very nicely clustered in the DISORGANIZED table. If having the data physically clustered  is important to you ‐ consider the use of an IOT over a table rebuild.  B*Trees Wrap‐up  B*Tree indexes are by far the most common and well‐understood indexing structures in  the Oracle database. They are an excellent general purpose indexing mechanism. They  provide very scalable access times, returning data from a 1,000 row index in about the  same amount of time as a 100,000 row index structure.  When to index, and what columns to index, is something you need to pay attention to in  your design. An index does not always mean faster access; in fact, you will find that  indexes will decrease performance in many cases if Oracle uses them. It is purely a  function of how large of a percentage of the table you will need to access via the index and  how the data happens to be laid out. If you can use the index to ʹanswer the questionʹ,  accessing a large percentage of the rows makes sense, since you are avoiding the extra  Expert one-on-one Oracle 361 scattered I/O to read the table. If you use the index to access the table, you will need to  ensure you are processing a small percentage of the total table.  You should consider the design and implementation of indexes during the design of your  application, not as an afterthought (as I so often see). With careful planning and due  consideration of how you are going to access the data, the indexes you need will be  apparent in most all cases.  Bitmap Indexes  Bitmap Indexes were added to Oracle in version 7.3 of the database. They are currently  available with the Oracle 8i Enterprise and Personal Editions, but not the Standard Edition.  Bitmap indexes are designed for data warehousing/ad‐hoc query environments where the  full set of queries that may be asked of the data is not totally known at system  implementation time. They are specifically not designed for OLTP systems or systems  where data is frequently updated by many concurrent sessions.  Bitmap indexes are structures that store pointers to many rows with a single index key  entry, as compared to a B*Tree structure where there is parity between the index keys and  the rows in a table. In a bitmap index, there will be a very small number of index entries,  each of which point to many rows. In a B*Tree, it is one‐to‐one ‐ an index entry points to a  single row.  Letʹs say you were creating a bitmap index on the JOB column in the EMP table as follows:   scott@TKYTE816> create BITMAP index job_idx on emp(job);  Index created.  Oracle will store something like the following in the index:  Value/Row  1  2  3  4  5  6  7  8  9  10  11  12  13  14  ANALYST   0  0  0  0  0  0  0  1  0  1  0  0  1  0  CLERK   1  0  0  0  0  0  0  0  0  0  1  1  0  1  MANAGER   0  0  0  1  0  1  1  0  0  0  0  0  0  0  PRESIDENT   0  0  0  0  0  0  0  0  1  0  0  0  0  0  SALESMAN   0  1  1  0  1  0  0  0  0  0  0  0  0  0    This shows that rows 8, 10, and 13 have the value ANALYST whereas the rows 4, 6, and 7  have the value MANAGER. It also shows me that no rows are null (Bitmap indexes store  null entries ‐ the lack of a null entry in the index implies there are no null rows). If I  Expert one-on-one Oracle 362 wanted to count the rows that have the value MANAGER, the bitmap index would do this  very rapidly. If I wanted to find all the rows such that the JOB was CLERK or MANAGER,  I could simply combine their bitmaps from the index as follows:  Value/Row  1  2  3  4  5  6  7  8  9  10  11  12  13  14 CLERK   1  0  0  0  0  0  0  0  0  0  1  1  0  1  MANAGER   0  0  0  1  0  1  1  0  0  0  0  0  0  0  CLERK or  MANAGER   1  0  0  1  0  1  1  0  0  0  1  1  0  1  This rapidly shows me that rows 1, 4, 6, 7, 11, 12, and 14 satisfy my criteria. The bitmap  Oracle stores with each key value is set up so that each position represents a row ID in the  underlying table, if we need to actually retrieve the row for further processing. Queries  such as:  select count(*) from emp where job = ʹCLERKʹ or job = ʹMANAGERʹ  will be answered directly from the bitmap index. A query such as:  select * from emp where job = ʹCLERKʹ or job = ʹMANAGERʹ  on the other hand will need to get to the table. Here Oracle will apply a function to turn  the fact that the iʹth bit is on in a bitmap, into a row ID that can be used to access the table.  When Should you use a Bitmap Index?  Bitmap indexes are most appropriate on low cardinality data. This is data where the  number of distinct items in the set of rows divided by the number of rows is a small  number (near zero). For example, a GENDER column might take on the values M, F, and  NULL. If you have a table with 20000 employee records in it then you would find that  3/20000 = 0.00015. This column would be a candidate for a bitmap index. It would  definitely not be a candidate for a B*Tree index as each of the values would tend to retrieve  an extremely large percentage of the table. B*Tree indexes should be selective in general as  outlined above. Bitmap indexes should not be selective, on the contrary they should be  very ʹunselectiveʹ.  Bitmap indexes are extremely useful in environments where you have lots of ad‐hoc  queries, especially queries that reference many columns in an ad‐hoc fashion or produce  aggregations such as COUNT. For example, suppose you have a table with three columns  GENDER, LOCATION, and AGE_GROUP. In this table GENDER has a value of M or F,  and LOCATION can take on the values 1 through 50 and AGE_GROUP is a code  Expert one-on-one Oracle 363 representing 18 and under, 19‐25, 26‐30, 31‐40, 41 and over. You have to support a large  number of ad‐hoc queries that take the form:   Select count(*)    from T   where gender = ʹMʹ     and location in ( 1, 10, 30 )     and age_group = ʹ41 and overʹ;        select *    from t   where (   ( gender = ʹMʹ and location = 20 )          or ( gender = ʹFʹ and location = 22 ))     and age_group = ʹ18 and underʹ;        select count(*) from t where location in (11,20,30);        select count(*) from t where age_group = ʹ41 and overʹ and gender = ʹFʹ;  You would find that a conventional B*Tree indexing scheme would fail you. If you wanted  to use an index to get the answer, you would need at least three and up to six  combinations of possible B*Tree indexes in order to access the data. Since any of the three  columns or any subset of the three columns may appear, you would need large  concatenated B*Tree indexes on:  • GENDER, LOCATION, AGE_GROUP ‐ For queries that used all three, or GENDER  with LOCATION, or GENDER alone.   • LOCATION, AGE_GROUP ‐ For queries that used LOCATION and AGE_GROUP  or LOCATION alone.   • AGE_GROUP, GENDER ‐ For queries that used AGE_GROUP with GENDER or  AGE_GROUP alone.   In order to reduce the amount of data being searched, other permutations might be  reasonable as well, to reduce the size of the index structure being scanned. This is ignoring  the fact that a B*Tree index on such low cardinality data is not a good idea.  Here the bitmap index comes into play. With three small bitmap indexes, one on each of  the individual columns, you will be able to satisfy all of the above predicates efficiently.  Oracle will simply use the functions AND, OR, and XOR with the bitmaps of the three  indexes together, to find the solution set for any predicate that references any set of these  three columns. It will take the resulting merged bitmap, convert the 1s into row IDs if  necessary, and access the data (if we are just counting rows that match the criteria, Oracle  will just count the 1 bits).  Expert one-on-one Oracle 364 There are times when bitmaps are not appropriate as well. They work well in a read  intensive environment, but they are extremely ill‐suited for a write intensive environment.  The reason is that a single bitmap index key entry points to many rows. If a session  modifies the indexed data, then all of the rows that index entry points to are effectively  locked. Oracle cannot lock an individual bit in a bitmap index entry; it locks the entire  bitmap. Any other modifications that need to update that same bitmap will be locked out.  This will seriously inhibit concurrency ‐ each update will appear to lock potentially  hundreds of rows preventing their bitmap columns from being concurrently updated. It  will not lock every row as you might think, just many of them. Bitmaps are stored in  chunks, using the EMP example from above we might find that the index key ANALYST  appears in the index many times, each time pointing to hundreds of rows. An update to a  row that modifies the JOB column will need to get exclusive access to two of these index  key