Académique Documents
Professionnel Documents
Culture Documents
Change SQL prompt name SQL> set sqlprompt Manimara > Manimara > Manimara > 3. Switch to DOS prompt SQL> host 4. How do I eliminate the duplicate rows ? SQL> delete from table_name where rowid not in (select max(rowid) from table group by duplicate_values_field_name); or SQL> delete duplicate_values_field_name dv from table_name ta where rowid <(select min(rowid) from table_name tb where ta.dv=tb.dv); Example. Table Emp Empno Ename 101 Scott 102 Jiyo 103 Millor 104 Jiyo 105 Smith delete ename from emp a where rowid < ( select min(rowid) from emp b where a.ename = b.ename); The output like, Empno Ename 101 Scott 102 Millor 103 Jiyo 104 Smith 5. How do I display row number with records? To achive this use rownum pseudocolumn with query, like SQL> SQL> select rownum, ename from emp; Output: 1 Scott 2 Millor 3 Jiyo 4 Smith 6. Display the records between two range select rownum, empno, ename from emp where rowid in (select rowid from emp where rownum <=&upto
minus select rowid from emp where rownum<&Start); Enter value for upto: 10 Enter value for Start: 7 ROWNUM EMPNO ENAME --------- --------- ---------1 7782 CLARK 2 7788 SCOTT 3 7839 KING 4 7844 TURNER 7. I know the nvl function only allows the same data type(ie. number or char or date Nvl(comm, 0)), if commission is null then the text Not Applicable want to display, instead of blank space. How do I write the query? SQL> select nvl(to_char(comm.),'NA') from emp; Output : NVL(TO_CHAR(COMM),'NA') ----------------------NA 300 500 NA 1400 NA NA 8. Oracle cursor : Implicit & Explicit cursors Oracle uses work areas called private SQL areas to create SQL statements. PL/SQL construct to identify each and every work are used, is called as Cursor. For SQL queries returning a single row, PL/SQL declares all implicit cursors. For queries that returning more than one row, the cursor needs to be explicitly declared. 9. Explicit Cursor attributes There are four cursor attributes used in Oracle cursor_name%Found, cursor_name%NOTFOUND, cursor_name%ROWCOUNT, cursor_name%ISOPEN 10. Implicit Cursor attributes Same as explicit cursor but prefixed by the word SQL SQL%Found, SQL%NOTFOUND, SQL%ROWCOUNT, SQL%ISOPEN
Tips : 1. Here SQL%ISOPEN is false, because oracle automatically closed the implicit cursor after executing SQL statements. : 2. All are Boolean attributes. 11. Find out nth highest salary from emp table SELECT DISTINCT (a.sal) FROM EMP A WHERE &N = (SELECT COUNT (DISTINCT (b.sal)) FROM EMP B WHERE a.sal<=b.sal); Enter value for n: 2 SAL --------3700 12. To view installed Oracle version information SQL> select banner from v$version; 13. Display the number value in Words SQL> select sal, (to_char(to_date(sal,'j'), 'jsp')) from emp; the output like, SAL (TO_CHAR(TO_DATE(SAL,'J'),'JSP')) --------- ----------------------------------------------------800 eight hundred 1600 one thousand six hundred 1250 one thousand two hundred fifty If you want to add some text like, Rs. Three Thousand only. SQL> select sal "Salary ", (' Rs. '|| (to_char(to_date(sal,'j'), 'Jsp'))|| ' only.')) "Sal in Words" from emp / Salary Sal in Words ------- -----------------------------------------------------800 Rs. Eight Hundred only. 1600 Rs. One Thousand Six Hundred only. 1250 Rs. One Thousand Two Hundred Fifty only. 14. Display Odd/ Even number of records Odd number of records: select * from emp where (rowid,1) in (select rowid, mod(rownum,2) from emp); 1 3 5 Even number of records: select * from emp where (rowid,0) in (select rowid, mod(rownum,2) from emp)
2 4 6 15. Which date function returns number value? months_between 16. Any three PL/SQL Exceptions? Too_many_rows, No_Data_Found, Value_Error, Zero_Error, Others 17. What are PL/SQL Cursor Exceptions? Cursor_Already_Open, Invalid_Cursor 18. Other way to replace query result null value with a text SQL> Set NULL N/A to reset SQL> Set NULL 19. What are the more common pseudo-columns? SYSDATE, USER , UID, CURVAL, NEXTVAL, ROWID, ROWNUM 20. What is the output of SIGN function? 1 for positive value, 0 for Zero, -1 for Negative value. 21. What is the maximum number of triggers, can apply to a single table? 12 triggers.
What are the difference between DDL, DML and DCL commands?
DDL is Data Definition Language statements. Some examples:
CREATE - to create objects in the database ALTER - alters the structure of the database DROP - delete objects from the database TRUNCATE - remove all records from a table, including all spaces allocated for the records are removed COMMENT - add comments to the data dictionary GRANT - gives user's access privileges to database REVOKE - withdraw access privileges given with the GRANT command
How does one escape special characters when building SQL queries?
The LIKE keyword allows for string searches. The '_' wild card character is used to match exactly one character, '%' is used to match zero or more occurrences of any characters. These characters can be escaped in SQL. Example:
SELECT name FROM emp WHERE id LIKE '%\_%' ESCAPE '\';
SELECT 'Franks''s Oracle site' FROM DUAL; SELECT 'A ''quoted'' word.' FROM DUAL; SELECT 'A ''''double quoted'''' word.' FROM DUAL;
Method 2:
SQL> create table table_name2 as select distinct * from table_name1; SQL> drop table_name1; SQL> rename table_name2 to table_name1; SQL> -- Remember to recreate all indexes, constraints, triggers, etc on table...
Note: One can eliminate N^2 unnecessary operations by creating an index on the joined fields in the inner loop (no need to loop through the entire table on each pass by a record). This will speedup the deletion process. Note 2: If you are comparing NOT-NULL columns, use the NVL function. Remember that NULL is not equal to NULL. This should not be a problem as all key columns should be NOT NULL by definition. Back to top of file
Create your table with a NOT NULL column (say SEQNO). This column can now be populated with unique values:
SQL> UPDATE table_name SET seqno = ROWNUM;
How does one get the time difference between two date columns?
Look at this example query:
select floor(((date1-date2)*24*60*60)/3600) || ' HOURS ' || floor((((date1-date2)*24*60*60) floor(((date1-date2)*24*60*60)/3600)*3600)/60) || ' MINUTES ' || round((((date1-date2)*24*60*60) floor(((date1-date2)*24*60*60)/3600)*3600 (floor((((date1-date2)*24*60*60) floor(((date1-date2)*24*60*60)/3600)*3600)/60)*60))) || ' SECS ' time_difference from ...
If you don't want to go through the floor and ceiling math, try this method (contributed by Erik Wile):
select to_char(to_date('00:00:00','HH24:MI:SS') + (date1 - date2), 'HH24:MI:SS') time_difference from ...
Note that this query only uses the time portion of the date and ignores the date itself. It will thus never return a value bigger than 23:59:59. Back to top of file
SYSDATE SYSDATE+1/86400
SYSDATE+1/24
SYSDATE+1/1440
-------------------- -------------------- --------------------------------------03-Jul-2002 08:32:12 03-Jul-2002 09:32:12 03-Jul-2002 08:33:12 03Jul-2002 08:32:13
NOW
NOW_PLUS_30_SECS
Now Tomorow/ next day Seven days from now One hour from now Three hours from now An half hour from now 10 minutes from now 30 seconds from now Tomorrow at 12 midnight Tomorrow at 8 AM Next Monday at 12:00 noon
SYSDATE SYSDATE + 1 SYSDATE + 7 SYSDATE + 1/24 SYSDATE + 3/24 SYSDATE + 1/48 SYSDATE + 10/1440 SYSDATE + 30/86400 TRUNC(SYSDATE + 1) TRUNC(SYSDATE + 1) + 8/24 NEXT_DAY(TRUNC(SYSDATE), 'MONDAY') + 12/24
First day of next month at TRUNC(LAST_DAY(SYSDATE ) + 1) 12 midnight First day of the current month TRUNC(LAST_DAY(ADD_MONTHS(SYSDATE,-1))) + 1
The next Monday, TRUNC(LEAST(NEXT_DAY(sysdate,''MONDAY'' ),NEXT_DAY(sysdate,''WEDNE Wednesday or Friday at 9 SDAY''), NEXT_DAY(sysdate,''FRIDAY'' ))) + (9/24)
a.m
For equal size ranges it might be easier to calculate it with DECODE(TRUNC(value/range), 0, rate_0, 1, rate_1, ...). Eg.
select ename "Name", sal "Salary", decode( trunc(f2/1000, 0), 0, 0.0, 1, 0.1, 2, 0.2,
Note: In this first query we select one more than the required row number, then we select the required one. Its far better than using a MINUS operation.
Ravi Pachalla provided these solutions: SELECT f1 FROM t1 WHERE rowid = ( SELECT rowid FROM t1 WHERE MINUS SELECT rowid FROM t1 WHERE rownum < 10); rownum <= 10
Alternatively...
SELECT * FROM emp WHERE rownum=1 AND rowid NOT IN (SELECT rowid FROM emp WHERE rownum < 10);
Please note, there is no explicit row order in a relational database. However, this query is quite fun and may even help in the odd situation.
10
Note: the 101 is just one greater than the maximum row of the required rows (means x= 90, y=100, so the inner values is y+1).
Ravi Pachalla provided this solution: SELECT rownum, f1 FROM t1 GROUP BY rownum, f1 HAVING rownum BETWEEN 2 AND 4;
Another solution is to use the MINUS operation. For example, to display rows 5 to 7, construct a query like this:
SELECT * FROM WHERE tableX rowid in (
SELECT rowid FROM tableX WHERE rownum <= 7 MINUS SELECT rowid FROM tableX WHERE rownum < 5); Youssef Youssef provided this soluton: "this one was faster for me and allowed for sorting before
filtering by rownum. The inner query (table A) can be a series of tables joined together with any operation before the filtering by rownum is applied."
SELECT * FROM (SELECT a.*, rownum RN FROM (SELECT * FROM t1 ORDER BY key_column) a WHERE rownum <=7) WHERE rn >=5
Please note, there is no explicit row order in a relational database. However, this query is quite fun and may even help in the odd situation.
11
Please note, there is no explicit row order in a relational database. However, these queries are quite fun and may even help in the odd situation. Back to top of file
12
One can produce an indented report by using the level number to substring or lpad() a series of spaces, and concatenate that to the string. Look at this example:
select lpad(' ', LEVEL * 2) || ENAME ........
One uses the "start with" clause to specify the start of the tree. More than one record can match the starting condition. One disadvantage of having a "connect by prior" clause is that you cannot perform a join to other tables. The "connect by prior" clause is rarely implemented in the other database offerings. Trying to do this programmatically is difficult as one has to do the top level query first, then, for each of the records open a cursor to look for child nodes. One way of working around this is to use PL/SQL, open the driving cursor with the "connect by prior" statement, and the select matching records from other tables on a row-by-row basis, inserting the results into a temporary table for later retrieval. Back to top of file
13
sum(decode(deptno,20,sal)) DEPT20, sum(decode(deptno,30,sal)) DEPT30, sum(decode(deptno,40,sal)) DEPT40 FROM scott.emp GROUP BY job) ORDER BY 1;
JOB
DEPT10
DEPT20
DEPT30
DEPT40
--------- ---------- ---------- ---------- ---------ANALYST CLERK MANAGER PRESIDENT SALESMAN 1300 2450 5000 5600 6000 1900 2975 950 2850
select a, b, decode( abs(a-b), a-b, 'a > b', 0, 'a = b', 'a < b') from tableX;
select decode( GREATEST(A,B), A, 'A is greater OR EQUAL than B', 'B is greater than A')...
select decode( GREATEST(A,B), A, decode(A, B, 'A NOT GREATER THAN B', 'A GREATER THAN B'),
14
Note: The decode function is not ANSI SQL and is rarely implemented in other RDBMS offerings. It is one of the good things about Oracle, but use it sparingly if portability is required. From Oracle 8i one can also use CASE statements in SQL. Look at this example:
SELECT ename, CASE WHEN sal>1000 THEN 'Over paid' ELSE 'Under paid' END FROM emp;
How can one dump/ examine the exact content of a database column?
SELECT DUMP(col1) FROM tab1 WHERE cond1 = val1;
For this example the type is 96, indicating CHAR, and the last byte in the column is 32, which is the ASCII code for a space. This tells us that this column is blank-padded. Back to top of file
2. SQL> create table t2 as select <specific columns> from t1; SQL> drop table t1;
15
Other workarounds:
1. -- Use a view with correct column names... rename t1 to t1_base; create view t1 <column list with new name> as select * from t1_base;
2. -- Recreate the table with correct column names... create table t2 <column list with new name> as select * from t1; drop table t1; rename t2 to t1;
3. -- Add a column with a new name and drop an old column... alter table t1 add ( newcolame datatype ); update t1 set newcolname=oldcolname; alter table t1 drop column oldcolname;
16
Perform an "ALTER SEQUENCE ... NOCACHE" to unload the unused cached sequence numbers from the Oracle library cache. This way, no cached numbers will be lost. If you then select from the USER_SEQUENCES dictionary view, you will see the correct high water mark value that would be returned for the next NEXTVALL call. Afterwards, perform an "ALTER SEQUENCE ... CACHE" to restore caching. You can use the above technique to prevent sequence number loss before a SHUTDOWN ABORT, or any other operation that would cause gaps in sequence values. Back to top of file
Note: If you run Oracle8, convert your LONGs to LOBs, as it can be replicated.
1.
What if you want a random number in the where clause, like, so you can choose 1/10 of the rows at random? Use a hash instead.
select * from emp where dbms_utility.get_hash_value(dump(rowid),0,100)<10;
2. The biggest difference between Oracle and everyone else is the locking mechanism. Everyone else gets share locks on data when they query it. That means other queries can also see the data, but queries block writes and writes block queries. (Update: the default for everyone else, for for example for DB2, is for queries not to block writes. Instead they show any data committed at the time the query internally fetches the data. This means each query result can be internally inconsistent, but it allows greater concurrency.) Oracle doesn't have share locks. Instead it uses snapshots, which are points in time. A query looks at the data as of some point in time. If someone changes the data later, that's OK, the query still sees the old version of the data. Each query result is internally consistent, but consecutive queries may not be consistent with one another. Queries don't block writes and writes don't block queries. Writes still block writes. Both share locks and
17
snapshots are reasonable concurrency models, but they aren't the same. It's wrong to think in terms of one when working with the other. 3. Derived tables (from-clause subqueries, inline views):
4. 5. 6. select * from (select * from emp); insert into (select deptno from dept) select trunc(a) from (select sum(empno)/5000 a from emp);
This has the same effect as if you used a view. It is useful for queries that have a repeated complex expression; for example
select x from (select avg(sin(empno)) x from emp group by deptno) where x > 0;
It's also useful when you want to use one of the values found by a subquery, for example when deleting all outdated entries from a log:
delete from log where rowid in (select rowid from log minus select a.rowid from log a, (select id, max(time) mtime from log group by id) b where a.id = b.id and a.time = b.mtime);
As of 9i, inline views can also be expressed with a "WITH clause", for example
with sub1 as (select deptno, avg(sin(empno)) x from emp group by deptno) select a.deptno, a.x, b.x from sub1 a, sub1 b where a.deptno > b.deptno;
7. When should varchar2 be used instead of char? Always. Varchar2 is always faster, more space efficient, less buggy, and its comparison rules are more likely to be what you expect. 8. Is SQL case sensitive? No. Can I give tables case-sensitive names with weird characters in them? Yes. Quote them, like so:
9. create table "Table" ("My column is KEWL!!!" int);
10. How do I insert data into an nvarchar2 column? From 9i onwards, just like any other character column, and the character set is guaranteed to hold the Unicode repertoire and measure characters using UCS2 codepoints. There is implicit conversion between char and nchar. Before 9i, avoid nvarchar2. If you really must use it, you insert like so: "insert into nemp (ename) values (n'SCOTT')". 11. Building a big bogus table:
12. 13. create table a (a1 number, a2 varchar2(50)); begin
18
14. for i in 1..10000 loop 15. insert into a values (i, 'I am a unique and extraordinary individual'); 16. end loop; 17. commit; 18. end; 19. / 20. alter table a add constraint apk primary key (a1); 21. select count(*) from a; -- 10000 rows
22. Like the making of sausage and politics, it is best not to understand the making of Oracle data types.
23. select dump(1), dump(-1), dump(sysdate) from dual;
Ouch! Ouch! You're twisting my arm. I'll explain, for Oracle numbers at least. 1. All data on disk looks like <length><stuff> 2. All data on disk is byte-sortable (other than the lengths). 3. Digits are base 100, and the first byte is the (base-100) exponent. For example, the number 1 has exponent 193, 10 also has 193, 100 has 194, 0.01 has 192. 4. Negative numbers complement the exponent. 62 for -1, 63 for -.01, 61 for -100. 5. Why? Because the exponent has to be byte-sortable. 6. Digits. Positive digits are 1..100. Negative digits are 101..2. Again, to be byte-sortable. Base-100 digits use about 7 out of 8 bits of each byte, so this is a reasonably efficient way of storing numbers. 7. Negative numbers have a trailing byte of value 102, unless the maximum number of digit bytes (20) are used. To make things byte sortable. -1 > -1.01, right? But (62,100) < (62,100,100). The trailing 102 corrects that: (62,100,102) > (62,100,100,102). 8. But why isn't the exponent for 1 192, for -1 63, and why are the digits 1..100 and 101..2 instead of 0..99 and 99..0? Because, way back in prehistory, Oracle used 0 as a null terminator, so you couldn't store 0 on disk for any other purpose. So 1 was added to everything. There's no reason not to store 0 on disk anymore because all values store their lengths separately, but the number format hasn't changed. 9. My guess is that negative infinity was originally (1,1), pushing the negative numbers out to 101..2 instead of 100..1. That's not what negative infinity is now, and SQL doesn't expose the infinities anyhow. 24. To find out how a query is executed, create a plan table with UTLXPLAN.SQL, fill the table with
25. explain plan for select ... ;
and then look at the results with a connect-by script, something like this:
select substrb(to_char(cost),1,6) cost, substrb(to_char(cardinality),1,6) card,
19
',1,level) ||
substrb(options,1,15) subtype, substrb(object_owner,1,5) owner, substrb(object_name,1,5) name, substrb(object_node,1,5) link from plan_table connect by parent_id = prior id start with id = 0; delete from plan_table;
33. Mutating/Constraining errors. Before 8i: o o o You only get these when you modify a table being read or you read a table being modified. Foreign key constraints read some tables implicitly. A table that might be deleted from due to delete cascade counts as modifying. You only get these errors from per-row triggers, not from after statement or before statement triggers (unless they are under per-row triggers). Delete cascade enforcement fires statement triggers every time it delete cascades a row, so all those statement triggers are subject to mutating errors too.
When you get the error, use your per-row trigger to fill a PL/SQL table instead. Then use an after-statement trigger to apply the changes based on that PL/SQL table. There are generic packages out there for generating the triggers, PL/SQL tables, and so forth needed to implement update cascade. Often you need more PL/SQL variables to keep track of whether you are already under a trigger, to avoid recursion. After 8i: Half the mutating errors go away. Specifically, it is still illegal to read or modify a mutating table, but modifying a table that is being read is fine. This allows the obvious before-row trigger implementation of update cascade, for example. The problem of delete cascade firing statement triggers under row triggers is fixed in Oracle8i too.
2.
How to enable constraints without locking tables for hours on end (using Oracle8 or later). First put all constraints in the ENABLE NOVALIDATE mode, then
20
ENABLE (or VALIDATE) them individually. The ENABLE NOVALIDATE modifies only metadata. The VALIDATE hold no locks, can run in parallel, and many such enables can be run concurrently. 3. How to store data case sensitive but to index it case insensitive (from 8i on): Set compatible=8.1.0.0.0, query_rewrite_integrity=trusted, and query_rewrite_enabled=true.
4. 5. 6.
create table a (a1 varchar2(10)); create index ai on a (upper(a1)); analyze table a compute statistics;
This will use the cost-based optimizer. From what I've heard, that's OK. As of Oracle8i the cost-based optimizer is a good thing, and it can use several access methods the rule-based optimizer doesn't know about.
21
ans:B
2.select empname from employess empname like 'jack%'; what are the values given below can be returned by the query? 1.jackson and sonjack 2.jackson 3.sonjack 4.none fo the above ans :2 3)Column alias name can be used for where clause ? 1.true 2.false ans :2 )where clause can not appear after group by clause? 1.true 2.false ans:1 5).How many joins required to avod cartesian product? ans: n-1 joins for n tables 6).Which of these are set operators? A. B. C. D. E. union union all intersect minus all of the above
ans :E 7).A single row subquery may return many values. A. B. true false
ans :B 8).select regionid,regioname as region from regions order by 1,regions group by regionid; Will the above statement successfully execute?
A. B.
true false
22
--Fred
Materialized View
Last Updated: 1/7/2002 Applies to: Oracle 8+ Oracle 8i introduced a new feature called a "materialized view". You define it just like any other view, except that you add the keyword MATERIALIZED:
CREATE MATERIALIZED VIEW view_name
23
A materialized view is like a combination of a table and a view. Like a view, it is defined as a logical view into the data of one or more tables. When you update the tables, subsequent queries of the view see the updated data. However, like a table, its data is stored in the database. Also, like a table, it is faster if you define indexes for it. A regular view is stored as a mapping of data from tables. When you modify the data in the tables, the view is completely ignored. When you access the view, it joins the data currently in the tables, and returns the data you requested. A materialized view is stored as such a mapping along with a copy of the actual data from the tables. When you modify the data in the tables, the view's copy of the data is also updated. When you access the view, the data is drawn directly from the copy. Thus a materialized view makes table updates a little slower, but makes view queries much faster. It also consumes additional space in the database. You could accomplish the same effect by defining an additional table instead of the view, and using triggers on the component tables to update it each time they are changed. However, using a materialized view is more convenient, more efficient, and clearer to the next person who has to maintain your database. Thanks to Andy Glick for sending me a sample of a materialized view from his application! --Fred
data. For example, you can find all references to a given table in all stored procedures, functions, and packages with the following query:
SELECT * FROM WHERE all_source LOWER(text) LIKE '%table_name%'
If you don't have SELECT access to the all_source table, you can try user_source or dba_source instead. Thanks to Chris Boos, the most knowledgeable Oracle guru I know, for this and many other tips! Tip within a tip: To prevent the LIKE clause from treating underscore as a wildcard, use the ESCAPE clause, as:
SELECT * FROM WHERE all_source LOWER(text) LIKE '%table\_name%' ESCAPE '\
For complete details on Oracle indexing for high performance, see my book "Oracle Tuning: The Definitive Reference". Oracle includes numerous data structures to improve the speed of Oracle SQL queries. Taking advantage of the low cost of disk storage, Oracle includes many new indexing algorithms that dramatically increase the speed with which Oracle queries are serviced. This article explores the internals of Oracle indexing; reviews the standard b-tree index, bitmap indexes, function-based indexes, and index-only tables (IOTs); and demonstrates how these indexes may dramatically increase the speed of Oracle SQL queries. Oracle uses indexes to avoid the need for large-table, full-table scans and disk sorts, which are required when the SQL optimizer cannot find an efficient way to service the SQL query. I begin our look at Oracle indexing with a review of standard Oracle b-tree index methodologies.
25
servicing simple queries. The b-tree index was introduced in the earliest releases of Oracle and remains widely used with Oracle. B-tree indexes are used to avoid large sorting operations. For example, a SQL query requiring 10,000 rows to be presented in sorted order will often use a b-tree index to avoid the very large sort required to deliver the data to the end user.
While b-tree indexes are great for simple queries, they are not very good for the following situations:
Low-cardinality columnscolumns with less than 200 distinct values do not have the selectivity required in order to benefit from standard b-tree index structures. No support for SQL functionsB-tree indexes are not able to support SQL queries using Oracle's built-in functions. Oracle9i provides a variety of built-in functions that allow SQL statements to query on a piece of an indexed column or on any one of a number of transformations against the indexed column.
26
Prior to Oracle9i, the Oracle SQL optimizer had to perform time-consuming long-table, full-table scans due to these shortcomings. Consequently, it was no surprise when Oracle introduced more robust types of indexing structures.
Bitmapped indexes
Oracle bitmap indexes are very different from standard b-tree indexes. In bitmap structures, a two-dimensional array is created with one column for every row in the table being indexed. Each column represents a distinct value within the bitmapped index. This two-dimensional array represents each value within the index multiplied by the number of rows in the table. At row retrieval time, Oracle decompresses the bitmap into the RAM data buffers so it can be rapidly scanned for matching values. These matching values are delivered to Oracle in the form of a Row-ID list, and these Row-ID values may directly access the required information. The real benefit of bitmapped indexing occurs when one table includes multiple bitmapped indexes. Each individual column may have low cardinality. The creation of multiple bitmapped indexes provides a very powerful method for rapidly answering difficult SQL queries. For example, assume there is a motor vehicle database with numerous low-cardinality columns such as car_color, car_make, car_model, and car_year. Each column contains less than 100 distinct values by themselves, and a b-tree index would be fairly useless in a database of 20 million vehicles. However, combining these indexes together in a query can provide blistering response times a lot faster than the traditional method of reading each one of the 20 million rows in the base table. For example, assume we wanted to find old blue Toyota Corollas manufactured in 1981:
select license_plat_nbr from vehicle where color = blue and make = toyota and year = 1981;
Oracle uses a specialized optimizer method called a bitmapped index merge to service this query. In a bitmapped index merge, each Row-ID, or RID, list is built independently by using the bitmaps, and a special merge routine is used in order to compare the RID lists and find the intersecting values. Using this methodology, Oracle can provide sub-second response time when working against multiple low-cardinality columns:
27
Function-based indexes
One of the most important advances in Oracle indexing is the introduction of function-based indexing. Function-based indexes allow creation of indexes on expressions, internal functions, and user-written functions in PL/SQL and Java. Function-based indexes ensure that the Oracle designer is able to use an index for its query. Prior to Oracle8, the use of a built-in function would not be able to match the performance of an index. Consequently, Oracle would perform the dreaded full-table scan. Examples of SQL with function-based queries might include the following:
Select Select Select Select * * * * from from from from customer customer customer customer where where where where substr(cust_name,1,4) = BURL; to_char(order_date,MM) = 01; upper(cust_name) = JONES; initcap(first_name) = Mike;
In Oracle, Oracle always interrogates the where clause of the SQL statement to see if a matching index exists. By using function-based indexes, the Oracle designer can create a matching index that exactly matches the predicates within the SQL where clause. This ensures that the query is retrieved with a minimal amount of disk I/O and the fastest possible speed.
Index-only tables
Beginning with Oracle8, Oracle recognized that a table with an index on every column did not require table rows. In other words, Oracle recognized that by using a special table-access method called an index fast full scan, the index could be queried without actually touching the data itself. Oracle codified this idea with its use of index-only table (IOT) structure. When using an IOT, Oracle does not create the actual table but instead keeps all of the required information inside the Oracle index. At query time, the Oracle SQL optimizer recognizes that all of the values necessary to service the query exist within the index tree, at which time the Oracle cost-based optimizer has a choice of either reading through the index tree nodes to pull the information in sorted order or invoke an index fast full scan, which will read the table in the same fashion as a full table scan, using sequential prefetch (as defined by the db_file_multiblock_read_count parameter). The
28
multiblock read facility allows Oracle to very quickly scan index blocks in linear order, quickly reading every block within the index tablespace. Here is an example of the syntax to create an IOT.
CREATE TABLE emp_iot ( emp_id number, ename varchar2(20), sal number(9,2), deptno number, CONSTRAINT pk_emp_iot_index PRIMARY KEY (emp_id) ) ORGANIZATION index TABLESPACE spc_demo_ts_01 PCTHRESHOLD 20 INCLUDING ename;
Index performance
Oracle indexes can greatly improve query performance but there are some important indexing concepts to understand.
Indexes and blocksize Indexes that experience lots of index range scans of index fast full scans (as evidence by multiblock reads) will greatly benefit from residing in a 32k blocksize. Today, most Oracle tuning experts utilize the multiple blocksize feature of Oracle because it provides buffer segregation and the ability to place objects with the most appropriate blocksize to reduce buffer waste. Some of the world record Oracle benchmarks use very large data buffers and multiple blocksizes. According to an article by Christopher Foot, author of the OCP Instructors Guide for Oracle DBA Certification, larger block sizes can help in certain situations: "A bigger block size means more space for key storage in the branch nodes of B-tree indexes, which reduces index height and improves the performance of indexed queries." In any case, there appears to be evidence that block size affects the tree structure, which supports the argument that data blocks affect the structure of the tree. Indexes and clustering The CBO's decision to perform a full-table vs. an index range scan is influenced by the clustering_factor (located inside the dba_indexes view), db_block_size, and avg_row_len. It is important to understand how the CBO uses these statistics to determine the fastest way to deliver the desired rows. Conversely, a high clustering_factor, where the value approaches the number of rows in the table (num_rows), indicates that the rows are not in the same sequence as the index, and additional I/O will be required for index range scans. As the clustering_factor approaches the number of rows in the table, the rows are out of sync with the index.
29
Oracle Metalink Note:223117.1 has some great advice for tuning-down db file sequential read waits by table reorganization in row-order: - If Index Range scans are involved, more blocks than necessary could be being visited if the index is unselective: by forcing or enabling the use of a more selective index, we can access the same table data by visiting fewer index blocks (and doing fewer physical I/Os). - If the index being used has a large Clustering Factor, then more table data blocks have to be visited in order to get the rows in each Index block: by rebuilding the table with its rows sorted by the particular index columns we can reduce the Clustering Factor and hence the number of table data blocks that we have to visit for each index block. This validates the assertion that the physical ordering of table rows can reduce I/O (and stress on the database) for many SQL queries.
Also see: Oracle SQL clustering_factor tuning index access Oracle SQL clustering_factor The importance of clustering_factor in multi-block I/O Oracle index rebuilding - indexes rebuild script Oracle table index rebuilding benefits
Conclusion
Oracle dominates the market for relational database technology, so Oracle designers must be aware of the specialized index structures and fully understand how they can be used to improve the performance of all Oracle SQL queries. Many of these techniques are discussed my book "Oracle Tuning: The Definitive Reference". This text details the process of creating all of Oracle's index tree structures and offers specialized tips and techniques for ensuring SQL queries are serviced using the fastest and most efficient indexing structure.
30
returns a value of ONE HUNDRED THREE THOUSAND FOUR HUNDRED SIXTYFIVE If we break the statement into each component function, then what happens is :
the inner TO_CHAR simply converts the number (which would probably be a numeric variable in practice) to CHAR so some magic can happen ... the TO_DATE converts the CHAR using the J (Julian day) format. (the Julian day is the number of days since January 1, 4712BC, which is when SQL*Plus was invented), having established the date value, we then convert that date back to a Julian day. Because the TO_CHAR in this case is used in DATE context, we can use the J mask to duplicate the original value, and append the SP (spell) format mask. 'Spell" does exactly that - it converts the number to words, hence the string value above.
and
SELECT TO_CHAR ( SYSDATE, 'DDSPTH') FROM dual; SIXTH --returns TWENTY-
Some simple manipulations can be included with the base conversion to cover floating numbers or currencies (email brianm@lt.com.au for source), eg. 103465.27 becomes ONE HUNDRED AND THREE THOUSAND FOUR HUNDRED AND SIXTY-FIVE DOLLARS AND TWENTY-SEVEN CENTS. One covenant however : if in your mad appreciation of this trivia you want to send me a cheque for more than $5,373,484.00, then you'll have to write it manually, or send more than one cheque! 31
SQL*Plus restricts Julian days to between 1 and 5373484, which won't be a problem for most applications, but should be borne in mind before using the technique in anger. 5373484 represents 31-Dec-9999, so this may be Oracle's way of introducing us to a Year 10K problem! Column Count in Oracle Table
11/4/2002 By ITtoolbox Popular Q&A Team for ITtoolbox as adapted from ORACLE-DEV-L discussion group Summary: How do you count the number of columns in a database table? Full Article: Disclaimer: Contents are not reviewed for correctness and are not endorsed or recommended by ITtoolbox or any vendor. Popular Q&A contents include summarized information from Oracle-Dev-L discussion unless otherwise noted.
1) Adapted from response by Dragan Pavlovic on Thursday, October 31, 2002
List Table Names, Rows, and Created Date This tip comes from Pasupuleti Sailaja, in Hyderabad, Andhra Pradesh, India.
-and table created date in current login user for any ORACLE version -- Usage : filename DIR.SQL Step 1) All lines from given program save with
-Step 2) Run the file DIR.SQL by giving START DIR (or) @ DIR at SQL *Plus prompt --- Author 500072, India. : i.e. SQL> @ DIR Pasupuleti Sailaja, ORACLE favorite, Hyderabad-
32
SAILAJAMAIL@YAHOO.COM
-- Program :
declare rs cur rp trs n un begin dbms_output.put_line(rpad('Table Name',40)||' Number of Rows Created Date'); dbms_output.put_line(rpad('-',73,'-')); cur:= dbms_sql.open_cursor; for t in (select object_name, created from user_objects where object_type='TABLE') loop dbms_sql.parse(cur,'select count(*) from ' || t.object_name, dbms_sql.v7); dbms_sql.define_column(cur, 1, rs); rp:= dbms_sql.execute(cur); n:=dbms_sql.fetch_rows(cur); dbms_sql.column_value(cur, 1, rs); dbms_output.put_line(rpad(t.object_name,48,'.')|| rpad(rs,15,'.')||t.created); end loop; dbms_sql.close_cursor(cur); select select count(*) user into into un n from tab where tabtype='TABLE'; integer; integer; integer; integer; integer; varchar2(30);
from
dual;
33
set
serveroutput off
feedback
on
feedback 6
-- Example Output:
Table Name Created Date Number of Rows
-----------------------------------------------------------------------ACCTS...........................................5.............. 29-JUN-03 ACCT_ADDRS......................................5.............. 29-JUN-03 BONUS...........................................0.............. 09-AUG-00 CHESS_SAVE......................................0.............. 29-JUN-03 CHESS_SAVE_BOARDSTATE...........................0.............. 29-JUN-03 CHESS_SAVE_CAPTURED.............................0.............. 29-JUN-03 CHESS_SAVE_PLAYER...............................0.............. 29-JUN-03 CITIES..........................................205............ 29-JUN-03 COMPANY_SUMMARY.................................3.............. 29-JUN-03 CUSTOMER........................................9.............. 09-AUG-00 CUSTOMERS.......................................14............. 29-JUN-03 DEMOKIT_CLASSES.................................9.............. 29-JUN-03 DEMOKIT_DEMOS...................................23............. 29-JUN-03 DEMOKIT_DEMO_SCRIPTS............................14............. 29-JUN-03
34
DEMOKIT_INSTALLED_SCRIPTS.......................5.............. 29-JUN-03 DEMOKIT_SCRIPTS.................................10............. 29-JUN-03 DEPT............................................4.............. 09-AUG-00 DUMMY...........................................1.............. 09-AUG-00 DUPEMP..........................................7168........... 12-AUG-00 EMP.............................................14............. 09-AUG-00 FFI$FUNCTION....................................2.............. 29-JUN-03 FFI$FUNCTIONARGS................................10............. 29-JUN-03 FFI$LIBRARY.....................................1.............. 29-JUN-03 FFI$TYPEDEF.....................................14............. 29-JUN-03 FUNDS...........................................5.............. 29-JUN-03 FUND_CONTRIB....................................16............. 29-JUN-03 FUND_XACT.......................................45............. 29-JUN-03 F_EMPCOMP.......................................2.............. 29-JUN-03 F_XACT_TYPE.....................................7.............. 29-JUN-03 GAME_SEMAPHORE..................................0.............. 29-JUN-03 INDCAT..........................................11............. 29-JUN-03 INVINFO.........................................5.............. 29-JUN-03 INVREQUEST......................................12............. 29-JUN-03
35
ITEM............................................0.............. 09-AUG-00 MENU_CAT........................................7.............. 29-JUN-03 MENU_ITM........................................35............. 29-JUN-03 MODE_BUTTON.....................................9.............. 29-JUN-03 ORD.............................................21............. 09-AUG-00 ORDER_HISTORY...................................5.............. 29-JUN-03 ORDPICT.........................................21............. 29-JUN-03 PORTFOLIO.......................................39............. 29-JUN-03 PRICE...........................................17............. 09-AUG-00 PRODUCT.........................................10............. 09-AUG-00 RCL_CLASSIFICATIONS.............................6.............. 29-JUN-03 RCL_COMPONENTS..................................7.............. 29-JUN-03 RCL_COMPONENT_DEPENDANCIES......................6.............. 29-JUN-03 RCL_FRAGMENTS...................................31............. 29-JUN-03 RCL_INSTRUCTIONS................................57............. 29-JUN-03 RCL_INSTRUCTIONS_TEXT...........................520............ 29-JUN-03 RCL_MODULES.....................................31............. 29-JUN-03 SALES_REVENUE...................................16............. 29-JUN-03 SALGRADE........................................5.............. 09-AUG-00
36
STOCKS..........................................328............ 29-JUN-03 STOCK_HISTORY...................................11............. 29-JUN-03 TABLE_CONFIG....................................11............. 29-JUN-03 USA.............................................50............. 29-JUN-03 -----------------------------------------------------------------------SCOTT User contain 56 Table(s)
37
System state
This is useful for determining the cause of database hangs. Take 3 successive system state dumps at 10 minute intervals. alter session set events 'immediate trace name SYSTEMSTATE level 10';
Process state
alter session set events 'immediate trace name PROCESSSTATE level 10';
Library cache
alter session set events 'immediate trace name library_cache level 10';
38
assume that the original database is called PROD and you want to create a TEST duplicate database. 1.) Copy production database files and init.ora The first step is to locate and copy all database files to their new location. You can use the view V$DATAFILE in the PROD database to locate these files. Before running the query from V$DATAFILE, ensure that you are connected to the PROD database by selecting from V$DATABASE:
SQL> select name from v$database; NAME --------------------------------------PROD SQL> select name from v$datafile; NAME --------------------------------------/u08/app/oradata/PROD/system01.dbf /u06/app/oradata/PROD/rbs01.dbf /u07/app/oradata/PROD/temp01.dbf /u10/app/oradata/PROD/userd01.dbf /u09/app/oradata/PROD/userx01.dbf
After recording these files, shutdown the PROD database and perform an operating system copy of all database files to another location and/or machine. In my example, I will copy all datafiles to a new location as shown in the following table: Old Location New Location
/u08/app/oradata/PROD/system01.dbf /u08/app/oradata/TEST/system01.dbf /u06/app/oradata/PROD/rbs01.dbf /u07/app/oradata/PROD/temp01.dbf /u10/app/oradata/PROD/userd01.dbf /u09/app/oradata/PROD/userx01.dbf /u06/app/oradata/TEST/rbs01.dbf /u07/app/oradata/TEST/temp01.dbf /u10/app/oradata/TEST/userd01.dbf /u09/app/oradata/TEST/userx01.dbf
After copying all files to their new location, startup the PROD database. From the production database, get a copy of the initPROD.ora file and copy it to initTEST.ora. In the initTEST.ora file, change the value of "db_name" from PROD to TEST. Keep in mind that you may also need to change:
39
If the TEST database is going to be on a different machine, copy the initTEST.ora file to that machine in the proper directory. 2.) Create the script that will re-create the controlfile Using SVRMGR on the PROD database, create a script that will be able to re-create the controlfile for the database.
PROD on testdb: svrmgrl SVRMGR> connect internal Connected. SVRMGR> alter database backup controlfile to trace; Statement processed.
The above statement will put a text copy of the controlfile in the USER_DUMP_DEST directory. You will need to search for the newest trace file in this directory. In UNIX you can use the "ls -lt" command. Once you find the correct trace file, rename it to cr_control.sql and edit it as follows: Remove everything up to the "START NOMOUNT" statement and everything after the semicolon at the end of the "CREATE CONTROLFILE" statement. Edit the line starting with "CREATE CONTROLFILE" and replace the word "REUSE" with the word "SET" right before the keyword DATABASE. On the same line, modify the database name changing it from PROD to TEST. On the same line, change the keyword NORESETLOGS to RESETLOGS.
40
'/u04/app/oradata/TEST/redo_g03b.log', '/u05/app/oradata/TEST/redo_g03c.log' ) SIZE 200K DATAFILE '/u08/app/oradata/TEST/system01.dbf', '/u06/app/oradata/TEST/rbs01.dbf', '/u07/app/oradata/TEST/temp01.dbf', '/u10/app/oradata/TEST/userd01.dbf', '/u09/app/oradata/TEST/userx01.dbf' ;
If the TEST database is on a different machine move this file to that machine. 3.) Create the new controlfile for TEST Make sure that your Oracle environment variable "ORACLE_SID" is set to TEST. (i.e. export ORACLE_SID=TEST). Now use SVRMGR and the CREATE CONTROLFILE script (cr_control.sql) to create your controlfile for TEST:
TEST on testdb: svrmgrl SVRMGR> connect internal Connected to an idle instance. SVRMGR> @cr_control ORACLE instance started. Total System Global Area 32798752 Fixed Size 39816 Variable Size 22600856 Database Buffers 9994240 Redo Buffers 163840 Statement processed. SVRMGR>
NOTE: Stay logged into SVRMGR and proceed to the next step. 4.) Open the TEST database Before opening the TEST database, you will need to perform incomplete recovery. After recovery you can open the database using the RESETLOGS option as show below:
SVRMGR> alter database recover database until cancel using backup controlfile; SVRMGR> alter database recover cancel; Statement processed. SVRMGR> alter database open resetlogs; Statement processed. SVRMGR>
You can verify that the database was renamed to TEST by querying from V$DATABASE:
SVRMGR> select name from v$database; NAME --------TEST 1 row selected.
41
SVRMGR>
42