SlideShare a Scribd company logo
SQL SCRIPTING SORCERY                                               OR

USING SQL AND SQL*PLUS TO GO BEYOND “SELECT *                                                               FROM EMP;”
Daniel W. Fink

         How do I show the manager-employee relationship with the department name
         instead of number?
         How do I see the top two salaries in the company?
         How do I see the top two salaries in each department?
         How do I select a sample of a production database to create a test database?
         How do I select a sample from each department to create a test database?
         How do I report with a Hire Year axis and Department axis format?
         How do I give different raises if someone was hired between April 1, 1981 and
         April 1 1982 or was hired at another time?
         How do I select fields from different tables based upon data?
         How can I avoid a self-join?
         How can I create an interactive menu in SQL*Plus?
         How can I default an accepted field to today’s date?
         Can I use a select statement inside a decode?


Creativity is the key. Look upon the limits of SQL as a floor to build upon,
not a ceiling to stop progress.
SQL is the language used for all communication with Oracle databases. It is a very simple language with a limited number of
elements, but can be very powerful with a little creativity. Some of the limitations is that are cannot perform IF-THEN-ELSE
or LOOP logic. With a little ‘outside the box’ thinking, this logic can be partially duplicated. For any desired result set, there
are usually two or more sql statements that can achieve the desired result. One may be elegant, but a poor performer, while
the other may be complicated and convoluted, but a real speed demon. Which is better? That is for you to decide.
The first topic to be covered is the use of Inline Queries, or selects within a select. Some of the main uses for inline queries
are for Top N, joined-hierarchical, sampling and view-avoidance. The ubiquitous DECODE command will be covered next.
We will explore using DECODE for creating matrix reports, making range-of-value decisions and selecting different columns
from different tables.

Remember, WHAT you ask for and WHAT you receive are NOT always the same!
Warning – The topics covered here are examples of creative problem solving and may not be the optimal solution for
performance. In fact, they may cause your query to slow to a crawl. Always test the solutions on a nonproduction platform
and consider other avenues, such as PL/SQL or risk incurring the wrath of the DBA. All scripts were run against Oracle
8.0.6 on Solaris and Oracle 8.1.6 on Windows NT.


Basic Concepts
Row Source
All data is retrieved from a row source. This may be a base object, such as a table or index, or it may be a result set returned
from a previous operation. Oracle will use no more than 2 row sources at any one time. If a query accesses many objects,




www.rmoug.org                                                                                  RMOUG Training Days 2001
SQL Scripting Sorcery                                                                             Fink


only 2 row sources are used at any time. The result set from this operation becomes one of the row sources for the next
operation.

Result Set
A result set is the output of an operation. According to relational theory, records are processed in groups, not as atomic units.
If two or more tables are joined, there may be intermediate result sets built during the execution of the statement.

Access Paths
Each time data is retrieved from the database, an access path is used. This may be a full-table scan, by rowid using an index
or by an index scan. The use of a particular access path may be determined by the Oracle optimizer or by using hints.

Rownum
Each row that is retrieved from the database that matches the WHERE condition is placed in a result set. The sequence within
the result set is the value in the pseudocolumn ROWNUM. If Oracle is using a full-table scan, the first row of the first block
is the first in the result set and is assigned ROWNUM 1. If Oracle uses an index to access the data, the first entry in the index
is assigned ROWNUM 1. Once a complete result set is built, the ORDER BY clause is applied. The result may be that the
order of the result set and the order presented to the user are not the same. If ROWNUM is used, the numbers may be and, in
fact, usually are, out of sequence.

ROWNUM and PREDICATES (the WHERE clause)
ROWNUM can be used to restrict the amount of data returned by a query, as we will see shortly. ROWNUM is assigned for
each row that matches all of the applicable conditions in the PREDICATE. For example, ROWNUM is assigned to a row
only if it matches the deptno condition. One common mistake is trying to use ROWNUM to find values greater than 1.
ROWNUM conditions can only be equal to 1 or less than/less than or equal to a number other than 1. The following
statement will never return a row.
   select ename from emp where rownum = 2;
When the first row is read from the emp table, the PREDICATE is applied. Since this is the first row, it is conditionally
assigned ROWNUM of 1. However, the condition is that ROWNUM must be equal to 2. Since this condition is not met, the
row is not placed in the result set. The second row is read. Since the result set is empty, this row is assigned ROWNUM of 1.
Once again, the condition is not met and the row is not placed in the result set. This process repeats until all the rows are read.
However, the condition never evaluates to TRUE, so no rows are placed in the result set.

Hierarchical Query
A hierarchical query is a query representing the recursive relationship of a table. They are also called explosion-of-parts, tree,
organizational queries. The main requirement is that a column in the table has an optional relationship to another column. In
the EMP table, the MGR column is related to the EMPNO column.

What is an Inline Query?
An inline query is a subquery used in the place of a table or view in the FROM clause of a statement. It is also referred to as
an inline view, dynamic query or nested query. It was added in Oracle7.2. This can be used anywhere the FROM clause is
used, even in a correlated subquery. The inline query will create a result set that the parent query uses as a row source.
         select col1, col2
         from
         (select colA col1, colB col2
               from table1                           Inline Query         Parent Query
               where colA = ‘TRUE’)
         order by col1;


One of the challenges is to return a result set in ordered fashion without using the ORDER BY clause. ROWNUM is an
Oracle pseudocolumn that represents the order in which the row was retrieved from the database. It is assigned after any
conditions are applied in the WHERE or HAVING clause, but before the ORDER BY clause is applied. However, the use of
an index may cause the result set to be ordered before ROWNUM is assigned. In Oracle8i, the ability to use the ORDER BY
clause to the inline query was added. In prior releases, it was necessary to use access paths or other methods, such as
DISTINCT or GROUP BY, to return an ordered result set. If there are indexes available to satisfy the sorting criteria, hints



www.rmoug.org                                                                                  RMOUG Training Days 2002
SQL Scripting Sorcery                                                                            Fink


can be used inside the inline query. Additionally, in order to reverse the sort order to descending, the sort key must be
manipulated.
One maintenance consideration is that an application must be rewritten if the underlying objects have a significant change.
Because the query is not stored in the data dictionary, dependencies cannot be determined by querying the
USER_DEPENDENCIES view.
An inline query is evaluated once per execution of the parent query. It can also be used to reduce network traffic. One
example was a view that accessed data across a dblink. By rewriting the view to access the remote data via an inline query,
performance was significantly improved. The subquery was executed once and returned one result set across the network,
instead of being executed once for each row in the preceding result set.
The output of an inline query is a static result set. In other words, it does not change based upon values presented in the
parent query. This is very important to remember and a key differentiation between an inline query and a correlated subquery.


?   How do I show the manager-employee relationship with the department name
    instead of number?
At first glance, this appears to be a fairly straightforward request. The basic query is very simple. It is adding in the
Department Name that adds a complication. In the EMP table, only the department number (deptno) is kept. This adheres to
the rules of relational design. However, to most users, 10 is not as meaningful as ACCOUNTING, the description of the
department (dname) in the DEPT table.

Simple hierarchical query
    SELECT level,
           LPAD(' ',2*LEVEL-2)||emp.ename ename,
           emp.empno,
           emp.mgr,
           emp.deptno
    FROM Emp
    CONNECT BY PRIOR emp.empno = emp.mgr
    START WITH emp.mgr IS NULL

    LEVEL      ENAME                EMPNO        MGR     DEPTNO
    ---------- --------------- ---------- ---------- ----------
             1 KING                  7839                    10
             2   JONES               7566       7839         20
             3     SCOTT             7788       7566         20
             4       ADAMS           7876       7788         20
             3     FORD              7902       7566         20
             2   BLAKE               7698       7839         30
             3     ALLEN             7499       7698         30
             3     WARD              7521       7698         30
             3     MARTIN            7654       7698         30
             3     TURNER            7844       7698         30
             3     JAMES             7900       7698         30
             2   CLARK               7782       7839         10
             3     MILLER            7934       7782         10


Joined hierarchical Query
To retrieve the department name, we need to join the DEPT and EMP table. The relationship between them is the DEPTNO
column. A simple addition of the DNAME column, DEPT table and the join condition should return the department name
instead of department number.
    SELECT level,
           LPAD(' ',2*level-2)||emp.ename ename,
              emp.empno,
           emp.mgr,
           dept.dname
    FROM Emp, Dept
    where emp.deptno = dept.deptno
    CONNECT BY PRIOR emp.empno = emp.mgr
    START WITH emp.mgr is null;
    FROM Emp, Dept




www.rmoug.org                                                                                RMOUG Training Days 2002
SQL Scripting Sorcery                                                                          Fink


        *
   ERROR at line 2:
   ORA-01437: cannot have join with CONNECT BY
Unfortunately, a join within a hierarchical query is not allowed. In order to get around this restriction, we place the
hierarchical query within an inline query and the problem is solved. The hierarchical query must be placed as the inline
query.
Joined hierarchical query using an inline view
   select e.e_level,
          e.ename,
          e.empno,
          e.mgr,
          d.dname
   from dept d,
        (SELECT level e_level,
                LPAD(' ',2*level-2)||emp.ename ename,
                emp.empno empno,
                emp.mgr mgr,
                emp.deptno deptno
         FROM Emp
         CONNECT BY PRIOR emp.empno = emp.mgr
         START WITH emp.mgr is null) e
   where e.deptno = d.deptno;

   E_LEVEL ENAME               EMPNO        MGR DNAME
   ------- -------------- ---------- ---------- --------------
         1 KING                 7839            ACCOUNTING
         2   CLARK              7782       7839 ACCOUNTING
         3     MILLER           7934       7782 ACCOUNTING
         2   JONES              7566       7839 RESEARCH
         3     SCOTT            7788       7566 RESEARCH
         4       ADAMS          7876       7788 RESEARCH
         3     FORD             7902       7566 RESEARCH
         2   BLAKE              7698       7839 SALES
         3     ALLEN            7499       7698 SALES
         3     WARD             7521       7698 SALES
         3     MARTIN           7654       7698 SALES
         3     TURNER           7844       7698 SALES
         3     JAMES            7900       7698 SALES

How about adding the manager’s Name? We simply join with the emp table to get the manager name. It is important to
remember to use an outer join, otherwise KING is not returned because the record has no MGR relationship.
   select e.ename, e.empno, e.mgr, e2.ename, d.dname
   from dept d,
        (SELECT rownum e_rownum,
                LPAD(' ',2*level-2)||emp.ename ename,
                emp.empno empno,
                emp.mgr mgr,
                emp.deptno deptno
         FROM Emp
         CONNECT BY PRIOR emp.empno = emp.mgr
         START WITH emp.mgr is null) e,
         emp e2
    where e.deptno = d.deptno
      and e2.empno (+) = e.mgr
    order by e.e_rownum;

   ENAME                     EMPNO        MGR ENAME                              DNAME
   ------------------- ---------- ---------- --------------------               --------------
   KING                      7839                                               ACCOUNTING
     JONES                   7566       7839 KING                               RESEARCH
       SCOTT                 7788       7566 JONES                              RESEARCH
         ADAMS               7876       7788 SCOTT                              RESEARCH
       FORD                  7902       7566 JONES                              RESEARCH
         SMITH               7369       7902 FORD                               RESEARCH
     CLARK                   7782       7839 KING                               ACCOUNTING
       MILLER                7934       7782 CLARK                              ACCOUNTING
     BLAKE                   7698       7839 KING                               SALES
       TURNER                7844       7698 BLAKE                              SALES



www.rmoug.org                                                                               RMOUG Training Days 2002
SQL Scripting Sorcery                                                                                Fink


        ALLEN                        7499          7698   BLAKE                     SALES
        WARD                         7521          7698   BLAKE                     SALES
        MARTIN                       7654          7698   BLAKE                     SALES
        JAMES                        7900          7698   BLAKE                     SALES


Top N Queries
A Top N query is used to show the top values within a database. For example, a sales department may want to see the top 5
Customers by Revenue. One of the limitations in versions of Oracle prior to 8.1.5 was the inability to perform a Top N type
query. Other commercial databases had this functionality, for example, Microsoft Access Top Values and Sybase SET
ROWCOUNT. In order to simulate this behavior, we use inline queries to create an ordered result set. This type of query is
enhanced in Oracle 8.1.6 using the ORDER BY clause in the inline query.
The default sorting order for Oracle is ascending, from least to greatest. In the case of numbers, it is the smallest to largest. If
I use the default ordering scheme for salary, the lowest paid employees would be at the beginning of the result set. In pre-
Oracle 8.1.5 versions, I must find a method to reverse the sort order. I accomplished this by subtracting the salary from 0.
This results in a the larger salary being placed at the front of the result set. In later versions, I simply sort in descending order.



?   How do I see the top two salaries in the company?
The key in this solution is to create a sorted result set and use it to find the lowest salary that meets the qualification. It seems
that it would be straightforward to use ROWNUM to find the 2nd salary and make a direct comparison. However, it is
important to remember that ROWNUM = 2 is not a condition that ever evaluates to TRUE.
In order to force a descending sort on the result set, I sorted on the negative value of the salary. This caused the largest salary
to be listed first in the result set.
Oracle 8.0.6
    SELECT ename,
           empno,
           sal,
           deptno,
           hiredate
    FROM emp
    WHERE sal >= (SELECT MIN(i_sal)
                  FROM (SELECT DISTINCT 0-sal r_sal,
                               sal i_sal
                        FROM emp)
                   WHERE ROWNUM <= 2);
Oracle 8.1.6
    SELECT ename,
           empno,
           sal,
           deptno,
           hiredate
    FROM emp
    WHERE sal >= (SELECT MIN(i_sal)
                  FROM (SELECT sal i_sal
                        FROM emp
                        ORDER BY i_sal desc)
                   WHERE ROWNUM <= 2);

    ENAME           EMPNO        SAL     DEPTNO HIREDATE
    ---------- ---------- ---------- ---------- -----------
    KING             7839       5000         10 17-NOV-1981
    FORD             7902       3000         20 03-DEC-1981
    SCOTT            7788       3000         20 09-DEC-1982


Each query returns the exact same result set. The ORDER BY clause in 8i makes the 2nd query more understandable and
supportable.




www.rmoug.org                                                                                    RMOUG Training Days 2002
SQL Scripting Sorcery                                                                              Fink



?   How do I see the top two salaries in each department?
Now here is a bit of a puzzler. The top 2 salaries in the company was pretty straightforward. How could I select just the
department? Instead of using an inline query as the row source for the parent query, it was used as the row source for a
correlated subquery. Remember, the inline query can be used in any FROM clause, even in a nested correlated subquery!
    SELECT M.ENAME,
           M.SAL,
           M.DEPTNO
    FROM EMP M
    WHERE M.SAL >= (SELECT DISTINCT O.SAL
                    FROM EMP O
                    WHERE (O.SAL,2) IN (SELECT I.SAL,
                                               ROWNUM
                                        FROM (SELECT DISTINCT 0-I2.SAL R_SAL,
                                                     I2.DEPTNO,
                                                     I2.SAL,
                                                     I2.ROWID
                                              FROM EMP I2) I
                                         WHERE I.DEPTNO = M.DEPTNO))
    ORDER BY 3, 2 DESC;

    ENAME             SAL     DEPTNO
    ---------- ---------- ----------
    KING             5000         10
    CLARK            2450         10
    SCOTT            3000         20
    FORD             3000         20
    BLAKE            2850         30
    ALLEN            1600         30

Now let’s update TURNER to create duplicate salaries in department 30 and rerun the query.

    ENAME             SAL     DEPTNO
    ---------- ---------- ----------
    KING             5000         10
    CLARK            2450         10
    SCOTT            3000         20
    FORD             3000         20
    BLAKE            2850         30
    ALLEN            1600         30
    TURNER           1600         30

Note that department 30 returns 3 records because the 2nd salary has two matching values. In essence, Allen and Turner have
‘tied’ for second place. How can this be resolved? You could use rownum to restrict further, but you may not return the same
employee each time.


?   How do I select a sample of a production database to create a test database?
For testing purposes, it is often requested to retrieve a subset of the data. Until Oracle8I and the ability to export a subset of
the data, the process to manually extract data was difficult. Once you are able to select a sample, the next step of actually
extracting and transfering the data becomes much easier. The condition using MOD function on the ROWNUM in the inline
query will evaluate to TRUE for every 3rd record. To extract every 4th record, replace the 3 with a 4. It is just that simple.

    SELECT ENAME,
           EMPNO,
           SAL ,
           I_ROWNUM,
           ROWNUM
    FROM (SELECT ENAME,
                 EMPNO,
                 SAL,
                 ROWNUM I_ROWNUM
          FROM EMP)
    WHERE MOD(I_ROWNUM,3) = 0




www.rmoug.org                                                                                  RMOUG Training Days 2002
SQL Scripting Sorcery                                                                            Fink


    ENAME           EMPNO        SAL   I_ROWNUM     ROWNUM
    ---------- ---------- ---------- ---------- ----------
    WARD             7521       1250          3          1
    MARTIN           7654       1250          6          2
    SMITH            7369        800          9          3
    BLAKE            7698       2850         12          4

Oracle 8.1.6
    SELECT ENAME,
           EMPNO,
           SAL,
           ROWNUM
    FROM EMP SAMPLE (33);

This query should return 33% (4 or 5 depending upon sampling algorithm) of the rows in the EMP table, but instead returned
between 1 and 7 rows each time it was executed. There is no explanation for this sampling variance. There are significant
restrictions in using the sample clause. Joins and remote tables are not supported and the cost-based optimizer will always be
used. In this case, I found that the pre-Oracle8i usage of mod on rownum was more reliable.


?   How do I select a sample from each department to create a test database?
Once again, we use an inline view within the correlated subquery to restrict our result set.

    SELECT ENAME P_ENAME,
           DEPTNO P_DEPTNO
    FROM EMP P
    WHERE (P.ROWID,1) IN (SELECT I.ROWID I_ROWID,
                                 MOD(ROWNUM,3)
                          FROM EMP I
                          WHERE I.DEPTNO = P.DEPTNO)
    ORDER BY P_DEPTNO;

    P_ENAME      P_DEPTNO
    ---------- ----------
    CLARK              10
    ADAMS              20
    SMITH              20
    TURNER             30
    MARTIN             30



Decode
What is a DECODE?
A simple method to implement if-then-else logic. The construct closely resembles the case statement used in several
languages. It can only do equality comparison. May be abused and create “ugly SQL”. It may not always be the most elegant
solution. DECODE provides an opportunity to be very creative, but it can also create performance and maintenance
nightmares. One thing to remember is that each row is processed, but each column can be processed multiple times.

IF-THEN-ELSE
    DECODE (expression, if true1, then1, if true2, then2…else thenn)
    SELECT ename,
           deptno,
           DECODE(deptno,
                  10, ‘ACCOUNTING’,
                  20, ‘RESEARCH’,
                  30, ‘SALES’,
                  ‘NOT INDICATED’)
    FROM emp;

    ENAME          DEPTNO DECODE(DEPTNO
    ---------- ---------- -------------
    TURNER             30 SALES
    ALLEN              30 SALES




www.rmoug.org                                                                                  RMOUG Training Days 2002
SQL Scripting Sorcery                                                                           Fink


    WARD                  30   SALES
    ADAMS                 20   RESEARCH
    JONES                 20   RESEARCH
    MARTIN                30   SALES
    CLARK                 10   ACCOUNTING
    SCOTT                 20   RESEARCH
    SMITH                 20   RESEARCH
    KING                  10   ACCOUNTING
    JAMES                 30   SALES
    BLAKE                 30   SALES
    FORD                  20   RESEARCH
    MILLER                10   ACCOUNTING



?   How do I report with a Hire Year axis and Department axis format?
One common request is to implement a Matrix (or Down and Across) report. The main challenge is to know the number of
columns that are required. The rows can be flexible, but the columns are fixed.
    SELECT TO_CHAR(HIREDATE, 'YYYY') HIRE_YEAR ,
           SUM(DECODE(DEPTNO,
                      10, SAL+(NVL(COMM,0)),
                      0)) DEPT_10,
               SUM(DECODE(DEPTNO,
                          20, SAL+(NVL(COMM,0)),
                          0)) DEPT_20,
               SUM(DECODE(DEPTNO,
                          30, SAL+(NVL(COMM,0)),
                          0)) DEPT_30
     FROM EMP
     GROUP BY TO_CHAR(HIREDATE, 'YYYY');

    HIRE    DEPT_10    DEPT_20    DEPT_30
    ---- ---------- ---------- ----------
    1980          0        800          0
    1981       7450       5975      11600
    1982       1300       3000          0
    1983          0       1100          0

To switch the axis so that the Years are listed across the top and Departments are listed down the side.
    SELECT deptno DEPTNO,
           SUM(DECODE(to_char(hiredate, 'YYYY'),
                      '1980', SAL+(NVL(COMM,0)),
                      0)) YR_1980,
           SUM(DECODE(to_char(hiredate, 'YYYY'),
                      '1981', SAL+(NVL(COMM,0)),
                      0)) YR_1981,
           SUM(DECODE(to_char(hiredate, 'YYYY'),
                      '1982', SAL+(NVL(COMM,0)),
                      0)) YR_1982,
           SUM(DECODE(to_char(hiredate, 'YYYY'),
                      '1983', SAL+(NVL(COMM,0)),
                      0)) YR_1983
     FROM EMP
     GROUP BY deptno;

        DEPTNO    YR_1980    YR_1981    YR_1982    YR_1983
    ---------- ---------- ---------- ---------- ----------
            10          0       7450       1300          0
            20        800       5975       3000       1100
            30          0      11600          0          0



?   Changes based upon a range of values using GREATEST and LEAST
GREATEST will return the greatest value in a range of values
LEAST will return the least value in the range of values
The format is greatest (n, min_of_range), least(n, max_of_range)


www.rmoug.org                                                                                RMOUG Training Days 2002
SQL Scripting Sorcery                                                                         Fink


    SELECT ENAME,
           HIREDATE,
           GREATEST(HIREDATE, TO_DATE('01-APR-1981')) GT_DATE,
           LEAST(HIREDATE, TO_DATE('01-APR-1982')) LT_DATE,
           DECODE(GREATEST(HIREDATE, TO_DATE('01-APR-1981')),
                  LEAST(HIREDATE, TO_DATE('01-APR-1982')), .1,
                   .05) PAY_RAISE
    FROM EMP;

    ENAME        HIREDATE      GT_DATE       LT_DATE      PAY_RAISE
    ----------   -----------   -----------   ----------- ----------
    ALLEN        20-FEB-1981   01-APR-1981   20-FEB-1981        .05
    WARD         22-FEB-1981   01-APR-1981   22-FEB-1981        .05
    JONES        02-APR-1981   02-APR-1981   02-APR-1981         .1
    BLAKE        01-MAY-1981   01-MAY-1981   01-MAY-1981         .1
    CLARK        09-JUN-1981   09-JUN-1981   09-JUN-1981         .1
    TURNER       08-SEP-1981   08-SEP-1981   08-SEP-1981         .1
    MARTIN       28-SEP-1981   28-SEP-1981   28-SEP-1981         .1
    KING         17-NOV-1981   17-NOV-1981   17-NOV-1981         .1
    JAMES        03-DEC-1981   03-DEC-1981   03-DEC-1981         .1
    FORD         03-DEC-1981   03-DEC-1981   03-DEC-1981         .1
    MILLER       23-JAN-1982   23-JAN-1982   23-JAN-1982         .1
    SCOTT        09-DEC-1982   09-DEC-1982   01-APR-1982        .05
    ADAMS        12-JAN-1983   12-JAN-1983   01-APR-1982        .05

Let’s examine the first record for ALLEN. The decode expression evaluates to ‘01-APR-1981’. The first comparison
expression evaluates to ‘20-FEB-1981’. Is ‘01-APR-1981’ = ‘20-FEB-1981’? No, therefore the result is not used. There is
not another comparison, so the default value is used.
Let’s examine the record for Jones. The decode expression evaluates to ‘02-APR-1981’. The first comparison expression
evaluates to ‘02-APR-1981’. Is ‘01-APR-1981’ = ‘02-APR-1981’? Yes, therefore the result is used.
Let’s examine the first record for ADAMS. The decode expression evaluates to ‘12-JAN-1983’. The first comparison
expression evaluates to ‘01-APR-1982’. Is ‘12-JAN-1983’ = ‘01-APR-1982’? No, therefore the result is not used. There is
not another comparison, so the default value is used.



?   How do I select fields from different tables based upon data?
What if you need to select similar data from different tables. The traditional approach is to perform a union. Using the
USER_ views, we extract information about TABLE and INDEX segments. In this case, the USER_SEGMENTS view is
accessed twice.
    SELECT S.SEGMENT_NAME,     'TABLE' SEGMENT_TYPE, T.LAST_ANALYZED
    FROM USER_SEGMENTS S,
         USER_TABLES T
    WHERE S.SEGMENT_NAME =     T.TABLE_NAME
    UNION
    SELECT S.SEGMENT_NAME,     'INDEX' SEGMENT_TYPE, T.LAST_ANALYZED
    FROM USER_SEGMENTS S,
         USER_INDEXES T
    WHERE S.SEGMENT_NAME =     T.INDEX_NAME
    ORDER BY 1;

    SEGMENT_NAME                        SEGMENT_TYPE          ANALYZE_DATE
    ------------------------------      -----------------     ------------
    BONUS                               TABLE                 06-DEC-2000
    DEPT                                TABLE                 06-DEC-2000
    DUMMY                               TABLE
    EMP                                 TABLE                 06-DEC-2000
    EMP_2                               TABLE
    IX_DEPTNO                           INDEX                 06-DEC-2000
    IX_DNAME                            INDEX                 06-DEC-2000
    IX_ENAME                            INDEX                 06-DEC-2000
    SALGRADE                            TABLE                 06-DEC-2000

The less traditional approach is to perform a union. By using a DECODE with an outer join, the same result set is returned. In
this case, the USER_SEGMENTS view is accessed only once.


www.rmoug.org                                                                              RMOUG Training Days 2002
SQL Scripting Sorcery                                                                Fink


    SELECT S.SEGMENT_NAME,
           S.SEGMENT_TYPE,
           DECODE(S.SEGMENT_TYPE,
                  'INDEX', I.LAST_ANALYZED,
                  'TABLE', T.LAST_ANALYZED) ANALYZE_DATE
    FROM USER_SEGMENTS S,
         USER_TABLES T,
         USER_INDEXES I
    WHERE S.SEGMENT_NAME = T.TABLE_NAME (+)
    AND S.SEGMENT_NAME = I.INDEX_NAME (+);

    SEGMENT_NAME                          SEGMENT_TYPE           ANALYZE_DATE
    ------------------------------        -----------------      ------------
    BONUS                                 TABLE                  06-DEC-2000
    DEPT                                  TABLE                  06-DEC-2000
    DUMMY                                 TABLE
    EMP                                   TABLE                  06-DEC-2000
    EMP_2                                 TABLE
    IX_DEPTNO                             INDEX                  06-DEC-2000
    IX_DNAME                              INDEX                  06-DEC-2000
    IX_ENAME                              INDEX                  06-DEC-2000
    SALGRADE                              TABLE                  06-DEC-2000




?   How can I avoid a self-join?
One of the common DBA scripts is to look at hit ratios using the V$SYSSTAT view.
Is more efficient with small data sets (less than 1 million records).

    SELECT (1 - SUM(DECODE(name, 'physical reads', value,0)) /
            (SUM(DECODE(name, 'db block gets', value,0)) +
            (SUM(DECODE(name, 'consistent gets', value,0))))) * 100
            "Read Hit Ratio"
    FROM v$sysstat;

    Read Hit Ratio
    --------------
         99.780491


    SELECT (1 - a.value/(b.value + c.value)) * 100 "Read Hit Ratio"
    FROM v$sysstat a, v$sysstat b, v$sysstat c
    WHERE a.name = 'physical reads'
    AND b.name = 'db block gets'
    AND c.name = 'consistent gets';

    Read Hit Ratio
    --------------
        99.7805272



?   How can I create an interactive menu in SQL*Plus?


sql_menu.sql
    set verify off pages 0 feed off
    accept table_choice prompt 'Enter Table (EMP/DEPT/SALGRADE/EXIT): '
    set termout off
    spool run_script.sql
    select decode(UPPER('&table_choice'),
                  'EMP', '@select_emp',
                  'DEPT', '@select_dept',
                  'SALGRADE', '@select_salgrade',
                  'EXIT', '@exit_sqlplus',




www.rmoug.org                                                                      RMOUG Training Days 2002
SQL Scripting Sorcery                               Fink


                 '@select_error') script_choice
   from dual;
   spool off
   @run_script.sql

select_dept.sql
   set termout on
   select * from dept;
   @sql_menu

select_emp.sql
   set termout on
   select * from emp;
   @sql_menu

select_error.sql
   select 'Invalid Menu Choice'
   from dual;
   @sql_menu

select_salgrade.sql
   set termout on
   select * from salgrade;
   @sql_menu

exit_sqlplus.sql
   exit




www.rmoug.org                                     RMOUG Training Days 2002

More Related Content

What's hot

pl/sql online Training|sql online Training | iTeknowledge
pl/sql online Training|sql online Training | iTeknowledgepl/sql online Training|sql online Training | iTeknowledge
pl/sql online Training|sql online Training | iTeknowledge
Masood Khan
 
Displaying Data from Multiple Tables - Oracle Data Base
Displaying Data from Multiple Tables - Oracle Data BaseDisplaying Data from Multiple Tables - Oracle Data Base
Displaying Data from Multiple Tables - Oracle Data Base
Salman Memon
 
A SAS&lt;sup>®&lt;/sup> Users Guide to Regular Expressions When the Data Resi...
A SAS&lt;sup>®&lt;/sup> Users Guide to Regular Expressions When the Data Resi...A SAS&lt;sup>®&lt;/sup> Users Guide to Regular Expressions When the Data Resi...
A SAS&lt;sup>®&lt;/sup> Users Guide to Regular Expressions When the Data Resi...
Ken Borowiak
 
New T-SQL Features in SQL Server 2012
New T-SQL Features in SQL Server 2012 New T-SQL Features in SQL Server 2012
New T-SQL Features in SQL Server 2012
Richie Rump
 
Oracle Baisc Tutorial
Oracle Baisc TutorialOracle Baisc Tutorial
Oracle Baisc Tutorial
bunny0143
 
Application sql issues_and_tuning
Application sql issues_and_tuningApplication sql issues_and_tuning
Application sql issues_and_tuning
Anil Pandey
 
Forall & bulk binds
Forall & bulk bindsForall & bulk binds
Forall & bulk binds
Nawaz Sk
 
Oracle SQL Advanced
Oracle SQL AdvancedOracle SQL Advanced
Oracle SQL Advanced
Dhananjay Goel
 
SQL Tunning
SQL TunningSQL Tunning
SQL Tunning
Dhananjay Goel
 
Testing File
Testing FileTesting File
Testing File
malikredpot
 
Aggregating Data Using Group Functions
Aggregating Data Using Group FunctionsAggregating Data Using Group Functions
Aggregating Data Using Group Functions
Salman Memon
 
Advanced functions in PL SQL
Advanced functions in PL SQLAdvanced functions in PL SQL
Advanced functions in PL SQL
Hosein Zare
 
Oracle PLSQL Step By Step Guide
Oracle PLSQL Step By Step GuideOracle PLSQL Step By Step Guide
Oracle PLSQL Step By Step Guide
Srinimf-Slides
 
Pl sql-ch1
Pl sql-ch1Pl sql-ch1
Pl sql-ch1
Mukesh Tekwani
 
Writing Basic SQL SELECT Statements
Writing Basic SQL SELECT StatementsWriting Basic SQL SELECT Statements
Writing Basic SQL SELECT Statements
Salman Memon
 
ORACLE PL SQL
ORACLE PL SQLORACLE PL SQL
ORACLE PL SQL
Srinath Maharana
 
SQL select statement and functions
SQL select statement and functionsSQL select statement and functions
SQL select statement and functionsVikas Gupta
 
Oracle: PLSQL Introduction
Oracle: PLSQL IntroductionOracle: PLSQL Introduction
Oracle: PLSQL Introduction
DataminingTools Inc
 

What's hot (19)

pl/sql online Training|sql online Training | iTeknowledge
pl/sql online Training|sql online Training | iTeknowledgepl/sql online Training|sql online Training | iTeknowledge
pl/sql online Training|sql online Training | iTeknowledge
 
Displaying Data from Multiple Tables - Oracle Data Base
Displaying Data from Multiple Tables - Oracle Data BaseDisplaying Data from Multiple Tables - Oracle Data Base
Displaying Data from Multiple Tables - Oracle Data Base
 
A SAS&lt;sup>®&lt;/sup> Users Guide to Regular Expressions When the Data Resi...
A SAS&lt;sup>®&lt;/sup> Users Guide to Regular Expressions When the Data Resi...A SAS&lt;sup>®&lt;/sup> Users Guide to Regular Expressions When the Data Resi...
A SAS&lt;sup>®&lt;/sup> Users Guide to Regular Expressions When the Data Resi...
 
New T-SQL Features in SQL Server 2012
New T-SQL Features in SQL Server 2012 New T-SQL Features in SQL Server 2012
New T-SQL Features in SQL Server 2012
 
Oracle Baisc Tutorial
Oracle Baisc TutorialOracle Baisc Tutorial
Oracle Baisc Tutorial
 
Oracle etl openworld
Oracle etl openworldOracle etl openworld
Oracle etl openworld
 
Application sql issues_and_tuning
Application sql issues_and_tuningApplication sql issues_and_tuning
Application sql issues_and_tuning
 
Forall & bulk binds
Forall & bulk bindsForall & bulk binds
Forall & bulk binds
 
Oracle SQL Advanced
Oracle SQL AdvancedOracle SQL Advanced
Oracle SQL Advanced
 
SQL Tunning
SQL TunningSQL Tunning
SQL Tunning
 
Testing File
Testing FileTesting File
Testing File
 
Aggregating Data Using Group Functions
Aggregating Data Using Group FunctionsAggregating Data Using Group Functions
Aggregating Data Using Group Functions
 
Advanced functions in PL SQL
Advanced functions in PL SQLAdvanced functions in PL SQL
Advanced functions in PL SQL
 
Oracle PLSQL Step By Step Guide
Oracle PLSQL Step By Step GuideOracle PLSQL Step By Step Guide
Oracle PLSQL Step By Step Guide
 
Pl sql-ch1
Pl sql-ch1Pl sql-ch1
Pl sql-ch1
 
Writing Basic SQL SELECT Statements
Writing Basic SQL SELECT StatementsWriting Basic SQL SELECT Statements
Writing Basic SQL SELECT Statements
 
ORACLE PL SQL
ORACLE PL SQLORACLE PL SQL
ORACLE PL SQL
 
SQL select statement and functions
SQL select statement and functionsSQL select statement and functions
SQL select statement and functions
 
Oracle: PLSQL Introduction
Oracle: PLSQL IntroductionOracle: PLSQL Introduction
Oracle: PLSQL Introduction
 

Similar to Sql scripting sorcerypaper

SQL – The Natural Language for Analysis - Oracle - Whitepaper - 2431343
SQL – The Natural Language for Analysis - Oracle - Whitepaper - 2431343SQL – The Natural Language for Analysis - Oracle - Whitepaper - 2431343
SQL – The Natural Language for Analysis - Oracle - Whitepaper - 2431343
Edgar Alejandro Villegas
 
Performance tuning
Performance tuningPerformance tuning
Performance tuning
ami111
 
Myth busters - performance tuning 102 2008
Myth busters - performance tuning 102 2008Myth busters - performance tuning 102 2008
Myth busters - performance tuning 102 2008paulguerin
 
Database development coding standards
Database development coding standardsDatabase development coding standards
Database development coding standardsAlessandro Baratella
 
Oracle SQL Part 2
Oracle SQL Part 2Oracle SQL Part 2
Oracle SQL Part 2
Gurpreet singh
 
Advanced MySQL Query Optimizations
Advanced MySQL Query OptimizationsAdvanced MySQL Query Optimizations
Advanced MySQL Query Optimizations
Dave Stokes
 
Cursors, triggers, procedures
Cursors, triggers, proceduresCursors, triggers, procedures
Cursors, triggers, procedures
Vaibhav Kathuria
 
San diegophp
San diegophpSan diegophp
San diegophp
Dave Stokes
 
Goldilocks and the Three MySQL Queries
Goldilocks and the Three MySQL QueriesGoldilocks and the Three MySQL Queries
Goldilocks and the Three MySQL Queries
Dave Stokes
 
Db performance optimization with indexing
Db performance optimization with indexingDb performance optimization with indexing
Db performance optimization with indexing
Rajeev Kumar
 
Teradata sql-tuning-top-10
Teradata sql-tuning-top-10Teradata sql-tuning-top-10
Teradata sql-tuning-top-10
Roland Wenzlofsky
 
Db2 sql tuning and bmc catalog manager
Db2 sql tuning and bmc catalog manager Db2 sql tuning and bmc catalog manager
Db2 sql tuning and bmc catalog manager
Krishan Singh
 
White Paper for OMG! Identifying and Refactoring Common SQL...
White Paper for OMG! Identifying and Refactoring Common SQL...White Paper for OMG! Identifying and Refactoring Common SQL...
White Paper for OMG! Identifying and Refactoring Common SQL...
Jeff Jacobs
 
Oracle Sql Tuning
Oracle Sql TuningOracle Sql Tuning
Oracle Sql Tuning
Chris Adkin
 
Myth busters - performance tuning 101 2007
Myth busters - performance tuning 101 2007Myth busters - performance tuning 101 2007
Myth busters - performance tuning 101 2007paulguerin
 
Merging data (1)
Merging data (1)Merging data (1)
Merging data (1)
Ris Fernandez
 
Sql interview question part 12
Sql interview question part 12Sql interview question part 12
Sql interview question part 12
kaashiv1
 
Sql interview question part 12
Sql interview question part 12Sql interview question part 12
Sql interview question part 12kaashiv1
 
Presentation interpreting execution plans for sql statements
Presentation    interpreting execution plans for sql statementsPresentation    interpreting execution plans for sql statements
Presentation interpreting execution plans for sql statements
xKinAnx
 

Similar to Sql scripting sorcerypaper (20)

SQL – The Natural Language for Analysis - Oracle - Whitepaper - 2431343
SQL – The Natural Language for Analysis - Oracle - Whitepaper - 2431343SQL – The Natural Language for Analysis - Oracle - Whitepaper - 2431343
SQL – The Natural Language for Analysis - Oracle - Whitepaper - 2431343
 
Performance tuning
Performance tuningPerformance tuning
Performance tuning
 
Myth busters - performance tuning 102 2008
Myth busters - performance tuning 102 2008Myth busters - performance tuning 102 2008
Myth busters - performance tuning 102 2008
 
Database development coding standards
Database development coding standardsDatabase development coding standards
Database development coding standards
 
Oracle SQL Part 2
Oracle SQL Part 2Oracle SQL Part 2
Oracle SQL Part 2
 
Advanced MySQL Query Optimizations
Advanced MySQL Query OptimizationsAdvanced MySQL Query Optimizations
Advanced MySQL Query Optimizations
 
Cursors, triggers, procedures
Cursors, triggers, proceduresCursors, triggers, procedures
Cursors, triggers, procedures
 
San diegophp
San diegophpSan diegophp
San diegophp
 
Goldilocks and the Three MySQL Queries
Goldilocks and the Three MySQL QueriesGoldilocks and the Three MySQL Queries
Goldilocks and the Three MySQL Queries
 
Db performance optimization with indexing
Db performance optimization with indexingDb performance optimization with indexing
Db performance optimization with indexing
 
Teradata sql-tuning-top-10
Teradata sql-tuning-top-10Teradata sql-tuning-top-10
Teradata sql-tuning-top-10
 
Db2 sql tuning and bmc catalog manager
Db2 sql tuning and bmc catalog manager Db2 sql tuning and bmc catalog manager
Db2 sql tuning and bmc catalog manager
 
White Paper for OMG! Identifying and Refactoring Common SQL...
White Paper for OMG! Identifying and Refactoring Common SQL...White Paper for OMG! Identifying and Refactoring Common SQL...
White Paper for OMG! Identifying and Refactoring Common SQL...
 
Oracle Sql Tuning
Oracle Sql TuningOracle Sql Tuning
Oracle Sql Tuning
 
Myth busters - performance tuning 101 2007
Myth busters - performance tuning 101 2007Myth busters - performance tuning 101 2007
Myth busters - performance tuning 101 2007
 
Merging data (1)
Merging data (1)Merging data (1)
Merging data (1)
 
Sql interview question part 12
Sql interview question part 12Sql interview question part 12
Sql interview question part 12
 
Ebook12
Ebook12Ebook12
Ebook12
 
Sql interview question part 12
Sql interview question part 12Sql interview question part 12
Sql interview question part 12
 
Presentation interpreting execution plans for sql statements
Presentation    interpreting execution plans for sql statementsPresentation    interpreting execution plans for sql statements
Presentation interpreting execution plans for sql statements
 

More from oracle documents

Applyinga blockcentricapproachtotuning
Applyinga blockcentricapproachtotuningApplyinga blockcentricapproachtotuning
Applyinga blockcentricapproachtotuning
oracle documents
 
Windowsosauthent
WindowsosauthentWindowsosauthent
Windowsosauthent
oracle documents
 
Whatistnsnames
WhatistnsnamesWhatistnsnames
Whatistnsnames
oracle documents
 
Whatisadatabaselink
WhatisadatabaselinkWhatisadatabaselink
Whatisadatabaselink
oracle documents
 
Varraysandnestedtables
VarraysandnestedtablesVarraysandnestedtables
Varraysandnestedtables
oracle documents
 
Usertracing
UsertracingUsertracing
Usertracing
oracle documents
 
Userpasswrd
UserpasswrdUserpasswrd
Userpasswrd
oracle documents
 
Userlimit
UserlimitUserlimit
Userlimit
oracle documents
 
Undo internalspresentation
Undo internalspresentationUndo internalspresentation
Undo internalspresentation
oracle documents
 
Undo internals paper
Undo internals paperUndo internals paper
Undo internals paper
oracle documents
 
Tablespacelmt
TablespacelmtTablespacelmt
Tablespacelmt
oracle documents
 
Tablerename
TablerenameTablerename
Tablerename
oracle documents
 
Sql scripting sorcerypresentation
Sql scripting sorcerypresentationSql scripting sorcerypresentation
Sql scripting sorcerypresentation
oracle documents
 
Sql for dbaspresentation
Sql for dbaspresentationSql for dbaspresentation
Sql for dbaspresentation
oracle documents
 
Sequencereset
SequenceresetSequencereset
Sequencereset
oracle documents
 
Rollbacksizes
RollbacksizesRollbacksizes
Rollbacksizes
oracle documents
 
Rollbackshrinks
RollbackshrinksRollbackshrinks
Rollbackshrinks
oracle documents
 
Rollbacklmt
RollbacklmtRollbacklmt
Rollbacklmt
oracle documents
 
Rollbackblocking
RollbackblockingRollbackblocking
Rollbackblocking
oracle documents
 
Rollback1555s
Rollback1555sRollback1555s
Rollback1555s
oracle documents
 

More from oracle documents (20)

Applyinga blockcentricapproachtotuning
Applyinga blockcentricapproachtotuningApplyinga blockcentricapproachtotuning
Applyinga blockcentricapproachtotuning
 
Windowsosauthent
WindowsosauthentWindowsosauthent
Windowsosauthent
 
Whatistnsnames
WhatistnsnamesWhatistnsnames
Whatistnsnames
 
Whatisadatabaselink
WhatisadatabaselinkWhatisadatabaselink
Whatisadatabaselink
 
Varraysandnestedtables
VarraysandnestedtablesVarraysandnestedtables
Varraysandnestedtables
 
Usertracing
UsertracingUsertracing
Usertracing
 
Userpasswrd
UserpasswrdUserpasswrd
Userpasswrd
 
Userlimit
UserlimitUserlimit
Userlimit
 
Undo internalspresentation
Undo internalspresentationUndo internalspresentation
Undo internalspresentation
 
Undo internals paper
Undo internals paperUndo internals paper
Undo internals paper
 
Tablespacelmt
TablespacelmtTablespacelmt
Tablespacelmt
 
Tablerename
TablerenameTablerename
Tablerename
 
Sql scripting sorcerypresentation
Sql scripting sorcerypresentationSql scripting sorcerypresentation
Sql scripting sorcerypresentation
 
Sql for dbaspresentation
Sql for dbaspresentationSql for dbaspresentation
Sql for dbaspresentation
 
Sequencereset
SequenceresetSequencereset
Sequencereset
 
Rollbacksizes
RollbacksizesRollbacksizes
Rollbacksizes
 
Rollbackshrinks
RollbackshrinksRollbackshrinks
Rollbackshrinks
 
Rollbacklmt
RollbacklmtRollbacklmt
Rollbacklmt
 
Rollbackblocking
RollbackblockingRollbackblocking
Rollbackblocking
 
Rollback1555s
Rollback1555sRollback1555s
Rollback1555s
 

Sql scripting sorcerypaper

  • 1. SQL SCRIPTING SORCERY OR USING SQL AND SQL*PLUS TO GO BEYOND “SELECT * FROM EMP;” Daniel W. Fink How do I show the manager-employee relationship with the department name instead of number? How do I see the top two salaries in the company? How do I see the top two salaries in each department? How do I select a sample of a production database to create a test database? How do I select a sample from each department to create a test database? How do I report with a Hire Year axis and Department axis format? How do I give different raises if someone was hired between April 1, 1981 and April 1 1982 or was hired at another time? How do I select fields from different tables based upon data? How can I avoid a self-join? How can I create an interactive menu in SQL*Plus? How can I default an accepted field to today’s date? Can I use a select statement inside a decode? Creativity is the key. Look upon the limits of SQL as a floor to build upon, not a ceiling to stop progress. SQL is the language used for all communication with Oracle databases. It is a very simple language with a limited number of elements, but can be very powerful with a little creativity. Some of the limitations is that are cannot perform IF-THEN-ELSE or LOOP logic. With a little ‘outside the box’ thinking, this logic can be partially duplicated. For any desired result set, there are usually two or more sql statements that can achieve the desired result. One may be elegant, but a poor performer, while the other may be complicated and convoluted, but a real speed demon. Which is better? That is for you to decide. The first topic to be covered is the use of Inline Queries, or selects within a select. Some of the main uses for inline queries are for Top N, joined-hierarchical, sampling and view-avoidance. The ubiquitous DECODE command will be covered next. We will explore using DECODE for creating matrix reports, making range-of-value decisions and selecting different columns from different tables. Remember, WHAT you ask for and WHAT you receive are NOT always the same! Warning – The topics covered here are examples of creative problem solving and may not be the optimal solution for performance. In fact, they may cause your query to slow to a crawl. Always test the solutions on a nonproduction platform and consider other avenues, such as PL/SQL or risk incurring the wrath of the DBA. All scripts were run against Oracle 8.0.6 on Solaris and Oracle 8.1.6 on Windows NT. Basic Concepts Row Source All data is retrieved from a row source. This may be a base object, such as a table or index, or it may be a result set returned from a previous operation. Oracle will use no more than 2 row sources at any one time. If a query accesses many objects, www.rmoug.org RMOUG Training Days 2001
  • 2. SQL Scripting Sorcery Fink only 2 row sources are used at any time. The result set from this operation becomes one of the row sources for the next operation. Result Set A result set is the output of an operation. According to relational theory, records are processed in groups, not as atomic units. If two or more tables are joined, there may be intermediate result sets built during the execution of the statement. Access Paths Each time data is retrieved from the database, an access path is used. This may be a full-table scan, by rowid using an index or by an index scan. The use of a particular access path may be determined by the Oracle optimizer or by using hints. Rownum Each row that is retrieved from the database that matches the WHERE condition is placed in a result set. The sequence within the result set is the value in the pseudocolumn ROWNUM. If Oracle is using a full-table scan, the first row of the first block is the first in the result set and is assigned ROWNUM 1. If Oracle uses an index to access the data, the first entry in the index is assigned ROWNUM 1. Once a complete result set is built, the ORDER BY clause is applied. The result may be that the order of the result set and the order presented to the user are not the same. If ROWNUM is used, the numbers may be and, in fact, usually are, out of sequence. ROWNUM and PREDICATES (the WHERE clause) ROWNUM can be used to restrict the amount of data returned by a query, as we will see shortly. ROWNUM is assigned for each row that matches all of the applicable conditions in the PREDICATE. For example, ROWNUM is assigned to a row only if it matches the deptno condition. One common mistake is trying to use ROWNUM to find values greater than 1. ROWNUM conditions can only be equal to 1 or less than/less than or equal to a number other than 1. The following statement will never return a row. select ename from emp where rownum = 2; When the first row is read from the emp table, the PREDICATE is applied. Since this is the first row, it is conditionally assigned ROWNUM of 1. However, the condition is that ROWNUM must be equal to 2. Since this condition is not met, the row is not placed in the result set. The second row is read. Since the result set is empty, this row is assigned ROWNUM of 1. Once again, the condition is not met and the row is not placed in the result set. This process repeats until all the rows are read. However, the condition never evaluates to TRUE, so no rows are placed in the result set. Hierarchical Query A hierarchical query is a query representing the recursive relationship of a table. They are also called explosion-of-parts, tree, organizational queries. The main requirement is that a column in the table has an optional relationship to another column. In the EMP table, the MGR column is related to the EMPNO column. What is an Inline Query? An inline query is a subquery used in the place of a table or view in the FROM clause of a statement. It is also referred to as an inline view, dynamic query or nested query. It was added in Oracle7.2. This can be used anywhere the FROM clause is used, even in a correlated subquery. The inline query will create a result set that the parent query uses as a row source. select col1, col2 from (select colA col1, colB col2 from table1 Inline Query Parent Query where colA = ‘TRUE’) order by col1; One of the challenges is to return a result set in ordered fashion without using the ORDER BY clause. ROWNUM is an Oracle pseudocolumn that represents the order in which the row was retrieved from the database. It is assigned after any conditions are applied in the WHERE or HAVING clause, but before the ORDER BY clause is applied. However, the use of an index may cause the result set to be ordered before ROWNUM is assigned. In Oracle8i, the ability to use the ORDER BY clause to the inline query was added. In prior releases, it was necessary to use access paths or other methods, such as DISTINCT or GROUP BY, to return an ordered result set. If there are indexes available to satisfy the sorting criteria, hints www.rmoug.org RMOUG Training Days 2002
  • 3. SQL Scripting Sorcery Fink can be used inside the inline query. Additionally, in order to reverse the sort order to descending, the sort key must be manipulated. One maintenance consideration is that an application must be rewritten if the underlying objects have a significant change. Because the query is not stored in the data dictionary, dependencies cannot be determined by querying the USER_DEPENDENCIES view. An inline query is evaluated once per execution of the parent query. It can also be used to reduce network traffic. One example was a view that accessed data across a dblink. By rewriting the view to access the remote data via an inline query, performance was significantly improved. The subquery was executed once and returned one result set across the network, instead of being executed once for each row in the preceding result set. The output of an inline query is a static result set. In other words, it does not change based upon values presented in the parent query. This is very important to remember and a key differentiation between an inline query and a correlated subquery. ? How do I show the manager-employee relationship with the department name instead of number? At first glance, this appears to be a fairly straightforward request. The basic query is very simple. It is adding in the Department Name that adds a complication. In the EMP table, only the department number (deptno) is kept. This adheres to the rules of relational design. However, to most users, 10 is not as meaningful as ACCOUNTING, the description of the department (dname) in the DEPT table. Simple hierarchical query SELECT level, LPAD(' ',2*LEVEL-2)||emp.ename ename, emp.empno, emp.mgr, emp.deptno FROM Emp CONNECT BY PRIOR emp.empno = emp.mgr START WITH emp.mgr IS NULL LEVEL ENAME EMPNO MGR DEPTNO ---------- --------------- ---------- ---------- ---------- 1 KING 7839 10 2 JONES 7566 7839 20 3 SCOTT 7788 7566 20 4 ADAMS 7876 7788 20 3 FORD 7902 7566 20 2 BLAKE 7698 7839 30 3 ALLEN 7499 7698 30 3 WARD 7521 7698 30 3 MARTIN 7654 7698 30 3 TURNER 7844 7698 30 3 JAMES 7900 7698 30 2 CLARK 7782 7839 10 3 MILLER 7934 7782 10 Joined hierarchical Query To retrieve the department name, we need to join the DEPT and EMP table. The relationship between them is the DEPTNO column. A simple addition of the DNAME column, DEPT table and the join condition should return the department name instead of department number. SELECT level, LPAD(' ',2*level-2)||emp.ename ename, emp.empno, emp.mgr, dept.dname FROM Emp, Dept where emp.deptno = dept.deptno CONNECT BY PRIOR emp.empno = emp.mgr START WITH emp.mgr is null; FROM Emp, Dept www.rmoug.org RMOUG Training Days 2002
  • 4. SQL Scripting Sorcery Fink * ERROR at line 2: ORA-01437: cannot have join with CONNECT BY Unfortunately, a join within a hierarchical query is not allowed. In order to get around this restriction, we place the hierarchical query within an inline query and the problem is solved. The hierarchical query must be placed as the inline query. Joined hierarchical query using an inline view select e.e_level, e.ename, e.empno, e.mgr, d.dname from dept d, (SELECT level e_level, LPAD(' ',2*level-2)||emp.ename ename, emp.empno empno, emp.mgr mgr, emp.deptno deptno FROM Emp CONNECT BY PRIOR emp.empno = emp.mgr START WITH emp.mgr is null) e where e.deptno = d.deptno; E_LEVEL ENAME EMPNO MGR DNAME ------- -------------- ---------- ---------- -------------- 1 KING 7839 ACCOUNTING 2 CLARK 7782 7839 ACCOUNTING 3 MILLER 7934 7782 ACCOUNTING 2 JONES 7566 7839 RESEARCH 3 SCOTT 7788 7566 RESEARCH 4 ADAMS 7876 7788 RESEARCH 3 FORD 7902 7566 RESEARCH 2 BLAKE 7698 7839 SALES 3 ALLEN 7499 7698 SALES 3 WARD 7521 7698 SALES 3 MARTIN 7654 7698 SALES 3 TURNER 7844 7698 SALES 3 JAMES 7900 7698 SALES How about adding the manager’s Name? We simply join with the emp table to get the manager name. It is important to remember to use an outer join, otherwise KING is not returned because the record has no MGR relationship. select e.ename, e.empno, e.mgr, e2.ename, d.dname from dept d, (SELECT rownum e_rownum, LPAD(' ',2*level-2)||emp.ename ename, emp.empno empno, emp.mgr mgr, emp.deptno deptno FROM Emp CONNECT BY PRIOR emp.empno = emp.mgr START WITH emp.mgr is null) e, emp e2 where e.deptno = d.deptno and e2.empno (+) = e.mgr order by e.e_rownum; ENAME EMPNO MGR ENAME DNAME ------------------- ---------- ---------- -------------------- -------------- KING 7839 ACCOUNTING JONES 7566 7839 KING RESEARCH SCOTT 7788 7566 JONES RESEARCH ADAMS 7876 7788 SCOTT RESEARCH FORD 7902 7566 JONES RESEARCH SMITH 7369 7902 FORD RESEARCH CLARK 7782 7839 KING ACCOUNTING MILLER 7934 7782 CLARK ACCOUNTING BLAKE 7698 7839 KING SALES TURNER 7844 7698 BLAKE SALES www.rmoug.org RMOUG Training Days 2002
  • 5. SQL Scripting Sorcery Fink ALLEN 7499 7698 BLAKE SALES WARD 7521 7698 BLAKE SALES MARTIN 7654 7698 BLAKE SALES JAMES 7900 7698 BLAKE SALES Top N Queries A Top N query is used to show the top values within a database. For example, a sales department may want to see the top 5 Customers by Revenue. One of the limitations in versions of Oracle prior to 8.1.5 was the inability to perform a Top N type query. Other commercial databases had this functionality, for example, Microsoft Access Top Values and Sybase SET ROWCOUNT. In order to simulate this behavior, we use inline queries to create an ordered result set. This type of query is enhanced in Oracle 8.1.6 using the ORDER BY clause in the inline query. The default sorting order for Oracle is ascending, from least to greatest. In the case of numbers, it is the smallest to largest. If I use the default ordering scheme for salary, the lowest paid employees would be at the beginning of the result set. In pre- Oracle 8.1.5 versions, I must find a method to reverse the sort order. I accomplished this by subtracting the salary from 0. This results in a the larger salary being placed at the front of the result set. In later versions, I simply sort in descending order. ? How do I see the top two salaries in the company? The key in this solution is to create a sorted result set and use it to find the lowest salary that meets the qualification. It seems that it would be straightforward to use ROWNUM to find the 2nd salary and make a direct comparison. However, it is important to remember that ROWNUM = 2 is not a condition that ever evaluates to TRUE. In order to force a descending sort on the result set, I sorted on the negative value of the salary. This caused the largest salary to be listed first in the result set. Oracle 8.0.6 SELECT ename, empno, sal, deptno, hiredate FROM emp WHERE sal >= (SELECT MIN(i_sal) FROM (SELECT DISTINCT 0-sal r_sal, sal i_sal FROM emp) WHERE ROWNUM <= 2); Oracle 8.1.6 SELECT ename, empno, sal, deptno, hiredate FROM emp WHERE sal >= (SELECT MIN(i_sal) FROM (SELECT sal i_sal FROM emp ORDER BY i_sal desc) WHERE ROWNUM <= 2); ENAME EMPNO SAL DEPTNO HIREDATE ---------- ---------- ---------- ---------- ----------- KING 7839 5000 10 17-NOV-1981 FORD 7902 3000 20 03-DEC-1981 SCOTT 7788 3000 20 09-DEC-1982 Each query returns the exact same result set. The ORDER BY clause in 8i makes the 2nd query more understandable and supportable. www.rmoug.org RMOUG Training Days 2002
  • 6. SQL Scripting Sorcery Fink ? How do I see the top two salaries in each department? Now here is a bit of a puzzler. The top 2 salaries in the company was pretty straightforward. How could I select just the department? Instead of using an inline query as the row source for the parent query, it was used as the row source for a correlated subquery. Remember, the inline query can be used in any FROM clause, even in a nested correlated subquery! SELECT M.ENAME, M.SAL, M.DEPTNO FROM EMP M WHERE M.SAL >= (SELECT DISTINCT O.SAL FROM EMP O WHERE (O.SAL,2) IN (SELECT I.SAL, ROWNUM FROM (SELECT DISTINCT 0-I2.SAL R_SAL, I2.DEPTNO, I2.SAL, I2.ROWID FROM EMP I2) I WHERE I.DEPTNO = M.DEPTNO)) ORDER BY 3, 2 DESC; ENAME SAL DEPTNO ---------- ---------- ---------- KING 5000 10 CLARK 2450 10 SCOTT 3000 20 FORD 3000 20 BLAKE 2850 30 ALLEN 1600 30 Now let’s update TURNER to create duplicate salaries in department 30 and rerun the query. ENAME SAL DEPTNO ---------- ---------- ---------- KING 5000 10 CLARK 2450 10 SCOTT 3000 20 FORD 3000 20 BLAKE 2850 30 ALLEN 1600 30 TURNER 1600 30 Note that department 30 returns 3 records because the 2nd salary has two matching values. In essence, Allen and Turner have ‘tied’ for second place. How can this be resolved? You could use rownum to restrict further, but you may not return the same employee each time. ? How do I select a sample of a production database to create a test database? For testing purposes, it is often requested to retrieve a subset of the data. Until Oracle8I and the ability to export a subset of the data, the process to manually extract data was difficult. Once you are able to select a sample, the next step of actually extracting and transfering the data becomes much easier. The condition using MOD function on the ROWNUM in the inline query will evaluate to TRUE for every 3rd record. To extract every 4th record, replace the 3 with a 4. It is just that simple. SELECT ENAME, EMPNO, SAL , I_ROWNUM, ROWNUM FROM (SELECT ENAME, EMPNO, SAL, ROWNUM I_ROWNUM FROM EMP) WHERE MOD(I_ROWNUM,3) = 0 www.rmoug.org RMOUG Training Days 2002
  • 7. SQL Scripting Sorcery Fink ENAME EMPNO SAL I_ROWNUM ROWNUM ---------- ---------- ---------- ---------- ---------- WARD 7521 1250 3 1 MARTIN 7654 1250 6 2 SMITH 7369 800 9 3 BLAKE 7698 2850 12 4 Oracle 8.1.6 SELECT ENAME, EMPNO, SAL, ROWNUM FROM EMP SAMPLE (33); This query should return 33% (4 or 5 depending upon sampling algorithm) of the rows in the EMP table, but instead returned between 1 and 7 rows each time it was executed. There is no explanation for this sampling variance. There are significant restrictions in using the sample clause. Joins and remote tables are not supported and the cost-based optimizer will always be used. In this case, I found that the pre-Oracle8i usage of mod on rownum was more reliable. ? How do I select a sample from each department to create a test database? Once again, we use an inline view within the correlated subquery to restrict our result set. SELECT ENAME P_ENAME, DEPTNO P_DEPTNO FROM EMP P WHERE (P.ROWID,1) IN (SELECT I.ROWID I_ROWID, MOD(ROWNUM,3) FROM EMP I WHERE I.DEPTNO = P.DEPTNO) ORDER BY P_DEPTNO; P_ENAME P_DEPTNO ---------- ---------- CLARK 10 ADAMS 20 SMITH 20 TURNER 30 MARTIN 30 Decode What is a DECODE? A simple method to implement if-then-else logic. The construct closely resembles the case statement used in several languages. It can only do equality comparison. May be abused and create “ugly SQL”. It may not always be the most elegant solution. DECODE provides an opportunity to be very creative, but it can also create performance and maintenance nightmares. One thing to remember is that each row is processed, but each column can be processed multiple times. IF-THEN-ELSE DECODE (expression, if true1, then1, if true2, then2…else thenn) SELECT ename, deptno, DECODE(deptno, 10, ‘ACCOUNTING’, 20, ‘RESEARCH’, 30, ‘SALES’, ‘NOT INDICATED’) FROM emp; ENAME DEPTNO DECODE(DEPTNO ---------- ---------- ------------- TURNER 30 SALES ALLEN 30 SALES www.rmoug.org RMOUG Training Days 2002
  • 8. SQL Scripting Sorcery Fink WARD 30 SALES ADAMS 20 RESEARCH JONES 20 RESEARCH MARTIN 30 SALES CLARK 10 ACCOUNTING SCOTT 20 RESEARCH SMITH 20 RESEARCH KING 10 ACCOUNTING JAMES 30 SALES BLAKE 30 SALES FORD 20 RESEARCH MILLER 10 ACCOUNTING ? How do I report with a Hire Year axis and Department axis format? One common request is to implement a Matrix (or Down and Across) report. The main challenge is to know the number of columns that are required. The rows can be flexible, but the columns are fixed. SELECT TO_CHAR(HIREDATE, 'YYYY') HIRE_YEAR , SUM(DECODE(DEPTNO, 10, SAL+(NVL(COMM,0)), 0)) DEPT_10, SUM(DECODE(DEPTNO, 20, SAL+(NVL(COMM,0)), 0)) DEPT_20, SUM(DECODE(DEPTNO, 30, SAL+(NVL(COMM,0)), 0)) DEPT_30 FROM EMP GROUP BY TO_CHAR(HIREDATE, 'YYYY'); HIRE DEPT_10 DEPT_20 DEPT_30 ---- ---------- ---------- ---------- 1980 0 800 0 1981 7450 5975 11600 1982 1300 3000 0 1983 0 1100 0 To switch the axis so that the Years are listed across the top and Departments are listed down the side. SELECT deptno DEPTNO, SUM(DECODE(to_char(hiredate, 'YYYY'), '1980', SAL+(NVL(COMM,0)), 0)) YR_1980, SUM(DECODE(to_char(hiredate, 'YYYY'), '1981', SAL+(NVL(COMM,0)), 0)) YR_1981, SUM(DECODE(to_char(hiredate, 'YYYY'), '1982', SAL+(NVL(COMM,0)), 0)) YR_1982, SUM(DECODE(to_char(hiredate, 'YYYY'), '1983', SAL+(NVL(COMM,0)), 0)) YR_1983 FROM EMP GROUP BY deptno; DEPTNO YR_1980 YR_1981 YR_1982 YR_1983 ---------- ---------- ---------- ---------- ---------- 10 0 7450 1300 0 20 800 5975 3000 1100 30 0 11600 0 0 ? Changes based upon a range of values using GREATEST and LEAST GREATEST will return the greatest value in a range of values LEAST will return the least value in the range of values The format is greatest (n, min_of_range), least(n, max_of_range) www.rmoug.org RMOUG Training Days 2002
  • 9. SQL Scripting Sorcery Fink SELECT ENAME, HIREDATE, GREATEST(HIREDATE, TO_DATE('01-APR-1981')) GT_DATE, LEAST(HIREDATE, TO_DATE('01-APR-1982')) LT_DATE, DECODE(GREATEST(HIREDATE, TO_DATE('01-APR-1981')), LEAST(HIREDATE, TO_DATE('01-APR-1982')), .1, .05) PAY_RAISE FROM EMP; ENAME HIREDATE GT_DATE LT_DATE PAY_RAISE ---------- ----------- ----------- ----------- ---------- ALLEN 20-FEB-1981 01-APR-1981 20-FEB-1981 .05 WARD 22-FEB-1981 01-APR-1981 22-FEB-1981 .05 JONES 02-APR-1981 02-APR-1981 02-APR-1981 .1 BLAKE 01-MAY-1981 01-MAY-1981 01-MAY-1981 .1 CLARK 09-JUN-1981 09-JUN-1981 09-JUN-1981 .1 TURNER 08-SEP-1981 08-SEP-1981 08-SEP-1981 .1 MARTIN 28-SEP-1981 28-SEP-1981 28-SEP-1981 .1 KING 17-NOV-1981 17-NOV-1981 17-NOV-1981 .1 JAMES 03-DEC-1981 03-DEC-1981 03-DEC-1981 .1 FORD 03-DEC-1981 03-DEC-1981 03-DEC-1981 .1 MILLER 23-JAN-1982 23-JAN-1982 23-JAN-1982 .1 SCOTT 09-DEC-1982 09-DEC-1982 01-APR-1982 .05 ADAMS 12-JAN-1983 12-JAN-1983 01-APR-1982 .05 Let’s examine the first record for ALLEN. The decode expression evaluates to ‘01-APR-1981’. The first comparison expression evaluates to ‘20-FEB-1981’. Is ‘01-APR-1981’ = ‘20-FEB-1981’? No, therefore the result is not used. There is not another comparison, so the default value is used. Let’s examine the record for Jones. The decode expression evaluates to ‘02-APR-1981’. The first comparison expression evaluates to ‘02-APR-1981’. Is ‘01-APR-1981’ = ‘02-APR-1981’? Yes, therefore the result is used. Let’s examine the first record for ADAMS. The decode expression evaluates to ‘12-JAN-1983’. The first comparison expression evaluates to ‘01-APR-1982’. Is ‘12-JAN-1983’ = ‘01-APR-1982’? No, therefore the result is not used. There is not another comparison, so the default value is used. ? How do I select fields from different tables based upon data? What if you need to select similar data from different tables. The traditional approach is to perform a union. Using the USER_ views, we extract information about TABLE and INDEX segments. In this case, the USER_SEGMENTS view is accessed twice. SELECT S.SEGMENT_NAME, 'TABLE' SEGMENT_TYPE, T.LAST_ANALYZED FROM USER_SEGMENTS S, USER_TABLES T WHERE S.SEGMENT_NAME = T.TABLE_NAME UNION SELECT S.SEGMENT_NAME, 'INDEX' SEGMENT_TYPE, T.LAST_ANALYZED FROM USER_SEGMENTS S, USER_INDEXES T WHERE S.SEGMENT_NAME = T.INDEX_NAME ORDER BY 1; SEGMENT_NAME SEGMENT_TYPE ANALYZE_DATE ------------------------------ ----------------- ------------ BONUS TABLE 06-DEC-2000 DEPT TABLE 06-DEC-2000 DUMMY TABLE EMP TABLE 06-DEC-2000 EMP_2 TABLE IX_DEPTNO INDEX 06-DEC-2000 IX_DNAME INDEX 06-DEC-2000 IX_ENAME INDEX 06-DEC-2000 SALGRADE TABLE 06-DEC-2000 The less traditional approach is to perform a union. By using a DECODE with an outer join, the same result set is returned. In this case, the USER_SEGMENTS view is accessed only once. www.rmoug.org RMOUG Training Days 2002
  • 10. SQL Scripting Sorcery Fink SELECT S.SEGMENT_NAME, S.SEGMENT_TYPE, DECODE(S.SEGMENT_TYPE, 'INDEX', I.LAST_ANALYZED, 'TABLE', T.LAST_ANALYZED) ANALYZE_DATE FROM USER_SEGMENTS S, USER_TABLES T, USER_INDEXES I WHERE S.SEGMENT_NAME = T.TABLE_NAME (+) AND S.SEGMENT_NAME = I.INDEX_NAME (+); SEGMENT_NAME SEGMENT_TYPE ANALYZE_DATE ------------------------------ ----------------- ------------ BONUS TABLE 06-DEC-2000 DEPT TABLE 06-DEC-2000 DUMMY TABLE EMP TABLE 06-DEC-2000 EMP_2 TABLE IX_DEPTNO INDEX 06-DEC-2000 IX_DNAME INDEX 06-DEC-2000 IX_ENAME INDEX 06-DEC-2000 SALGRADE TABLE 06-DEC-2000 ? How can I avoid a self-join? One of the common DBA scripts is to look at hit ratios using the V$SYSSTAT view. Is more efficient with small data sets (less than 1 million records). SELECT (1 - SUM(DECODE(name, 'physical reads', value,0)) / (SUM(DECODE(name, 'db block gets', value,0)) + (SUM(DECODE(name, 'consistent gets', value,0))))) * 100 "Read Hit Ratio" FROM v$sysstat; Read Hit Ratio -------------- 99.780491 SELECT (1 - a.value/(b.value + c.value)) * 100 "Read Hit Ratio" FROM v$sysstat a, v$sysstat b, v$sysstat c WHERE a.name = 'physical reads' AND b.name = 'db block gets' AND c.name = 'consistent gets'; Read Hit Ratio -------------- 99.7805272 ? How can I create an interactive menu in SQL*Plus? sql_menu.sql set verify off pages 0 feed off accept table_choice prompt 'Enter Table (EMP/DEPT/SALGRADE/EXIT): ' set termout off spool run_script.sql select decode(UPPER('&table_choice'), 'EMP', '@select_emp', 'DEPT', '@select_dept', 'SALGRADE', '@select_salgrade', 'EXIT', '@exit_sqlplus', www.rmoug.org RMOUG Training Days 2002
  • 11. SQL Scripting Sorcery Fink '@select_error') script_choice from dual; spool off @run_script.sql select_dept.sql set termout on select * from dept; @sql_menu select_emp.sql set termout on select * from emp; @sql_menu select_error.sql select 'Invalid Menu Choice' from dual; @sql_menu select_salgrade.sql set termout on select * from salgrade; @sql_menu exit_sqlplus.sql exit www.rmoug.org RMOUG Training Days 2002