orc info tip

Upload: sendhilmca

Post on 07-Apr-2018

224 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/6/2019 Orc Info Tip

    1/36

    (decode(name, 'consistent gets',value) *decode(name,'db block gets',value)) as hit_ratioFrom v$sysstatwhere name IN ('physical reads', 'consistent gets', 'db block gets');if ratio > 95 you may decrease the buffer size in case required for

    other purposeif hit_ratio between 70 and 94 then its OKif hit_ratio 60 to 69 then one has to add more buffers==================suppose it is less than 70, then question arises as to how many buffersto add. In order to determine, only on SYS login andDB_BLOCK_LRU_EXTENDED_STATISTICS set to non-zero value in Select100*TRUNC(indx/100) + 1 || '-' || 100*(TRUNC(indx/100)+1 range,SUM(count) additional_hitsFROM x$KCBRBHGROUP BY TRUNC(indx/100);

    suppose the resultrange additional_hits=========================1-100 78002101-200 105000201-300 1005in this case if you notice increasing the buffer size by 200 is sufficient,further increases will not decrease, then perform above operation withX$KCBCDH view. For this, the init.ora prameter to to trueSummary: How to find valid Description:Ever spend some values you can easily find the table. This table

    "TERRITORY" considered valid (Also shows Summary: NLS_LANGparameter can Description: While running an export on character setof WE8ISO8859P1, a warning message sayingpossible character conversion NLS_LANG parm to English_the exportworked properly. mind when running exports something other than theyou could find that special mysteriously disappear from running theexport will need NLS_LANG=English_Canada.NLS_LANGSummary: "Query is executing..."?Description: "Query is executing..." isn'tit? Who knows? This does, ifyour query uses rollback

    segments (updates, inserts,etc.) Using the statementbelow, you can find out ifyour UPDATE (or whatever)is proceeding or rolling back.Step 1: Obtain the SID forthe session in question. Thereare a few ways to obtain the

  • 8/6/2019 Orc Info Tip

    2/36

    (proper) SID, but this maywork as well as any:select sid, serial#, username,terminal from v$session;Look through the results to

    find the SID you need. In thisexample, the SID is 10.Step 2: select taddrfrom v$sessionwhere sid = '10' ; Inthis example, the result is023ED71CStep 3: Using the value oftaddr returned in step 2, dothis:select used_ublk

    from v$transactionwhereaddr='023ED71C'or, you can combinesteps 2 and 3 asfollows:select used_ublkfrom v$transactionwhere addr=(select TADDR fromv$session where

    sid='10');Step 4: Wait a few seconds,and run STEP 3 again. if theresults show an increasingnumber, the transaction isproceeding. If the resultsshow a smaller number, thena rollback is occurring.Summary:Migratingusing

    ODMADescription: I'mmigratingmore than242 oracle7.3.4databases toOracle 8.1.6

  • 8/6/2019 Orc Info Tip

    3/36

    UsingODMA(Oracle DataMigrationAsistant)

    could find abig error inthe OracleNote76460.1fromMetalink(ChecklistforMigratingfrom

    Oracle7 toOracle8.1 onUNIX).Step 13,when youhave toCONVERTthe database,does notwork asindicated,

    so, what youhave to dois:1) startupyour db2) backupyour controlfile to trace3) shutdownyour db4) startup

    nomount5) recreateyour controlfiles usingscriptgenerated instep 2).6) alter

  • 8/6/2019 Orc Info Tip

    4/36

    databaseopen7) continuewith OracleChecklist.

    Believe me,it works fine!!! Hope thiscould beuseful.Email me ifany problemcomes up.Summary: Migrating using ODMADescription: I'm migrating more than 242 oracle 7.3.4databases to Oracle 8.1.6

    Using ODMA (Oracle Data Migration Asistant)could find a big error in the Oracle Note 76460.1from Metalink (Checklist for Migrating fromOracle7 to Oracle8.1 on UNIX).Step 13, when you have to CONVERT thedatabase, does not work as indicated, so, what youhave to do is:1) startup your db2) backup your control file to trace3) shutdown your db4) startup nomount

    5) recreate your control files using scriptgenerated in step 2).6) alter database open7) continue with Oracle Checklist.Believe me, it works fine !!! Hope this could beuseful. Email me if any problem comes up.Summary: Running SQLPLUS in NT scriptsDescription:Be aware, you can't setup an environmentvariable SQLPLUS in an NT script. It will fail.Oracle 8.1.6 has changed from plus80.exe to

    sqlplus.exe. If you have an environment variableset, i.e.SQLPLUS=d:\oracle\ora81\bin\sqlplus.exe, thebatch job will fail.Change the environment variable to anythingelse other than SQLPLUS. i.e. setSPLUS=d:\oracle\ora81\bin\sqlplus.exeSummary: How to cleanup unused temporary segments

  • 8/6/2019 Orc Info Tip

    5/36

    Description:To cleanup or remove unused temporarysegments you can use the following SQLstatement on the temporary tablespace:alter tablespace default storage

    (pctincrease )use the pctincrease value you've alreadyspecified for this tablespace. This SQL "wakesup" the system monitor process, which removesall unused temp. segmentsSummary: How to load data from MS excel to an Oracletable?Description: If your Excel sheet has a simple table formatthen you can copy it as text file with tabdelimiters. Then create a simple ctl-file fromSQL*Loader just like that:

    load datainfile ""appendinto tablefields terminated by ' ' ()The key is that in apostrophes you shouldspecify the tab character (ASCII code 9). Useone of the text editors that support quoting(usually with Ctrl+q combination keystroke).After that you can issue:

    sqlldr80 userid= control= log=data=That's all.Some clarification.Better use clause "...fields terminated byX'09'..." in order to specify the tab characterexplicitly.You can also try saving the file from Excel intext file CSV-format or TAB-delimited andload with the SQL*Loader (Oracle Utilities) orBorland DataPump (from Delphi or C++

    distribut).Summary: How do I restrict a query by "ROWNUM"range?Description: If you try to use rownum to restrict a query bya range that does not start with 1, you willquickly find that it does not work. Forexample:SQL> SELECT * from TABLE1 WHERE

  • 8/6/2019 Orc Info Tip

    6/36

    rownum BETWEEN 5 AND 10no rows selectedThe reason for this, is that rownum is apseudo-column produced AFTER the queryreturns. Normally, it can only be used to

    restrict a query to return a rownumber rangethat starts with 1 (like rownum)

  • 8/6/2019 Orc Info Tip

    7/36

    OWNER: SHAREDObjects in devl but not demoObject Name Object Type Status----------------------------------- -------------------------TSU_SELECT_CAD_FN FUNCTION VALID

    TSU_SELECT_FN FUNCTION VALIDFMU_PLS INDEX VALIDFMU_PRECIP INDEX VALIDSYSPIPE PACKAGE VALIDSYSPIPE PACKAGE BODY VALIDABLE_FK_CONSTRAINTS PROCEDURE INVALIDWTRSHD_SEQ SEQUENCE VALIDADMIN_CODE TABLE VALIDFMPT_FORM_HELP TABLE VALIDFMU_COUNTY_V VIEW VALIDFMU_TRUST_V VIEW VALID12 rows selected.OBJDIFF OBJECT DIFFERENCE REPORT ReportDate: 02/10/97Page: 1OWNER: SHAREDObjects in demo but not devlObject Name Object Type Status----------------------------------- -------------------------PPR_LABEL_NM FUNCTION VALIDFMA_PEST_PEST_CD_I INDEX VALIDGEO_ADMIN_UNIT_SEQ SEQUENCE VALIDTR_FMA_STATUS TRIGGER INVALIDTR_INSERT_FMA_AREA TRIGGER INVALIDFMA_INS_FMA_AREA_TR TRIGGER VALIDRX_SUM_RPT_VIEW VIEW VALID7 rows selected.

    Notes: If no database link is entered, then the script uses the CURRENTinstance. Also,the title uses the instance name in each instances v$parameter table(Objects in{instance_1_name} but not in {instance_2_name}.The second script compares the actual table definitions in twoinstances:SQL> @tabdiffTable Owner: SHAREDFirst instance DB Link (Include @):

    Second instance DB Link (Include @):@TESTLINKTABDIFF SCHEMA DIFFERENCE REPORT ReportDate: 02/10/97Page: 1OWNER: SHAREDDifferences betweendevl and demoInstance Table ColumnDataType Len Pr Null?-------- -------------------------

  • 8/6/2019 Orc Info Tip

    8/36

    ------------------------- -------- ---- ---- -----demo FMU FMU_RESTR_BEG_DT_BADDATE 7 =0 Ydemo FMU FMU_RESTR_END_DT_BADDATE 7 =0 Y2 rows selected.

    And now listings of the actual scripts:/*************************************************************************//* objdiff.sql - Lists the objects in a schema that are notin both of *//* two instances. Uses database links andthe SQL MINUS *//* operator to make the comparison.*//**//* Author: Ken Atkins ([email protected])*/

    /* http://www.arrowsent.com/oratip*//**//* Written: 5/11/95*//**//* You need to have a database link setup for any instancethat you want *//* to make a comparison for.*//*

    *//* Please feel free to use and modify this script as longit is not sold *//* or included in any software without the prior permissionof the author*//* If you do make some good improvements, please send themto me, and I *//* can incorporate them in a future version and make themavailable to *//* others (giving you credit of course!).*//**//*************************************************************************/set pagesize 60set linesize 80set verify offset feedback offset pause off;--define obj_owner = '&1'--define inst_1 = '&2'--define inst_2 = '&3'

    http://www.arrowsent.com/oratiphttp://www.arrowsent.com/oratip
  • 8/6/2019 Orc Info Tip

    9/36

    accept obj_owner prompt 'Object Owner: 'accept inst_1 prompt 'First instance DB Link (Include @):'accept inst_2 prompt 'Second instance DB Link (Include @):'clear breaksttitle offset heading off

    column datetime noprint new_value datetimecolumn inst_code1 noprint new_value inst_code1column inst_code2 noprint new_value inst_code2select to_char(sysdate,'MM/DD/YY') datetimefrom dual/select value inst_code1from v$parameter&inst_1where name = 'db_name'/select value inst_code2from v$parameter&inst_2where name = 'db_name'/set feedback onset heading onset newpage 0ttitle left 'OBJDIFF'-col 25 'OBJECT DIFFERENCE REPORT' -col 53 'Report Date: ' datetime -skip 1 col 60 'Page: ' sql.pno -skip 1 col 10 'OWNER: ' obj_owner -skip 1 center 'Objects in &inst_code1 but not &inst_code2'-skip 2set null=0column object_type format a15 heading 'Object Type';column object_name format a35 heading 'Object Name';column status format a10 heading 'Status';column inst_code format a10 heading 'Instance';select object_name, object_type, statusfrom all_objects&inst_1where owner = UPPER('&obj_owner')-- and object_type != 'SYNONYM'MINUSselect object_name, object_type, statusfrom all_objects&inst_2where owner = UPPER('&obj_owner')-- and object_type != 'SYNONYM'order by 2,3

    /set heading off;set feedback off;select '' from dual/set heading on;set feedback on;ttitle left 'OBJDIFF'-

  • 8/6/2019 Orc Info Tip

    10/36

    col 25 'OBJECT DIFFERENCE REPORT' -col 53 'Report Date: ' datetime -skip 1 col 60 'Page: ' sql.pno -skip 1 col 10 'OWNER: ' obj_owner -skip 1 center 'Objects in &inst_code2 but not &inst_code1'-

    skip 2select object_name, object_type, statusfrom all_objects&inst_2where owner = UPPER('&obj_owner')and object_type != 'SYNONYM'MINUSselect object_name, object_type, statusfrom all_objects&inst_1where owner = UPPER('&obj_owner')and object_type != 'SYNONYM'order by 2,3/undefine datetimeundefine inst_code1undefine inst_code2undefine obj_owner

    ================================================================/*************************************************************************//* tabdiff.sql - Lists the differences in table definitionsin the tables*//* for a schema in two different instances.Uses database*//* links and the SQL MINUS operator to make

    the comparison.*//**//* Author: Ken Atkins ([email protected])*//* http://www.arrowsent.com/oratip*//**//* Written: 5/11/95*//**/

    /* You need to have a database link setup for any instancethat you want *//* to make a comparison for.*//**//* Please feel free to use and modify this script as longit is not sold *//* or included in any software without the prior permissionof the author*/

    http://www.arrowsent.com/oratiphttp://www.arrowsent.com/oratip
  • 8/6/2019 Orc Info Tip

    11/36

    /* If you do make some good improvements, please send themto me, and I *//* can incorporate them in a future version and make themavailable to *//* others (giving you credit of course!).*/

    /**//*************************************************************************/set pagesize 60set linesize 105set verify offset feedback offset pause off;--define obj_owner = '&1'--define inst_1 = '&2'--define inst_2 = '&3'accept obj_owner prompt 'Table Owner: 'accept inst_1 prompt 'First instance DB Link (Include @):'accept inst_2 prompt 'Second instance DB Link (Include @):'clear breaksttitle offset heading offcolumn datetime noprint new_value datetimecolumn inst_code1 noprint new_value inst_code1column inst_code2 noprint new_value inst_code2select to_char(sysdate,'MM/DD/YY') datetimefrom dual/select value inst_code1from v$parameter&inst_1where name = 'db_name'/select value inst_code2from v$parameter&inst_2where name = 'db_name'/set feedback onset heading onset newpage 0ttitle left 'TABDIFF'-col 25 'SCHEMA DIFFERENCE REPORT' -col 53 'Report Date: ' datetime -skip 1 col 60 'Page: ' sql.pno -skip 1 col 10 'OWNER: ' obj_owner -

    skip 1 center 'Differences between &inst_code1 and&inst_code2' -skip 2column table_name format a25 heading 'Table';column column_name format a25 heading 'Column';column data_type format a8 heading 'DataType';column data_length format 999 heading 'Len';column data_precision format 999 heading 'Pr';column nullable format a5 heading 'Null?';

  • 8/6/2019 Orc Info Tip

    12/36

    column inst_code format a8 heading 'Instance';(select '&inst_code1' inst_code, table_name, column_name,data_type, data_length, data_precision, nullablefrom all_tab_columns&inst_1where owner = UPPER('&obj_owner')

    and table_name in (select table_name fromall_tables&inst_2where owner = UPPER('&obj_owner'))MINUSselect '&inst_code1' inst_code, table_name, column_name,data_type, data_length, data_precision, nullablefrom all_tab_columns&inst_2where owner = UPPER('&obj_owner'))UNION(select '&inst_code2' inst_code, table_name, column_name,data_type,data_length, data_precision, nullablefrom all_tab_columns&inst_2where owner = UPPER('&obj_owner')and table_name in (select table_name fromall_tables&inst_1where owner = UPPER('&obj_owner'))MINUSselect '&inst_code2' inst_code, table_name, column_name,data_type,data_length, data_precision, nullablefrom all_tab_columns&inst_1where owner = UPPER('&obj_owner'))order by 2, 3/undefine datetimeundefine inst_code1undefine inst_code2undefine obj_owner

    Tip #12: SQL Script to show 'hit ratio' of currentlyrunning processes.(Type: SQL)Have you ever wondered why your server was running so slow? Whoelse is runningqueries and why are they bogging the system down?? So you go round

    up a DBA and askthem to monitor the database using one of those shnazzy DBA typetools. But DBAs arenot always had for the asking, and you do not have access to the tools,so what do youdo? This tip is a couple of simple SQL scripts which will show whichOracle processesare currently running in an instance, and what the buffer hit ratio is for

  • 8/6/2019 Orc Info Tip

    13/36

    those processes(low hit ratios are an indication of poorly tuned SQL, which can slowthe WHOLEinstance down).The first script shows the active processes and their current hit ratio.

    /*************************************************************************//* listproc.sql - Lists currently running processes andtheir hit ratios *//**//* Author: Ken Atkins ([email protected])*//* http://www.arrowsent.com/oratip*//**//* You need select access to V$SESSION, V$PROCESS, andV$SESS_IO *//* to run this script.*//**//* The columns returned by this script are:*// Oracle ID (schemaname) = The oracle 'schema' or'user' that is *//* running the SQL statement.*//* System ID (username) = The system id that theprocess is *//* running under. Will bethe unix userid *//* if Oracle running on unix.*//* Program = The name of the program that isrunning the SQL.*//* Physical Reads = The number of physical blockreads. *//* Hit Ratio = The ratio of buffer to physicalblock reads. *//* be an indication of theefficiency of the query*//* running. Anything under 90% isbad. Very low */

    /* hit ratios (< 10-20%) in aprocess can slow *//* down the whole system.*//*************************************************************************/column schemaname format a10 heading 'Oracle ID'column username format a10 heading 'System ID'column program format a32 heading 'Program'

    http://www.arrowsent.com/oratiphttp://www.arrowsent.com/oratip
  • 8/6/2019 Orc Info Tip

    14/36

    column hit_ratio format 9.90 heading 'Hit Ratio'column physical_reads format 9999999 heading 'Reads'column sid format 99999SELECT s.schemaname, p.username, s.program

    ,io.physical_reads,(io.block_gets+io.consistent_gets)/(io.block_gets+io.consistent_gets+io.physical_reads) hit_ratioFROM V$Session s,V$Process p,V$Sess_io ioWHERE s.paddr = p.addrAND s.sid = io.sid-- Only look at active processesAND s.status = 'ACTIVE'-- Need this predicate to prevent division by 0AND (io.block_gets+io.consistent_gets+io.physical_reads)> 0/

    An example of using the script:SQL> @hitratioOracle ID System ID ProgramReads Hit Ratio---------- ---------- ---------------------------------------- ---------SYS oracle71.00SYS oracle710894 .83SYS oracle7

    18 .95BDES490 oracle7 C:\ORAWIN\BIN\PLUS31.EXE1.00BDES490 oracle7 sqlplus@larabee (TNS interface)3478 .83

    The next script is a simpler version that just shows all of processes andtheir status, sidand serial#. The sid and serial# are used in the ALTER SYSTEM KILLSESSIONcommand to kill oracle processes that are 'stuck'./*************************************************************************//* listproc.sql - Lists currently processes, status, sid &serial# *//**//* Author: Ken Atkins ([email protected])*//* http://www.arrowsent.com/oratip*//**/

    http://www.arrowsent.com/oratiphttp://www.arrowsent.com/oratip
  • 8/6/2019 Orc Info Tip

    15/36

    /* You need select access to V$SESSION, V$PROCESS to runthis script *//**//*************************************************************************/

    column schemaname format a10 heading 'Oracle ID'column username format a10 heading 'System ID'column program format a30 heading 'Program'column user_name format a15 heading 'User Name'column sid format 99999SELECT s.schemaname,p.username,s.program,s.sid,s.serial#,s.statusFROM V$Session s,V$Process pwhere s.paddr = p.addr/

    An example of running the script:SQL> @listprocOracle ID System ID Program SIDSERIAL# STATUS---------- ---------- ------------------------------ --------------- --------KATK490 C:\WINDOWS\SYSTEM32\OLE2.DLL 21447 KILLEDSYS oracle7 11 ACTIVESYS oracle7 2

    1 ACTIVESYS oracle7 31 ACTIVESYS oracle7 41 ACTIVESYS oracle7 51 ACTIVEORAPIPE orapipe ? @gamera (TNS interface) 98021 INACTIVEBDES490 oracle7 C:\ORAWIN\BIN\CKRON10L.DLL 12105 INACTIVEJOJJ490 oracle7 C:\ORAWIN\BIN\R25DES.EXE 732691 INACTIVEBDES490 oracle7 C:\ORAWIN\BIN\PLUS31.EXE 16275 ACTIVEARJJ490 oracle7 C:\ORAWIN\BIN\CKRON10L.DLL 62029 INACTIVEBHAR490 oracle7 C:\ORAWIN\BIN\PLUS31.EXE 102545 INACTIVEBDES490 oracle7 sqlplus@larabee (TNS interface) 17619 ACTIVEMAJJ490 oracle7 C:\ORAWIN\BIN\CKRON10L.DLL 1335 INACTIVE

  • 8/6/2019 Orc Info Tip

    16/36

    BHAR490 oracle7 C:\ORAWIN\BIN\R25DES.EXE 1439 INACTIVEARJJ490 oracle7 C:\ORAWIN\BIN\R25DES.EXE 89173 INACTIVEMAJJ490 oracle7 C:\ORAWIN\BIN\R25DES.EXE 112273 INACTIVE

    SHARED oracle7 C:\ORAWIN\BIN\PLUS31.EXE 1567 INACTIVEBDES490 oracle7 C:\ORAWIN\BIN\PLUS31.EXE 18739 INACTIVE

    Tip #11: Procedure to disable FK constraints TO a table.(Type: DBA)So you have to reload the data in a table that is maintained in anothersystem. But thereare these pesky Foreign Keys defined TO this table from other tables inyour database.Oh Well, Select the names of the FKs from the constraints table, enterthe commands todisable them, now load the data. What? You missed one? Disable it,reload. Now enableall of the constraints again. Kind of tedious. This tip details a storedprocedure that canautomatically disable or enable all of the FK constraints *TO* aspecified table.The following procedure uses the following steps to enable or disableall of the FKconstraints *TO* a specified table:1. Finds the PK of the specified table.2. Uses this PK to find all of the FKs that are linked to the PK.3. Puts together an ALTER TABLE DISABLE CONSTRAINT command todisableeach FK.4. Uses dynamic SQL to execute the commands.PROMPTPROMPT Creating Procedure able_fk_constraintsCREATE OR REPLACE PROCEDURE able_fk_constraints(pTable IN VARCHAR2 ,pAble IN VARCHAR2 )ISvPKName VARCHAR2(80);-- This cursor returns the list of FK constraints linked to

    the specified-- PK constraint.CURSOR curFK(pcPKName IN VARCHAR2) ISSELECT constraint_name, table_nameFROM user_constraintsWHERE r_constraint_name = pcPKName;-- These two variables are used for the dynamic SQLnDDLCursor INTEGER;nDDLReturn INTEGER;

  • 8/6/2019 Orc Info Tip

    17/36

    BEGIN/*******************************************************************************//* ABLE_FK_CONSTRAINTS - This procedure easilyenables/disables FK constraints *//* pointing TO the specified table.

    *//**//* Parameters: pTable - The name of the table todis/enable FK *//* constraints to.*//* pAble - One of: DISABLE or ENABLE*//*******************************************************************************/-- Get the name of the PK constraint for the specifiedtable.BEGINSELECT constraint_name INTO vPKNameFROM user_constraintsWHERE table_name = pTableAND constraint_type = 'P';END;-- Now get the FK constraints linked to the PK constraintof the specified table.FOR fk IN curFK(vPKName) LOOP-- Use dynamic SQL to execute the ALTER TABLE commandand dis/enable the constraintnDDLCursor := dbms_sql.open_cursor;dbms_sql.parse(nDDLCursor,'ALTER TABLE '||fk.table_name||' '||pAble||' CONSTRAINT '||fk.constraint_name, 1);nDDLReturn := dbms_sql.execute(nDDLCursor);dbms_sql.close_cursor(nDDLCursor);END LOOP;END ABLE_FK_CONSTRAINTS;/

    An example of using the script:execute able_fk_constraints('MYTABLE','DISABLE');truncate table mytable;@load_mytableexecute able_fk_constraints('MYTABLE','ENABLE');

    Of course, the procedure has to beinstalled in a schemathat has the ALTER TABLE systemprivelege, andsecurity to modify the specified table.

  • 8/6/2019 Orc Info Tip

    18/36

    Also, the data thatis loaded into the table may cause anexisting FK

    contraint to no longer be valid (like if anexpected codeis no longer there). In this case, theENABLE will bomb,and the data will have to be fixed beforethe constraintcan be re-enabled. Tip #52: Getting Rid of"Inputtruncated to # characters" (Type:SQL*Plus)Are you getting the annoying message "Input truncated to #characters" whenever yourun a SQL script in SQL*Plus? This can be very annoying, especially ifyou are runningSQL scripts that produce reports or generate other SQL scripts. This tipwill tell you howto get rid of this message!

    An Example of the ProblemConsider the following SQL*Plus report:set pagesize 30set linesize 40set feedback offttitle CENTER 'Test Employee Report' skip 2break on dname skip 1spool tstrep.lstSELECT d.dname, e.empno, e.enameFROM Dept d, Emp eWHERE d.deptno = e.deptno

    ORDER BY d.dname, e.ename/spool offIf executed you might see:Test Employee ReportDNAME EMPNO ENAME-------------- ---------- ----------ACCOUNTING 7782 CLARK7839 KING

  • 8/6/2019 Orc Info Tip

    19/36

    7934 MILLERRESEARCH 7876 ADAMS7902 FORD7566 JONES7788 SCOTT7369 SMITH

    SALES 7499 ALLEN7698 BLAKE7900 JAMES7654 MARTIN7844 TURNER7521 WARDInput truncated to 9 characters Problem Message!

    As you can see, you have the unwanted message at the bottom of thereport.

    What Causes The Problem?This problem is caused by having anything OTHER than a blank line atthe bottom of

    your SQL*Plus script! The last line of the script *must* be a blank line,that is a linewith a carriage return and NOTHING ELSE. For example:set pagesize 30set linesize 40set feedback offttitle CENTER 'Test Employee Report' skip 2break on dname skip 1spool tstrep.lstSELECT d.dname, e.empno, e.enameFROM Dept d, Emp e

    WHERE d.deptno = e.deptnoORDER BY d.dname, e.ename/spool offBlank Line!

    Tip #44: Ordering by a Hierarchy (Type:SQL)Have you ever tried to order a hierarchical query? The results are notencouraging. Theordering returned by Oracle is based on the hierarchy, and there is no

    easy way to orderWITHIN the hierarchy levels. So how do we get around this problem?Well, there is noeasy way to do it. However, with a little work, the solution presented inthis tip will do it.

    What Happens if I Order byI will use the infamous EMP/DEPT tables to illustrate this technique.Using these tables,

  • 8/6/2019 Orc Info Tip

    20/36

    you might use the following SQL for a standard hierarchical query:SQL>1 SELECT level, LPAD(' ',2*level-2)||emp.ename ename,emp.empno, emp.mgr, emp.deptno2 FROM Emp3 CONNECT BY PRIOR emp.empno = emp.mgr

    4* START WITH emp.empno = 7839SQL> /LEVEL ENAME EMPNO MGR DEPTNO--------- -------------------- --------- --------- ---------1 KING 7839 102 JONES 7566 7839 203 SCOTT 7788 7566 204 ADAMS 7876 7788 203 FORD 7902 7566 204 SMITH 7369 7902 202 BLAKE 7698 7839 303 ALLEN 7499 7698 303 WARD 7521 7698 30

    3 MARTIN 7654 7698 303 TURNER 7844 7698 303 JAMES 7900 7698 302 CLARK 7782 7839 103 MILLER 7934 7782 10

    Now let's say you want to order alphabetically within each level (i.e.BLAKE, CLARK,JONES for level 2, and ALLEN, JAMES, MARTIN, TURNER, WARD for level3 underBLAKE). Here are some standard attempts at this:SQL> l1 SELECT level, LPAD(' ',2*level-2)||emp.ename ename,

    emp.empno, emp.mgr, emp.deptno2 FROM Emp3 CONNECT BY PRIOR emp.empno = emp.mgr4* START WITH emp.empno = 7839LEVEL ENAME EMPNO MGR DEPTNO--------- -------------------- --------- --------- ---------4 ADAMS 7876 7788 204 SMITH 7369 7902 203 ALLEN 7499 7698 303 FORD 7902 7566 203 JAMES 7900 7698 303 MARTIN 7654 7698 303 MILLER 7934 7782 103 SCOTT 7788 7566 203 TURNER 7844 7698 303 WARD 7521 7698 302 BLAKE 7698 7839 302 CLARK 7782 7839 102 JONES 7566 7839 201 KING 7839 1014 rows selected.SQL> l1 SELECT level, LPAD(' ',2*level-2)||emp.ename ename,

  • 8/6/2019 Orc Info Tip

    21/36

    emp.empno, emp.mgr, emp.deptno2 FROM Emp3 CONNECT BY PRIOR emp.empno = emp.mgr4 START WITH emp.empno = 78395* order by emp.enameSQL> /

    LEVEL ENAME EMPNO MGR DEPTNO--------- -------------------- --------- --------- ---------4 ADAMS 7876 7788 203 ALLEN 7499 7698 302 BLAKE 7698 7839 302 CLARK 7782 7839 103 FORD 7902 7566 203 JAMES 7900 7698 302 JONES 7566 7839 201 KING 7839 103 MARTIN 7654 7698 303 MILLER 7934 7782 103 SCOTT 7788 7566 204 SMITH 7369 7902 203 TURNER 7844 7698 303 WARD 7521 7698 3014 rows selected.SQL> l1 SELECT level, LPAD(' ',2*level-2)||emp.ename ename,emp.empno, emp.mgr, emp.deptno2 FROM Emp3 CONNECT BY PRIOR emp.empno = emp.mgr4 START WITH emp.empno = 78395* order by level,emp.enameSQL> /LEVEL ENAME EMPNO MGR DEPTNO--------- -------------------- --------- --------- ---------1 KING 7839 102 BLAKE 7698 7839 302 CLARK 7782 7839 102 JONES 7566 7839 203 ALLEN 7499 7698 303 FORD 7902 7566 203 JAMES 7900 7698 303 MARTIN 7654 7698 303 MILLER 7934 7782 103 SCOTT 7788 7566 203 TURNER 7844 7698 303 WARD 7521 7698 304 ADAMS 7876 7788 20

    4 SMITH 7369 7902 20None of these give us what we want.

    Use a Hierarchy Order KeyThe only way I have found to truly resolve this problem is to add ahierarchy orderingkey column to the table with the hierarchy. This column needs to bepopulatedprogrammatically in such a way that you get the desired ordering. This

  • 8/6/2019 Orc Info Tip

    22/36

    key has to be theconcatenation of some sort of order key for EACH of the parent levelsabove thehierarchy node. This will allow the hierarchy to be ordered within eachlevel while

    allowing the children to be placed directly underneath their parent. Forexample,consider the EMP_HIER_ORDER column that I added to the standardemp table below:EMPNO ENAME EMP_HIER_ORDER--------- -------------------- -----------------------------7369 SMITH 00080007000500127499 ALLEN 0008000300027521 WARD 0008000300147566 JONES 000800077654 MARTIN 0008000300097698 BLAKE 000800037782 CLARK 00080004

    7788 SCOTT 0008000700117839 KING 00087844 TURNER 0008000300137876 ADAMS 00080007001100017900 JAMES 0008000300067902 FORD 0008000700057934 MILLER 000800040010

    Now if I order by EMP_HIER_ORDER I get:SQL> l1 SELECT level, LPAD(' ',2*level-2)||emp.ename ename,emp.empno,2 FROM Emp3 CONNECT BY PRIOR emp.empno = emp.mgr4 START WITH emp.empno = 78395 order by emp_hier_orderSQL> /LEVEL ENAME EMPNO MGR EMP_HIER_ORDER--------- -------------------- --------- --------------------------1 KING 7839 00082 BLAKE 7698 7839 000800033 ALLEN 7499 7698 0008000300023 JAMES 7900 7698 0008000300063 MARTIN 7654 7698 0008000300093 TURNER 7844 7698 0008000300133 WARD 7521 7698 000800030014

    2 CLARK 7782 7839 000800043 MILLER 7934 7782 0008000400102 JONES 7566 7839 000800073 FORD 7902 7566 0008000700054 SMITH 7369 790200080007000500123 SCOTT 7788 7566 0008000700114 ADAMS 7876 77880008000700110001

  • 8/6/2019 Orc Info Tip

    23/36

    Which is exactly what I want. The first four characters ofEMP_HIER_ORDER are usedfor ordering the top level of the hierarchy ("0008"), the second four areused for orderingthe second level ("0003","0004","0007"), and the third four for the

    third level, etc.NOTE: The above query used the hierarchical clauses (CONNECT BY,etc.) Using thehierarchy ordering column you could construct a query that does notneed it. Forinstance:SELECT length(emp_hier_order)/4 lvl, LPAD('',(length(emp_hier_order)/2)-2)||emp.ename ename,FROM Emporder by emp_hier_order

    Populating The Hierarchy Ordering KeyThe main problem with this technique is that it requires that extra code

    be written andexecuted to populate the hierarchy ordering key. I used the followingstored procedure topopulate the EMP_HIER_ORDER key in the above example:CREATE OR REPLACE PROCEDURE Update_Emp_Hier IS-- Cursor to return the ordering key for empCURSOR emp_order_cur ISSELECT empnoFROM EmpORDER BY ename;-- Hierarchy queryCURSOR hier_cur IS

    SELECT LEVEL lvl, empnoFROM EmpSTART WITH emp.empno = 7839CONNECT BY PRIOR emp.empno = emp.mgr;TYPE vc_tabtype IS TABLE OF VARCHAR2(4) INDEX BY BINARY_INTEGER;t_ordkey vc_tabtype;t_key vc_tabtype;v_hier_key VARCHAR2(30);v_OrdCnt NUMBER := 0;BEGIN---- Load the ordering key into a PL/SQL table to save tableaccess

    FOR e IN emp_order_cur LOOPv_OrdCnt := v_OrdCnt + 1;t_ordkey(e.empno) := LPAD(TO_CHAR(v_OrdCnt),4,'0');END LOOP;-- Now open the hierarchy queryFOR h IN hier_cur LOOP-- Store the order key for the current level in the hierarchyt_key(h.lvl) := t_ordkey(h.empno);-- Build the full ordering key for the current record. Thiswill

  • 8/6/2019 Orc Info Tip

    24/36

    -- consist of the current record's ordering key preceded inorder-- by the ordering keys of every level above it in thehierarchy.v_hier_key := '';FOR i IN 1..h.lvl LOOP

    v_hier_key := v_hier_key||t_key(i);END LOOP;UPDATE EmpSET emp_hier_order = v_hier_keyWHERE empno = h.empno;END LOOP;END;/

    This stored procedure can be called from the client that maintains thehierarchy, executedeither manually (i.e. when the user says they are done editing thehierarchy), orautomatically. But a better method would be to put a call to thisprocedure into a triggerfor the table. The following trigger definition would work:CREATE OR REPLACE TRIGGER emphierorderAFTER INSERT OR DELETE OR UPDATE OF mgrON EmpBEGINupdate_emp_hier;END;

    This trigger would automatically maintain the hierarchy after anyupdates to the tablethat would affect the hierarchy (i.e. updates to the MGR column).

    Drawbacks to This TechniqueOf course there are a few drawbacks to this technique:1. You have to create and maintain a "denormalized" column.2. You have to write and maintain the code that populates the column.3. Since ANY update to the table causes ALL of the rows to be updated,there maybe some performance problems for large frequently updatedhierarchies. (Thereare ways to reduce this impact, but they are usually design specific,and out of thescope of this tip).

    Tip #42: A Single Hierarchy View forMultipleHierarchies (Type: SQL)When you use hierarchical queries (queries using CONNECT BY andPRIOR), youalways have to specify the top of a particular hierarchy using the"START WITH"

  • 8/6/2019 Orc Info Tip

    25/36

    syntax. This is often done by hard coding the PK of the top of thehierarchy in the"START WITH" clause. However, if you have many hierarchies in thesame table, youmight want to be able to have the same program use ANY of the

    hierarchies, and specifywhich hierarchy (and thus, which "START WITH" key) at runtime.Wouldn't it be nice ifyou could put the hierarchy query in a view, and simply specify thehierarchy to use atruntime? Well, you can! This tip will show one technique for doing this.

    Starting With A Standard Hierarchy QueryI will use the infamous EMP table to illustrate this technique. However,since thestandard emp table only has one hierarchy (starting with "KING"), Iadded a second

    hierarchy. I also updated the DEPTNO for all of the standard records tohave the sameDEPTNO (which I use to differentiate the two hierarchies). Using thistables, you mightuse the following SQL for a standard hierarchical query:SQL>1 SELECT level, LPAD(' ',2*level-2)||emp.ename ename,emp.empno, emp.mgr, emp.deptno2 FROM Emp3 CONNECT BY PRIOR emp.empno = emp.mgr4* START WITH emp.empno = 7839SQL> /LEVEL ENAME EMPNO MGR DEPTNO--------- -------------------- --------- --------- ---------1 KING 7839 102 BLAKE 7698 7839 103 MARTIN 7654 7698 103 ALLEN 7499 7698 103 TURNER 7844 7698 103 JAMES 7900 7698 103 WARD 7521 7698 102 CLARK 7782 7839 103 MILLER 7934 7782 102 JONES 7566 7839 103 FORD 7902 7566 104 SMITH 7369 7902 105 Ken 999 7369 103 SCOTT 7788 7566 104 ADAMS 7876 7788 10

    I placed a second hierarchy in the same table, this one starting with"SONG":SQL>1 SELECT level, LPAD(' ',2*level-2)||emp.ename ename,emp.empno, emp.mgr, emp.deptno2 FROM Emp3 CONNECT BY PRIOR emp.empno = emp.mgr4* START WITH emp.empno = 6000SQL> /LEVEL ENAME EMPNO MGR DEPTNO

  • 8/6/2019 Orc Info Tip

    26/36

    --------- -------------------- --------- --------- ---------1 SONG 6000 402 GOMEZ 6001 6000 403 WILLIAMS 6002 6001 404 DIRKSEN 6003 6002 405 ATKINS 6004 6003 405 DESZELL 6005 6003 405 DEVITT 6006 6003 402 SMITH 6007 6000 40

    3 GEORGE 6008 6007 403 JONES 6009 6007 404 MILLER 6010 6009 404 BAKER 6011 6009 40

    Trying to Make The Query more GenericLet's try leaving off the "START WITH" in a view in an attempt to makea generichierarchy view:SQL> CREATE OR REPLACE VIEW Emp_Hier AS2 SELECT level lvl, LPAD(' ',2*level-2)||emp.ename ename,emp.empno, emp.mgr, emp.deptno3 FROM Emp4 CONNECT BY PRIOR emp.empno = emp.mgr

    5 /Now if we select from this view without any predicates, the query willstill return, but itwill return the results of a hierarchy starting with EVERY record in thetable. Forexample:SQL> SELECT lvl, ename, empno, mgr, deptno2 FROM Emp_Hier3 /LEVEL ENAME EMPNO MGR DEPTNO--------- -------------------- --------- --------- ---------1 KING 7839 10

    2 BLAKE 7698 7839 103 MARTIN 7654 7698 103 ALLEN 7499 7698 103 TURNER 7844 7698 103 JAMES 7900 7698 103 WARD 7521 7698 102 CLARK 7782 7839 103 MILLER 7934 7782 102 JONES 7566 7839 103 FORD 7902 7566 104 SMITH 7369 7902 105 Ken 999 7369 103 SCOTT 7788 7566 10

    4 ADAMS 7876 7788 101 BLAKE 7698 7839 102 MARTIN 7654 7698 102 ALLEN 7499 7698 102 TURNER 7844 7698 102 JAMES 7900 7698 102 WARD 7521 7698 101 CLARK 7782 7839 102 MILLER 7934 7782 10

  • 8/6/2019 Orc Info Tip

    27/36

    1 JONES 7566 7839 102 FORD 7902 7566 103 SMITH 7369 7902 104 Ken 999 7369 102 SCOTT 7788 7566 103 ADAMS 7876 7788 10

    1 MARTIN 7654 7698 101 ALLEN 7499 7698 101 TURNER 7844 7698 101 JAMES 7900 7698 101 WARD 7521 7698 10. . . . .. . . . .. . . . .1 SONG 6000 402 GOMEZ 6001 6000 403 WILLIAMS 6002 6001 404 DIRKSEN 6003 6002 405 ATKINS 6004 6003 405 DESZELL 6005 6003 405 DEVITT 6006 6003 402 SMITH 6007 6000 40

    3 GEORGE 6008 6007 403 JONES 6009 6007 404 MILLER 6010 6009 404 BAKER 6011 6009 401 GOMEZ 6001 6000 402 WILLIAMS 6002 6001 403 DIRKSEN 6003 6002 40

    . . . . .

    . . . . .

    . . . . .

    (NOTE: I did not display the complete results of this query)Notice that the results start with the standard hierarchy (beginningwith "KING"),followed by another hierarchy starting with "BLAKE" (which is a child of

    "KING", andshould not have it's own hierarchy), followed by "CLARK", "JONES", etc.There areeven one level hierarchies for the records at the bottom of the tree (i.e."MARTIN","ALLEN", etc.). Now this query is generic, and it will also pick up the2nd completehierarchy (starting with "SONG"). You can use the DEPTNO column toselect one or theother of the hierarchies:SQL> SELECT lvl, ename, empno, mgr, deptno

    2 FROM Emp_Hier3 WHERE deptno = 403 /LEVEL ENAME EMPNO MGR DEPTNO--------- -------------------- --------- --------- ---------1 SONG 6000 402 GOMEZ 6001 6000 403 WILLIAMS 6002 6001 404 DIRKSEN 6003 6002 405 ATKINS 6004 6003 405 DESZELL 6005 6003 40

  • 8/6/2019 Orc Info Tip

    28/36

    5 DEVITT 6006 6003 402 SMITH 6007 6000 403 GEORGE 6008 6007 403 JONES 6009 6007 404 MILLER 6010 6009 404 BAKER 6011 6009 401 GOMEZ 6001 6000 402 WILLIAMS 6002 6001 403 DIRKSEN 6003 6002 40

    4 ATKINS 6004 6003 404 DESZELL 6005 6003 404 DEVITT 6006 6003 401 WILLIAMS 6002 6001 402 DIRKSEN 6003 6002 403 ATKINS 6004 6003 403 DESZELL 6005 6003 403 DEVITT 6006 6003 401 DIRKSEN 6003 6002 402 ATKINS 6004 6003 402 DESZELL 6005 6003 402 DEVITT 6006 6003 401 ATKINS 6004 6003 401 DESZELL 6005 6003 401 DEVITT 6006 6003 401 SMITH 6007 6000 402 GEORGE 6008 6007 40

    2 JONES 6009 6007 403 MILLER 6010 6009 403 BAKER 6011 6009 401 GEORGE 6008 6007 401 JONES 6009 6007 402 MILLER 6010 6009 402 BAKER 6011 6009 401 MILLER 6010 6009 401 BAKER 6011 6009 40

    This limits the query to one of the hierarchies, but it does not eliminatethe spurioushierarchies. Therefore, leaving off the START WITH predicate is fairlyuseless

    Using a Database Function to Dynamically Determine

    the Top of theHierarchyIn order to add the "START WITH" back into the view, yet make theview dynamic, youcan create a database function that returns the top parent of ahierarchy given a key thatidentifies the hierarchy (the deptno in this example). The followingfunction does this forour example:CREATE OR REPLACE FUNCTION Get_Emp_Top(p_DeptNo IN NUMBER) RETURNNUMBER IS

    v_TopParent NUMBER;BEGINSELECT empno INTO v_TopParentFROM EmpWHERE Deptno = p_DeptnoAND mgr IS NULL;RETURN(v_TopParent);END;/

  • 8/6/2019 Orc Info Tip

    29/36

    Now, we update the view, adding a START WITH clause that uses thefunction:SQL> CREATE OR REPLACE VIEW Emp_Hier AS2 SELECT level lvl, LPAD(' ',2*level-2)||emp.ename ename,emp.empno, emp.mgr, emp.deptno3 FROM Emp

    4 CONNECT BY PRIOR emp.empno = emp.mgr5 START WITH emp.empno = Get_Emp_Top(emp.deptno)6 /SQL> SELECT lvl, ename, empno, mgr, deptno2 FROM Emp_Hier3 /LVL ENAME EMPNO MGR DEPTNO--------- -------------------- --------- --------- ---------1 KING 7839 102 BLAKE 7698 7839 103 MARTIN 7654 7698 103 ALLEN 7499 7698 103 TURNER 7844 7698 103 JAMES 7900 7698 103 WARD 7521 7698 102 CLARK 7782 7839 103 MILLER 7934 7782 102 JONES 7566 7839 103 FORD 7902 7566 104 SMITH 7369 7902 105 Ken 999 7369 103 SCOTT 7788 7566 104 ADAMS 7876 7788 101 SONG 6000 402 GOMEZ 6001 6000 403 WILLIAMS 6002 6001 404 DIRKSEN 6003 6002 405 ATKINS 6004 6003 405 DESZELL 6005 6003 405 DEVITT 6006 6003 402 SMITH 6007 6000 403 GEORGE 6008 6007 403 JONES 6009 6007 40

    4 MILLER 6010 6009 404 BAKER 6011 6009 40

    As you can see, we now have a view that will return ONLY thecomplete hierarchy ofboth hierarchies we have defined. We can simply add a predicate tothe SELECT fromthe view to only display one of the hierarchies, thereby giving us thedynamic selection ofthe hierarchy from the view:SQL> SELECT lvl, ename, empno, mgr, deptno2 FROM Emp_Hier3 WHERE DEPTNO = 40

    4 /LVL ENAME EMPNO MGR DEPTNO--------- -------------------- --------- --------- ---------1 SONG 6000 402 GOMEZ 6001 6000 403 WILLIAMS 6002 6001 404 DIRKSEN 6003 6002 405 ATKINS 6004 6003 405 DESZELL 6005 6003 405 DEVITT 6006 6003 402 SMITH 6007 6000 403 GEORGE 6008 6007 403 JONES 6009 6007 40

  • 8/6/2019 Orc Info Tip

    30/36

    4 MILLER 6010 6009 404 BAKER 6011 6009 40

    Putting a couple of Tips TogetherIn a previous tip (Tip #40), I detailed a method to allow joins tohierarchical queries. Wecan combine that technique with the one from this tip to give us a very

    powerful view:CREATE OR REPLACE VIEW emp_hier ASSELECT emphier.emplevel, emphier.ename ind_ename, emphier.ename,emphier.empno,dept.deptno, dept.dname, dept.loc,emp.ename mgr_enameFROM Dept, Emp,(select level emplevel, LPAD(' ',2*level-2)||ename ename,empno, mgr, deptnofrom Empconnect by prior empno = mgrstart with empno = Get_Emp_Top(emp.deptno)) emphier

    WHERE emphier.deptno = dept.deptnoAND emphier.mgr = emp.empno (+)

    Here is an example of using the view:SQL> select ind_ename, mgr_ename, dname, loc2 from emp_hier3 where deptno = 404 /IND_ENAME MGR_ENAME DNAME LOC-------------------- ---------- -------------- -------------SONG OPERATIONS BOSTONGOMEZ SONG OPERATIONS BOSTONWILLIAMS GOMEZ OPERATIONS BOSTONDIRKSEN WILLIAMS OPERATIONS BOSTONATKINS DIRKSEN OPERATIONS BOSTONDESZELL DIRKSEN OPERATIONS BOSTON

    DEVITT DIRKSEN OPERATIONS BOSTONSMITH SONG OPERATIONS BOSTONGEORGE SMITH OPERATIONS BOSTONJONES SMITH OPERATIONS BOSTONMILLER JONES OPERATIONS BOSTONBAKER JONES OPERATIONS BOSTON

    Click[here] for a SQL script that createsand populatesthe EMP table used in this example, thenruns the

    example queries. Tip #40: Using "InlineViews" to Jointo Hierarchical Queries (Type: SQL)Have you ever tried to join to a hierarchical query (a query usingCONNECT BY andPRIOR) only to get this message:ORA-01437: cannot have join with CONNECT BY

  • 8/6/2019 Orc Info Tip

    31/36

    One of the limitations of hierarchical queries is that you cannot join tothem. However,there are often times you would like to join to them anyway. Forinstance, if thehierarchy table only has surrogate keys, and you would like to display

    the real value.This tip shows how you can use "Inline Views" (which are SELECTs inthe FROMclause) to join tables to a hierarchical query.

    Starting With A Standard Hierarchy QueryI will use the infamous EMP/DEPT tables to illustrate this technique.Using these tables,you might use the following SQL for a standard hierarchical query:SQL>1 SELECT level, LPAD(' ',2*level-2)||emp.ename ename,emp.empno, emp.mgr, emp.deptno2 FROM Emp

    3 CONNECT BY PRIOR emp.empno = emp.mgr4* START WITH emp.empno = 7839SQL> /LEVEL ENAME EMPNO MGR DEPTNO--------- -------------------- --------- --------- ---------1 KING 7839 102 BLAKE 7698 7839 303 MARTIN 7654 7698 303 ALLEN 7499 7698 303 TURNER 7844 7698 303 JAMES 7900 7698 303 WARD 7521 7698 302 CLARK 7782 7839 303 MILLER 7934 7782 102 JONES 7566 7839 203 FORD 7902 7566 20

    4 SMITH 7369 7902 205 Ken 999 7369 203 SCOTT 7788 7566 204 ADAMS 7876 7788 20

    Try to Join This Query To the DEPT TableIf you try to join this query to the DEPT table, it won't work:SQL> l1 select level, LPAD(' ',2*level-2)||ename ename, empno, mgr,dept.deptno, dept.dname2 from emp, dept3 where emp.deptno = dept.deptno4 connect by prior empno = mgr5* start with empno = 7839SQL> /from emp, dept*ERROR at line 2:ORA-01437: cannot have join with CONNECT BY

    Place the Hierarchical Query in an "Inline View"Since Oracle 7.3, we could actually use a complete SELECT statementas one of the"tables" in a query. Using this technique, we can turn the hierarchical

  • 8/6/2019 Orc Info Tip

    32/36

    query into a "table"and join it do the DEPT table:SQL> l1 SELECT emphier.emplevel, emphier.ename, emphier.empno,dept.deptno, dept.dname2 FROM Dept

    3 ,(select level emplevel, LPAD(' ',2*level-2)||enameename, empno, mgr, deptno4 from Emp5 connect by prior empno = mgr6 start with empno = 78397 ) emphier8* WHERE emphier.deptno = dept.deptnoSQL> /EMPLEVEL ENAME EMPNO DEPTNO DNAME--------- -------------------- --------- --------- --------------1 KING 7839 10 ACCOUNTING2 BLAKE 7698 30 SALES3 MARTIN 7654 30 SALES3 ALLEN 7499 30 SALES3 TURNER 7844 30 SALES

    3 JAMES 7900 30 SALES3 WARD 7521 30 SALES2 CLARK 7782 30 SALES3 MILLER 7934 10 ACCOUNTING2 JONES 7566 20 RESEARCH3 FORD 7902 20 RESEARCH4 SMITH 7369 20 RESEARCH5 Ken 999 20 RESEARCH3 SCOTT 7788 20 RESEARCH4 ADAMS 7876 20 RESEARCH

    The SELECT statement inside the parentheses is treated just as if itwere a view that youare joining to. It is given an alias, "emphier", which is used to refer to itin the SELECT

    clause (i.e. "emphier.ename"), and in the WHERE clause (i.e."emphier.deptno"). Since itis treated like a view, we can join it to the Dept table with the followingpredicate:WHERE emphier.deptno = dept.deptno

    This will allow you to display the department name ("DNAME") in yourhierarchicalquery.

    Putting the Query into a ViewQuite often, these hierarchical queries can be useful in many programsand reports. It is

    often helpful to create a view that lists the hierarchy and joins to usefultables. Here is anexample of a view using the EMP/DEPT tables. This view allows you tolist thedepartment name and location and the manager name in the query:CREATE OR REPLACE VIEW emp_hier ASSELECT emphier.emplevel, emphier.ename ind_ename, emphier.ename,emphier.empno,dept.deptno, dept.dname, dept.loc

  • 8/6/2019 Orc Info Tip

    33/36

    ,emp.ename mgr_enameFROM Dept, Emp,(select level emplevel, LPAD(' ',2*level-2)||ename ename,empno, mgr, deptnofrom Empconnect by prior empno = mgr

    start with empno = 7839) emphierWHERE emphier.deptno = dept.deptnoAND emphier.mgr = emp.empno (+)

    Here is an example of using the view:SQL> select ind_ename, mgr_ename, dname, loc2 from emp_hier3SQL> /IND_ENAME MGR_ENAME DNAME LOC-------------------- ---------- -------------- -------------KING ACCOUNTING NEW YORKBLAKE KING SALES CHICAGOMARTIN BLAKE SALES CHICAGOALLEN BLAKE SALES CHICAGO

    TURNER BLAKE SALES CHICAGOJAMES BLAKE SALES CHICAGOWARD BLAKE SALES CHICAGOCLARK KING SALES CHICAGOMILLER CLARK ACCOUNTING NEW YORKJONES KING RESEARCH DALLASFORD JONES RESEARCH DALLASSMITH FORD RESEARCH DALLASKen SMITH RESEARCH DALLASSCOTT JONES RESEARCH DALLASADAMS SCOTT RESEARCH DALLAS

    Tip #38: Listing Records with the HighestValues using

    SQL Only. (Type: SQL)There are times where you want to simply return the rows with acertain number of thehighest (or lowest) values for a certain column. This type offunctionality is easy toimplement in PL/SQL (just order by the column and grab the first nrows from the query),but more difficult to do using SQL only. This tip shows you a method todo this in SQL.

    Data Used for The Examples in this TipThe following data (from the infamous EMP table) will be used for all of

    the examples inthis Tip:SQL> desc empName Null? Type------------------------------- -------- ----EMPNO NOT NULL NUMBER(4)ENAME CHAR(10)JOB CHAR(9)MGR NUMBER(4)HIREDATE DATESAL NUMBER(7,2)COMM NUMBER(7,2)

  • 8/6/2019 Orc Info Tip

    34/36

    DEPTNO NOT NULL NUMBER(2)SQL> SELECT empno, sal FROM EMP;EMPNO SAL--------- ---------41 420046 680099 900023 200011 4000

    10 350051 450052 450053 800054 290010 rows selected.

    ROWNUM does not work!Many SQL begginers are tempted to try to use ROWNUM along with anORDER BY tolimit the rows returned to the highest values. However, this does notwork, becuaseOracle sets the ROWNUM value before the query results are ordered!Consider thefollowing query:SQL> SELECT empno, sal, rownum2 FROM Emp3 ORDER BY sal DESC4SQL> /EMPNO SAL ROWNUM--------- --------- ---------99 9000 353 8000 946 6800 251 4500 752 4500 841 4200 111 4000 510 3500 654 2900 1023 2000 4

    Notice that the records are ordered by the SAL column, but not theROWNUM column.If you added a where clause to limit the query to the first threeROWNUMs, you wouldget:SQL> l1 SELECT empno, sal, rownum

    2 FROM Emp3 WHERE ROWNUM < 44* ORDER BY sal DESCSQL> /EMPNO SAL ROWNUM--------- --------- ---------99 9000 346 6800 241 4200 1

  • 8/6/2019 Orc Info Tip

    35/36

    Which does NOT return the three highest SALs!

    Solution: Correlated SubQuery to Same TableOne solution for this problem is to use a correlated subquery to thesame table. Thefollowing select will return the correct rows:

    SQL> l1 SELECT empno, sal2 FROM Emp e13 WHERE 3 > (SELECT COUNT(*) FROM Emp e24 WHERE e1.sal < e2.sal)5* ORDER BY SAL descSQL> /EMPNO SAL--------- ---------99 900053 800046 6800

    For every row processed by the main query, the correlated subquery

    returns a count(COUNT(*) ) of the number of rows with higher salaries (WHERE e1.sal< e2.sal). Thenthe main query only returns rows that have fewer than three salariesthat are higher(WHERE 3 > ...). For example, for EMPNO=46, the salary is "6800".There is only 1row with a higher salary (EMPNO=99), so the subquery returns "1",which is less than 3,causing the "WHERE 3 > ..." to evaluate to TRUE, thereby returning therow.

    A Problem With This TechniqueHowever, there is a problem with this method. What if there are morethan one row withthe same salary? Consider the following query, where we change it toreturn the first 4rows:SQL> l1 SELECT empno, sal2 FROM Emp e13 WHERE 4 > (SELECT COUNT(*) FROM Emp e24 WHERE e1.sal < e2.sal)5* ORDER BY SAL desc

    SQL> /EMPNO SAL--------- ---------99 900053 800046 680051 450052 4500

    Instead of returning 4 rows, it returned 5! This is because this

  • 8/6/2019 Orc Info Tip

    36/36

    technique returns ALL ofthe rows with the highest 4 salaries, not the first 4 rows. This is aproblem with thistechnique, so you need to make sure that it is acceptible in yourdesign before you use it.

    An Alternative Technique which Lists RankIf you want to use a join instead of a correlated subquery, you coulduse the followingselect:SQL> l1 SELECT e1.deptno, e1.empno, e1.sal, COUNT(distinct e2.empno)2 FROM Emp e1, Emp e23 WHERE e1.sal /DEPTNO EMPNO SAL COUNT(DISTINCTE2.EMPNO)--------- --------- --------- -----------------------30 99 9000 150 53 8000 220 46 6800 340 51 4500 550 52 4500 520 41 4200 640 11 4000 740 10 3500 850 54 2900 930 23 2000 10

    This select turns the correlated subquery into a self-join with a GROUPBY. This allows

    us to change the count into a sort of RANK. However, the problem withequal salariesremains (notice the two records with a "rank" of 5).This rank can then be used to select the first three rows:SQL> l1 SELECT e1.deptno, e1.empno, e1.sal, COUNT(distinct e2.empno)2 FROM Emp e1, Emp e23 WHERE e1.sal /DEPTNO EMPNO SAL COUNT(DISTINCTE2.EMPNO)--------- --------- --------- -----------------------30 99 9000 150 53 8000 220 46 6800 3