Chapter 14
Chapter 14
Chapter 14
14
C H A P T E R
In This Chapter
ndexes are structures that allow you to quickly access rows in a table based on the value of one or more columns in that table. You use indexes for two primary purposes: Faster queries. Indexing columns frequently referenced in queries helps Oracle retrieve data at maximum speed. Unique values. Oracle automatically generates an index to enforce unique values in the primary key of a table. You can also use this feature for any other column (or set of columns) that you require to be unique. Like anything else in life, using an index involves some tradeoffs. When you create an index, you are trading disk space and increased insert, update, and delete time in return for faster query response. Indexes require overhead during inserts and updates because the index needs to be updated in response to new rows, or to changes in indexed columns in existing rows. Even when deleting a row, you must take the time to remove that rows entry from the index. Indexes also require disk space. The columns to be indexed must be reproduced as part of the index. If you have many indexes on a table, its possible that the total space the indexes use will exceed the space the table itself uses. In this chapter, youll read about the different types of indexes that Oracle supports. Youll see how to create them, how to reorganize them, and how to drop them. Youll also learn about the data dictionary views that you can use to retrieve information about indexes.
Understanding index types Creating an index Altering an index Deleting an index Listing indexes
382
Batty Flipper
Nosey Paintuin
Rascal Shorty
Skipper Squacky
The blocks at the bottom of the tree are called leaf nodes. They contain the index entries together with pointers to rows for those entries.
Figure 14-1: An example of the inverted tree structure used by B*Tree indexes
When you query for a particular value, Oracle navigates down through the tree to the leaf node containing the entry you are after. That entry contains a pointer to the row in the table. B*Tree indexes have several characteristics that make them a good choice for most uses: They maintain the sort order of the data, making it easy to look up a range of records. For example, you might want to find all animals whose names begin with S. With multicolumn indexes, you can use the leading-edge columns to resolve a query, even if that query doesnt reference all columns in the index.
383
They automatically stay balanced, at least they are supposed to, with all leaf nodes at the same depth, so the time you need to retrieve one entry is consistent for all entries in the index. Performance remains relatively constant, even as the size of the indexed table grows. Within the B*Tree structure, you have some flexibility in how the index operates. When you create a B*Tree index, you can choose from among these two options:
REVERSE
Reverses the bytes in each index entry. The name Justin, for example, can be indexed as nitsuJ. If Oracle Parallel Server is being used, and primary keys are being generated from a numeric sequence, reversing the index can help prevent multiple instances from competing for the same index blocks. Requires that each index entry be unique. This also inherently prevents two rows in the underlying table from having the same value for the indexed columns. Oracle normally uses unique indexes to enforce primary key constraints.
UNIQUE
The REVERSE option is compatible with UNIQUE. The default, if you choose neither option, is for the index to be stored unreversed, and for it to allow multiple entries for the same value.
384
Employee Table
Emp No -----101 202 230 432 239 598 ... Retired ------Yes No Yes Yes Yes No State ----OH MI MI MI MT IN
The yes/no bitmapped index gets one bit string for yes and one for no. Figure14-2: A conceptual view of a bitmapped index
Each distinct state value gets its own string of bits, one for each row in the table.
With bitmapped indexes on both the RETIRED column and the STATE column, as shown in Figure 14-2, Oracle can easily retrieve the bitmaps for Y and for MI, and then and them together. Oracle ands two bitmaps by merging them into one bitmap that flags rows that meet the condition represented by both of the original bitmaps. Table 14-1 provides an example.
Anding two bit streams is a logic operation that most computers can do extremely quickly. Oracle can then use the resulting bitmap to identify those rows that match all the criteria.
385
As great as bitmapped indexes are, they do have some disadvantages. They were designed for query-intensive databases, so you shouldnt use them in online transaction processing (OLTP) systems. They arent good for range queries, and you shouldnt use them for columns that contain more than just a few distinct values. Creating a bitmapped index on a name column would be a poor choice. The greater the number of distinct values in a column, the less efficient bitmapped indexing becomes.
Note
Creating an Index
Indexes are created using the CREATE INDEX statement. When you create an index, you need to specify the table on which the index is to be built. After identifying the table, you should also consider the following questions: Which columns should be part of the index? Should the index be B*Tree or bitmapped? Should the index be unique? Should the REVERSE option be used? In what tablespace should the index be stored? Once you have answered these questions, you are ready to create the index using Schema Manager, or by executing a CREATE INDEX command from SQL*Plus.
386
Further suppose that you are running an application that frequently queries for employees based on their first and last names. Perhaps the application uses a query like this:
SELECT last_name, first_name, department_no FROM employee WHERE last_name = Burgers AND first_name = Jenny;
At first glance, you might think to create an index on both the first and last name columns. However, if you think more about it, you may have only a few employees with any given last name. By creating the index on last name only, you cut the space required by the index in half. The tradeoff is increased I/O. If three people have the same last name, Oracle will need to follow each of the three index entries to the table, retrieve the row, and then check the first name. Sometimes this tradeoff is worth making, and sometimes it isnt. You may need to experiment a bit to be sure. Rather than cutting fields from an index, another strategy is to add more fields than are required. Lets assume that your application allows queries based only on the last name, and that it always issues SQL statements like this:
SELECT last_name, first_name, department_no FROM employee WHERE last_name LIKE Nue%;
Further, assume that once this query is executed, the name and department number of each matching employee will be listed on a screen from which you can choose the specific employee that you want. Which index would you create to support this query? The obvious answer is to create an index on last name. However, you might gain some efficiency in terms of response time if you create the following index instead:
CREATE INDEX emp_name_dept ON employee ( last_name, first_name, department_no );
Why place all three columns in the index when the query bases its search on only one? Doing so allows Oracle to retrieve from the index all the data that the query requires. Instead of reading each index entry and following it back to the table to get the first name and department number, Oracle can just get all three values from the index. The tradeoff here is more disk space for the index in return for the potentially faster response time.
Tip
No hard and fast rules exist for making these types of choices. The best strategy is to develop some relevant metrics, measure the performance that you get with different types of indexes, and compare that to the disk space that they use. Base your decision on those numbers.
387
Note
If you were creating this index, and you didnt want it to be unique, you would just leave the keyword UNIQUE out of the command. To make it a reverse index, you can add the keyword REVERSE immediately following the tablespace name.
388
The CHECKUP_TYPE field in the CHECKUP_HISTORY table is a good candidate for a bitmapped index. This field is a foreign key to the CHECKUP table, and contains only the three distinct values ANNUAL, MONTHLY, and DAILY. You can create a bitmapped index on that column using this command:
CREATE BITMAP INDEX checkup_history_type ON checkup_history ( checkup_type ) TABLESPACE indx;
Bitmapped indexes may not be unique, so you cant combine the two types.
The advantage of using the NOLOGGING option is greater speed, which results from not having to write the index data out to the redo log. The risk you are taking is that if you lose some of your database files because of a disk failure and you have to recover those files, the index wont be recovered. Thats a pretty small risk, because you can always create the index again. You just have to remember to do that.
The NOSORT option does exactly what its name implies. Normally, when you create an index, Oracle sorts the table based on the index columns. When you use NOSORT, Oracle skips the sort step and assumes that the data in the table is ordered correctly to start with. Needless to say, skipping the sort can save you significant time. If, while creating the index, Oracle discovers that the rows in the table are in fact not presorted, the operation will abort, and you will have to create the index in the usual manner.
389
You cant create all indexes while the table remains online. Whether you can use ONLINE depends on the exact mix of options that you are using when you create the index.
Youll learn more about how the optimizer works in Chapter 19, Tuning SQL Statements.
Index statistics arent maintained automatically. You have to periodically regenerate them using ANALYZE TABLE commands.
CrossReference
Chapter 18, Optimizers and Statistics, talks more about the importance of doing this.
Generating statistics via the ANALYZE command uses a significant amount of CPU and I/O resources. You end up reading through either the entire index or at least a significant part of it. When you create a new index, however, you have a perfect opportunity to sidestep this overhead by combining the generation of the statistics with the creation of the index. To do that, use the COMPUTE STATISTICS keyword in your CREATE command, as shown here:
CREATE UNIQUE INDEX animal_checkup_history ON checkup_history ( id_no, checkup_date ) TABLESPACE indx COMPUTE STATISTICS;
390
There is one disadvantage to the COMPUTE STATISTICS option, and that is that you cant mix it with the ONLINE option.
To create function-based indexes, you must have the QUERY REWRITE system privilege.
Once youve created an index such as the one shown previously, you can place the function call UPPER(animal_name) in the WHERE clause of a SELECT statement, and Oracle will be able to use the function-based index ANIMAL_NAME_UPPER to resolve the query. Without a function-based index, wrapping a function around a column name in the WHERE clause of a query would prevent any index on that column from ever being used.
391
392
When youre selecting the columns to be included in an index, click those columns in the order in which you want them to appear. If you click the wrong column, or if you click a column out of order, just click the column again to deselect it. As you click each column to be included in the index, Schema Manager displays the order on the right-hand side of the Create Index dialog box.
Browsing indexes
You can easily browse indexes using Schema Manager and quickly get a look at the definitions for any that interest you. Do this by expanding the Indexes folder and drilling down to the schema of interest. Figure 14-5 illustrates using Schema Manager to list the indexes in the SEAPARK schema.
If a particular index interests you, click that index, and Schema Manager displays the details in the right pane of the window.
393
Altering an Index
You can change an index after you create it, but for the most part, you are limited to changing only its physical characteristics. You cant add columns to an index. To do that, you would have to drop the index and re-create it. However, Oracle does allow you to make the following changes: Move an index to another tablespace Rebuild an index using different storage parameters Rename an index De-allocate unused space Many of the changes that you can make, such as relocating an index to another tablespace, actually require the complete re-creation of the index. However, Oracle automates the process and can often keep the index online and usable while the rebuild proceeds.
Note
When Oracle rebuilds an index, it does so using only the information in the index. If the index is corrupt (missing a value or containing a pointer to a nonexistent row), the corruption will still exist in the rebuilt index.
You can make many of the physical changes by using the REBUILD clause. When you rebuild an index, Oracle re-creates the index from scratch by using a new set of storage parameters. The following example rebuilds the animal_checkup_history index by changing a number of parameters and settings:
ALTER INDEX animal_checkup_history REBUILD TABLESPACE users ONLINE NOLOGGING STORAGE (INITIAL 5K NEXT 5K);
Lets go through these clauses one by one: ALTER INDEX animal_checkup_history REBUILD Specifies that you want to rebuild the index named an_chk_hist. TABLESPACE users Creates the new version of the index in the tablespace named USERS. ONLINE Specifies that you want users to be able to access the table while the rebuild is in progress.
394
Note
You cant rebuild an index online if you are also using the COMPUTE STATISTICS or REVERSE clause. Bitmapped indexes cannot be rebuilt online either.
NOLOGGING Prevents Oracle from recording the rebuild in the redo log. This will speed the rebuild process (less I/O), but if you ever need to recover the database from a backup and roll forward through the changes, the index rebuild will not be included. Instead, you may need to manually rebuild the index again. STORAGE (INITIAL 5K NEXT 5K) Specifies to allocate an initial extent of 5KB for the index. Subsequent extents will also be 5KB each. These values override the default settings for the tablespace in which the index is being created. Certain types of changes are not allowed when the ONLINE option is requested. For example, you cant compute statistics during an online rebuild. If you try to do a rebuild online that Oracle doesnt support, you will get the following error:
ORA-08108: may not build or rebuild this type of index online
If you get an ORA-08108 error, you will have to rebuild the index offline. This means that no one will be able to insert, update, or delete from the table until the rebuild is finished.
395
396
One task that Schema Manager cant perform is to rebuild an index. To do that, you have to write the ALTER INDEX REBUILD statement yourself and submit it using SQL*Plus.
Deleting an Index
You can use the DROP INDEX command to delete an index from a database. Consider this example:
DROP INDEX animal_checkup_history;
In Schema Manager, you can drop an index by right-clicking on the index name and selecting Remove from the pop-up menu.
Listing Indexes
If you need to find out information about indexes, you can query Oracles data dictionary. The data dictionary will tell you the names of the indexes in your database, the names of the columns that make up those indexes, and which options you chose to use when creating them. The following two data dictionary views are relevant to indexes:
DBA_INDEXES DBA_IND_COLUMNS
Returns one row for each index in the database Returns one row for each indexed column
If you dont have access to the DBA views, remember that you do have access to the ALL and USER views. The ALL_INDEXES view lists indexes on all tables to which you have access, and USER_INDEXES lists indexes that you own.
397
If you dont have access to the DBA_INDEXES view, use ALL_INDEXES instead. The DBA_INDEXES view contains many more columns than those listed here. The contents of most columns are self-evident from the column names.
You must order the results by COLUMN_POSITION to get them to list in the correct order. The DESCEND column tells you whether the index sorts a particular column in ascending order or descending order. Most indexes are sorted in ascending order.
Note
The COLUMN_NAME field in DBA_IND_COLUMNS is defined as a VARCHAR2(4000). Thats rather wide. If you are executing a query from SQL*Plus that includes that column, you may want to enter a command like COLUMN COLUMN_NAME FORMAT A30 to set the display width to a more reasonable value, which in this case would be 30 characters.
Summary
In this chapter, you learned: Indexes on a database function like an index in a book: They speed your access to the data in a table. Oracle supports two types of indexes: B*Tree and bitmapped. B*Tree indexes are structured in an inverse tree and are good all-around choices. Bitmapped indexes excel when you are querying columns containing a small number of distinct values (a Yes or No column, for example). Function-based indexes, a new feature in Oracle8i, allow you to create indexes based on the result of an expression applied to a column. Queries that use the same expression in their WHERE clause can take advantage of these indexes. The DBA_INDEXES view returns information about the indexes in a database. DBA_IND_COLUMNS tells you which columns make up an index.