A Bag of Tricks and Tips for DBAs and Developers Ari Kaplan Independent Consultant EOUG Madrid 2000 Introduction Most books and documentation are set up like reference materials Training courses focus on the fundamental aspects of products in a limited time Hands-on experience is one of the best ways to learn tricks to maximize your use of Oracle Dumping the Contents of a Table into an ASCII Delimited File Why do it? Export/Import are used only with Oracle databases You cannot always Export from one Oracle version and Import into another ASCII files can be used to copy data from Oracle to other databases: Sybase Informix Microsoft Access Paradox Microsoft SQL Server Microsoft Excel email programs Paradox Foxpro Dumping the Contents of a Table into an ASCII Delimited File To concatenate two columns in SQL statement, you use the concatenation symbol: || For example, assume there is a table such as that below: EMPLOYEES Table NAME David Max Ari Kaplan Mickey Mouse Dilbert AGE 27 28 100 3 DEPARTMENT 241 242 242 250 You can spool the table with one record per line with the following SQL: SELECT NAME||AGE||DEPARTMENT FROM EMPLOYEES; Dumping the Contents of a Table into an ASCII Delimited File The result of SELECT NAME||AGE||DEPARTMENT FROM EMPLOYEES; will be: David Max27241 Ari Kaplan28242 Mickey Mouse100242 Dilbert3250 You can see that the format does not separate columns. Typical formats include tabs to separate columns. In Oracle, this is represented with a CHR(9). So, a better syntax would be the following SQL: SELECT NAME||CHR(9)||AGE||CHR(9)||DEPARTMENT FROM EMPLOYEES; The result will be: David Max Ari Kaplan Mickey Mouse Dilbert 27 28 100 3 241 242 242 250 Dumping the Contents of a Table into an ASCII Delimited File 1) SET HEADER OFF SET PAGESIZE 0 SET FEEDBACK OFF SET TRIMSPOOL ON 2) SPOOL file_name.txt <SQL statement> 3) SPOOL OFF 4) For tables where the length each record is greater than 80, be sure to set the LINESIZE value large enough. Otherwise Oracle will break up the lines every 80 characters, making the file not easily readable by other applications. Getting Around LONG Datatype Limitations Limitations: LONG columns cannot do string manipulations The CREATE TABLE table_A AS SELECT * FROM table_b cannot be done if table_b has a LONG column Snapshots and Replication have problems with LONG columns Limit of one LONG column per table Overcoming the LONG columns: Use BLOB datatypes and procedures with Oracle8. Oracle has already announced that it will discontinue LONG datatypes in a future release. You can convert LONG to LOB with the TO_LOB function. Use PL/SQL to manipulate LONG datatype Getting around LONG Datatype Limitations Assume that there is a table: RESUME_BANK Name VARCHAR2(40) Department NUMBER Resume_text LONG (stores the resume text) Assume that you want to review all resumes with the word “ORACLE” in it. Normally, you would issue: SELECT resume_text FROM resume_bank WHERE UPPER(resume_bank) like ‘%ORACLE%’; The result would be the ORA-932 “Inconsistent Datatypes”. Getting Around LONG Datatype Limitations To find all such resume records with the LONG datatype in a table, you must use PL/SQL. Below is a sample program that will do this by selecting one resume from the resume bank: SET SERVEROUTPUT ON SIZE 100000; SET LONG 100000; DECLARE LONG_var LONG; resume_name varchar2(40); CURSOR x IS SELECT name, resume_text FROM RESUME_BANK; BEGIN FOR cur_record in X resume_name := cur_record.name; LONG_var := cur_record.LONG_var; IF UPPER(resume_name) like ‘%ORACLE%’ THEN /* Print the resume to the screen */ DBMS_OUTPUT.PUT_LINE(‘Below is the resume of ‘||resume_name|| ’ that contains the word ORACLE:’); DBMS_OUTPUT.PUT_LINE(LONG_var); ENDIF; END LOOP; END; / Using SQL to Dynamically Generate SQL Scripts Why do this? The syntax to create a public synonym follows: CREATE PUBLIC SYNONYM synonym_name FOR owner.table_name; The following statement will create a series of “CREATE PUBLIC SYNONYM” statements: SELECT ‘CREATE PUBLIC SYNONYM ‘|| object_name || ‘ FOR ‘ || owner || ‘.’ || object_name || ‘;’ FROM ALL_OBJECTS WHERE OWNER = ‘IOUG’; This will produce an output similar to: CREATE PUBLIC SYNONYM ATTENDEE FOR IOUG.ATTENDEE; CREATE PUBLIC SYNONYM HOTEL_ID FOR IOUG.HOTEL_ID; CREATE PUBLIC SYNONYM CREDIT_CARD_NBR FOR IOUG. CREDIT_CARD_NBR; Notice how there is a space in the original SQL statement between the FOR and the preceding quote. Without the space, the output would look like... Using SQL to Dynamically Generate SQL Scripts The output from the previous SQL: CREATE PUBLIC SYNONYM ATTENDEEFOR IOUG.ATTENDEE; CREATE PUBLIC SYNONYM HOTEL_IDFOR IOUG.HOTEL_ID; CREATE PUBLIC SYNONYM CREDIT_CARD_NBRFOR IOUG. CREDIT_CARD_NBR; You can see how you must be careful of spaces and quotes. Also, note the final concatenation with the semicolon (;) 1) SET HEADER OFF SET PAGESIZE 0 SET FEEDBACK OFF 2) SPOOL file_name.sql <SQL statement> 3) 4) SPOOL OFF For output that is greater than 80 characters, be sure to set the LINESIZE value large enough. Otherwise Oracle will break up the lines every 80 characters, making the SQL fail. Using SQL to Dynamically Generate SQL Scripts Putting it all together, the commands below will generate a clean file that contains only proper SQL statements: SQL> SET HEAD OFF SQL> SET PAGESIZE 0 SQL> SET FEEDBACK OFF SQL> SPOOL create_synonym.SQL SQL> SELECT ‘CREATE PUBLIC SYNONYM ‘|| object_name || ‘ FOR ‘ || owner || ‘.’ || object_name || ‘;’ FROM ALL_OBJECTS WHERE OWNER = ‘IOUG’; CREATE PUBLIC SYNONYM ATTENDEE FOR IOUG.ATTENDEE; CREATE PUBLIC SYNONYM HOTEL_ID FOR IOUG.HOTEL_ID; CREATE PUBLIC SYNONYM CREDIT_CARD_NBR FOR IOUG. CREDIT_CARD_NBR; SQL> SPOOL OFF Dynamically Recreate Your INIT.ORA file What is the INIT.ORA file? How can your INIT.ORA file get lost or out of synch? The V$PARAMETER data dictionary view contains the list of initialization parameters and their values. NAME NUM NAME TYPE VALUE ISDEFAULT ISSES MODIFIABLE ISSYS MODIFIABLE ISMODIFIED ISADJUSTED DESCRIPTION TYPE NUMBER VARCHAR2(64) NUMBER VARCHAR2(512) VARCHAR2(9) VARCHAR2(5) VARCHAR2(9) VARCHAR2(10) VARCHAR2(5) VARCHAR2(64) Dynamically Recreate Your INIT.ORA file To see all of the parameters and their values (along with whether or not they are the default values), issue the following SQL: SQL> COLUMN NAME FORMAT A30 SQL> COLUMN VALUE FORMAT A30 SQL> SELECT NAME, VALUE, ISDEFAULT FROM V$PARAMETER ORDER BY NAME; NAME shared_pool_size snapshot_refresh_interval snapshot_refresh_keep_connect snapshot_refresh_process sort_area_retained_size sort_area_size sort_direct_writes sort_spacemap_size sort_write_buffer_size VALUE 300000000 60 FALSE 0 0 51200000 TRUE 512 32768 ISDEFAULT FALSE TRUE TRUE TRUE TRUE FALSE FALSE TRUE TRUE Dynamically Recreate Your INIT.ORA file To generate the INIT.ORA file, you can use SQL to generate SQL and spool the results to a file: SQL> SET LINESIZE 200 SQL> SET TRIMSPOOL ON SQL> SET HEADING OFF SQL> SET FEEDBACK OFF SQL> SET PAGESIZE 0 SQL> SELECT NAME||’=‘||VALUE 2 FROM V$PARAMETER WHERE ISDEFAULT=‘FALSE’ 3 ORDER BY NAME SQL> SPOOL initSID.ora SQL> / sequence_cache_entries=100 sequence_cache_hash_buckets=89 shared_pool_size=30000 sort_area_size=512000 sort_direct_writes=TRUE rollback_segments=rbig, r01, r02, r03, r04 … SQL> SPOOL OFF Finding All Referencing Constraints to a Particular Table The DBA_CONSTRAINTS data dictionary view contains information on table relationships: SQL> DESC DBA_CONSTRAINTS Name Null? Type OWNER CONSTRAINT_NAME CONSTRAINT_TYPE TABLE_NAME SEARCH_CONDITION R_OWNER R_CONSTRAINT_NAME DELETE_RULE STATUS DEFERRABLE DEFERRED VALIDATED GENERATED BAD LAST_CHANGE NOT NULL NOT NULL VARCHAR2(30) VARCHAR2(30) VARCHAR2(1) VARCHAR2(30) LONG VARCHAR2(30) VARCHAR2(30) VARHCAR2(9) VARCHAR2(8) VARCHAR2(14) VARCHAR2(9) VARCHAR2(13) VARCHAR2(14) VARCHAR2(3) DATE NOT NULL Finding All Referencing Constraints to a Particular Table To see the constraints on the EMP and DEPT tables, along with which constraints they are referencing: SQL> SELECT OWNER||’.’||TABLE_NAME “TABLE”, 2 CONSTRAINT_NAME, R_CONSTRAINT_NAME 3 FROM DBA_CONSTRAINTS 4 WHERE TABLE_NAME IN (‘EMP’,’DEPT’) TABLE CONSTRAINT_NAME R_CONSTRAINT_NAME SCOTT.EMP FK_EMP PK_DEPT SCOTT.DEPT PK_DEPT Finding All Referencing Constraints to a Particular Table To see all constraints that point to a table, issue: SQL> SELECT OWNER||’.’||TABLE_NAME “TABLE”, 2 CONSTRAINT_NAME, R_CONSTRAINT_NAME 3 FROM DBA_CONSTRAINTS 4 WHERE (OWNER, R_CONSTRAINT_NAME) IN 5 (SELECT OWNER, CONSTRAINT_NAME 6 FROM DBA_CONSTRAINTS B 7 WHERE B.OWNER = ‘&owner’ AND 8 B.TABLE_NAME = ‘&table’); Enter value for owner: SYSTEM Enter value for table: DEPT TABLE SCOTT.EMP TRAINING.EMP CONSTRAINT_NAME FK_EMP FK_EMP R_CONSTRAINT_NAME PK_DEPT PK_DEPT Finding the Total REAL Size of Data in a Table, Not Just the INIT/NEXT Extent What are INITIAL and NEXT extents? How does data fill into extents? Two possibilities: ANALYZE the table or use the ROWID pseudo-column ANALYZE generates block statistics. Oracle7’s ROWID is in restricted format, which includes the block Oracle8’s ROWID (extended format) can be converted to restricted format with the DBMS_ROWID package. Finding the Total REAL Size of Data in a Table, Not Just the INIT/NEXT Extent You can easily see the number of blocks that have been used by analyzing the table and querying the USER_TABLES view: SQL> ANALYZE TABLE table_name ESTIMATE STATISTICS; Table Analyzed. SQL> SELECT TABLE_NAME, BLOCKS, EMPTY_BLOCKS 2 FROM USER_TABLES WHERE TABLE_NAME = ‘table_name’; TABLE_NAME BLOCKS EMPTY_BLOCKS table_name 964 465 This will quickly give the results, but the downsides are … Finding the Total REAL Size of Data in a Table, Not Just the INIT/NEXT Extent In Oracle7, to find out the total blocks, enter the following SQL: SELECT count(distinct(substr(rowid,1,8))) FROM table_name; In Oracle8, you can find the total blocks using DBMS_ROWID: SELECT count(distinct(substr(dbms_rowid.rowid_to_restricted(rowid),1,8))) FROM table_name; You will get the total number of blocks that contain records of the table. To determine the overall size, find the block size: SELECT value FROM V$PARAMETER WHERE NAME = ‘db_block_size’; The total space of the data is the multiplication of “Total Blocks” This will be in bytes. * “Block Size”. To find the density (rows per block for EACH block), issue (Oracle7): SELECT distinct(substr(rowid,1,8)) FROM table_name; -or in Oracle8SELECT (distinct(substr(dbms_rowid.rowid_to_restricted(rowid),1,8)) FROM table_name; What SQL Statement is a Particular User Account Executing One of the most important DBA jobs is to know what the users are doing Oracle does not make this easy Determine the SID of the user account you wish to monitor. To get a list of all users logged in at any given time, type in the following SQL: SELECT sid, schemaname, osuser, substr(machine, 1, 20) Machine FROM v$session ORDER BY schemaname; What SQL Statement is a Particular User Account Executing A sample output follows: SID ---1 2 3 4 7 13 6 14 12 SCHEMANAME ---------------SYS SYS SYS SYS SYSTEM SYSTEM WWW_DBA WWW_DBA WWW_DBA OSUSER ----------- MACHINE ------------ plat oracle oracle oracle oracle ustate1 ustate1 dw1-uw dw1-uw dw1-uw What SQL Statement is a Particular User Account Executing Now, you can select a user from list. When you run the SQL below, the SQL statement that the user account is running will be shown. SELECT sql_text FROM v$sqlarea WHERE (address, hash_value) IN (SELECT sql_address, sql_hash_value FROM v$session WHERE sid = &1); Temporarily Changing User Passwords to Log in as Them Can you determine a user’s password if you are the DBA? How are passwords stored in the database? SELECT username, password FROM DBA_USERS; You will get a result of something like: USERNAME AKAPLAN SYSTEM SYS PASSWORD 923FEA35C3BE5824 34D93E8F22C21DA4 A943B39DF93E02C2 . Temporarily Changing User Passwords to Log in as Them There is a trick that you can use to temporarily change the users password to something you do know, and then change it back when you are done. The unsuspecting user will never know ;) Before you do this, you need to find the encrypted password: SELECT password FROM all_users WHERE username = ‘enter_username_here’; Temporarily Changing User Passwords to Log in as Them Write down the 16-digit encrypted password. You will need this later. Now, change the password of the user to whatever you want: ALTER USER AKAPLAN IDENTIFIED BY applaud_now; You can now go into the database as the AKAPLAN user: sqlplus AKAPLAN/applaud_now To change the password back, use the “BY VALUES” clause of the “ALTER USER” command. For the password, supply the 16-digit encrypted password in single quotes. An example is shown below: ALTER USER AKAPLAN IDENTIFIED BY VALUES ‘923FEA35C3BE5824’; Temporarily Changing User Passwords to Log in as Them Oracle8 Password-Related Management Options: Highlighted items are affected by temporarily changing passwords FAILED_LOGIN_ATTEMPTS PASSWORD_GRACE_TIME PASSWORD_LIFE_TIME PASSWORD_LOCK_TIME PASSWORD_REUSE_MAX PASSWORD_REUSE_TIME PASSWORD_VERIFY_FUNCTION Where to Now? There are many discussion Newsgroups on the internet for you to give questions and get answers: comp.databases.oracle.server comp.databases.oracle.tools comp.databases.oracle.misc These can be accessed through a newsgroup program or “www.deja.com” Ari’s free Oracle Tips web page at: There are over 370 tips and answers to questions that have been posed to me over the years. This paper will be downloadable from the web page as well. www.orafaq.org, www.orafans.com, www.ioug.org, www.orasearch.com, www.revealnet.com, www.lazydba.com, www.dbdomain.com Other good sites with links: