2. Overview
• Basic Concepts
• Inline Query
• DECODE
• CASE
• Stupid SQL Tricks
• Special Surprise
3. Row Source
• Access Path
• Result Set
• ROWNUM pseudocolumn
– Order in result set, before explicit sorting
– After predicate is applied
• ‘WHERE’ clause
4. Row Source
• All data is retrieved from a row source
• May be a base object or the result set from a
previous operation
• Oracle will use no more than 2 row sources
for any single operation
• Result set may be row source for other
operations
5. Access Paths
• What is an access path
• How access paths affect result sets
• Using different access paths
7. How Access Paths Affect Result
Sets
• Table
– Physical Order in datafile(s)
– ROWID order
• Index
– ‘Sorted’ order
– Even if table is accessed
• Other
– Order of result set
9. Result Sets
• The output of an operation
• Records are processed as a group
10. Sorting the Result Set
• Explicit Sorting
– ORDER BY
• Implicit Sorting
– DISTINCT
– GROUP BY
– UNION/INTERSECT/MINUS
11. Inline Query
• What is an Inline Query?
• Hierarchical Query Joins
• Top N Queries
• Sampling
12. What is an Inline Query?
• FROM clause contains a ‘SELECT’
statement instead of a table or view
– New in 7.2
• Cannot contain ORDER BY
– Now Allowed in 8i
• Predicate cannot be dependent upon
external condition
– Cannot be a correlated subquery
14. Hierarchical Query Joins
• Basic Hierarchical Query uses one and only
one table
• Join cannot occur in Hierarchical Query
• Push query to inline query…it becomes just
another result set!
15. Base 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
16. Joining a Hierarchical Query
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
*
ERROR at line 2:
ORA-01437: cannot have join with CONNECT BY
17. Joining A Hierarchical Query
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;
18. Joining A Hierarchical Query
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
19. Top N Queries
• What are the Top N values?
• Rownum and result set are key
• New features introduced in Oracle8i
– ORDER BY in inline query
20. Top N Values
• Limited number of records based upon a
value or set of values
– Top 2 salaries in the company
– Top 2 salaries in each department
– What happens in the case of a tie?
• Implemented in other platforms
21. Top 2 Salaries
Oracle 8.0.6 Oracle 8.1.6
select ename, select *
empno, from (select ename,
sal, empno,
deptno, sal,
hiredate deptno,
from (select distinct 0-sal, hiredate
ename, from emp
empno, order by sal desc)
sal, where rownum <= 2;
deptno,
hiredate
from emp)
where rownum <= 2;
22. Top 2 Salaries
ENAME EMPNO SAL DEPTNO HIREDATE
---------- ---------- ---------- ---------- -----------
KING 7839 5000 10 17-NOV-1981
SCOTT 7788 3000 20 09-DEC-1982
• Only 2 records returned
• ….but there are 2 employees with $3000
salary
• Should 3 records be returned?
23. Top 2 Salaries, part 2
Oracle 8.0.6 Oracle 8.1.6
select ename, select ename,
empno, empno,
sal, sal,
deptno, deptno,
hiredate hiredate
from emp from emp
where sal >= where sal >=
(select min(i_sal) (select min(i_sal)
from (select from
distinct 0-sal r_sal, (select sal i_sal
sal i_sal from emp
from emp) order by i_sal desc)
where rownum <= 2); where rownum <= 2);
24. Top 2 Salaries
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
• 3 records are returned
• ….for the top 2 salaries
• Is this the correct result?
25. Top 2 Salaries per Department
• Extraction of data is dependent upon data
within query
• Move inline query into correlated subquery
26. Top 2 Salaries per Department
select m.ename, select m.ename,
m.sal, m.sal,
m.deptno m.deptno
from emp m from emp m
where m.sal >= where m.sal >=
(select distinct o.sal (select distinct o.sal
from emp o from emp o
where (o.sal,2) in where (o.sal,2) in
(select i.sal, (select i.sal,
rownum rownum
from (select from (select
distinct 0-i2.sal r_sal, i2.deptno,
i2.deptno, i2.sal
i2.sal, from emp i2
i2.rowid order by i2.sal desc,
from emp i2) i i2.deptno) i
where i.deptno = m.deptno)) where i.deptno = m.deptno))
order by deptno, sal desc; order by deptno, sal desc;
27. Top 2 Salaries per Department
ENAME SAL DEPTNO
---------- ---------- ----------
KING 5000 10
CLARK 2450 10
SCOTT 3000 20
FORD 3000 20
BLAKE 2850 30
ALLEN 1600 30
TURNER 1600 30
28. RANK
• Added in Oracle8i
– Added for analytical functions
• Will include ties, but will skip ranks
• Can this be used for Top N Queries?
29. RANK
SELECT deptno, ename, sal, comm,
RANK() OVER (PARTITION BY deptno
ORDER BY sal DESC, comm) as rk
FROM emp
where RANK() OVER (PARTITION BY deptno
ORDER BY sal DESC, comm) <= 2;
where RANK() OVER (PARTITION BY deptno ORDER BY sal
DESC, comm) <= 2
*
ERROR at line 4:
ORA-30483: window functions are not allowed here
30. RANK
select *
from (SELECT deptno, ename, sal, comm,
RANK() OVER (PARTITION BY deptno
ORDER BY sal DESC, comm) as rk
FROM emp )
where rk <=2;
DEPTNO ENAME SAL COMM RK
---------- ---------- ---------- ---------- ----------
10 KING 5000 1
10 CLARK 2450 2
20 JONES 3000 1
20 FORD 3000 1
30 BLAKE 3000 1
30 ALLEN 1600 300 2
31. DECODE
• Deconstructing DECODE
• Cross-tab reporting
• Conditional DML
• Simple menu
• Duplicating a CASE function
32. Deconstructing Decode
• DECODE is a crude decision-making
construct
• Implement
– IF-THEN-ELSE
– CASE
• Comparison must be equality
– One or more values
33. Deconstructing Decode
SELECT ename, ENAME DEPTNO DECODE(DEPTNO
deptno, --------- ---------- -------------
DECODE(deptno, TURNER 30 SALES
10, ‘ACCOUNTING’, ALLEN 30 SALES
20, ‘RESEARCH’,
WARD 30 SALES
30, ‘SALES’,
ADAMS 20 RESEARCH
‘NOT INDICATED’)
JONES 20 RESEARCH
FROM emp;
MARTIN 30 SALES
IF DEPTNO = 10 CLARK 10 ACCOUNTING
THEN ‘ACCOUNTING’
ELSE IF DEPTNO = 20
THEN ‘RESEARCH’
ELSE IF DEPTNO = 30
THEN ‘SALES’
ELSE ‘NOT INDICATED’
34. Cross-Tab Reports
• Spreadsheet type report
– Down and Across
– Static
– Summary
• Each column is processed multiple times
– ‘Ignore’ values not required
• GROUP BY important
36. Conditional DML
Dynamic Select
• Select different column based on dynamic
values
• Same table, different columns
• Different table, different columns
– Traditional – UNION
– Cool – OUTER JOIN and DECODE
37. Conditional DML
Dynamic Select
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
------------------------------ ----------------- ------------
DEPT TABLE 06-DEC-2000
EMP TABLE 06-DEC-2000
IX_DEPTNO INDEX 06-DEC-2000
IX_DNAME INDEX 06-DEC-2000
IX_ENAME INDEX 06-DEC-2000
38. Conditional DML
Dynamic Update
• Updating records based on dynamic value
• Multiple update statements?
• Single DECODE statement
– Use GREATEST and LEAST to simulate
Range Check
40. Conditional DML
Dynamic Update
• Use CASE instead
– But we haven’t learned that yet!
• Available Oracle 8.1.6
• Much Cleaner Code
41. Conditional DML
Dynamic Update
select ename,
hiredate,
CASE when TRUNC(hiredate)
between ’01-APR-1981’
and ’31-MAR-1982’
then 0.1
else
then 0.05
END pay_raise
from emp;
42. CASE
• Finally, a real CASE function!
• New in Oracle8i
– ANSI-standard
• Limit of 128 comparisons
• Nonequality comparisons
43. CASE
DECODE Approach CASE Approach
SELECT ename, SELECT ename,
deptno, deptno,
DECODE(deptno, CASE WHEN deptno = 10
10, ‘ACCOUNTING’, THEN 'ACCOUNTING'
20, ‘RESEARCH’, WHEN deptno = 20
30, ‘SALES’, THEN 'RESEARCH'
‘NOT INDICATED’) WHEN deptno = 30
FROM emp; THEN 'SALES'
ELSE 'NOT INDICATED'
END
FROM emp;
44. CASE
• Comparisons
– Any Valid Oracle Expression
– Do not have to be equality
46. Case Approach
select ename,
sal,
CASE when sal between 0 and 999
then 'Low Pay'
when sal between 1000 and 2500
then 'Medium Pay'
when sal between 2500 and 4000
then 'High Pay'
when sal > 4001
then 'DBA Pay'
END compensation
from emp
47. CASE
• May be nested
select job,
comm,
sal,
CASE when comm is not null
then (CASE when comm > 100
then sal + comm
when comm <= 100
then sal + 100
END)
when job = 'CLERK'
then sal
else sal * 1.1
END compensation
from emp
52. Using SELECT as an Expression
• What values can be returned from a
column?
– Data
– Literal
– Result of an Expression
– Result of a SELECT statement
54. Basic Construct
select (select count(*)
from emp) emp_count,
(select count(*)
from dept) dept_count
from dual;
EMP_COUNT DEPT_COUNT
---------- -----------
8 4
55. Basic Construct
• Subquery can return one and only one row
• Subquery can return one and only one
column
– Multiple columns may be concatenated
• The main query will return as many rows as
there are in the main table
56. Basic Construct
• Can
– restrict using a WHERE
– use a GROUP BY
– restrict using a HAVING
– join multiple tables
– be a correlated subquery
57. Selecting Multiple Values
select (select dname||loc
from dept
where deptno = 10)dname_loc
from dual;
DNAME_LOC
---------------------------
ACCOUNTINGNEW YORK
58. Joining Tables
select (select count(e1.empno)
from emp e1,
dept d1
where d1.loc = 'NEW YORK'
and d1.deptno = e1.deptno)
ny_emps
from dual
59. Using as an expression
select s.segment_name,
s.segment_type,
decode(s.segment_type,
'TABLE', (select t.last_analyzed
from user_tables t
where t.table_name = s.segment_name),
'INDEX', (select i.last_analyzed
from user_indexes i
where i.index_name = s.segment_name),
NULL) date_analyzed
from user_segments s
60. Using as an expression
select s.segment_name,
s.segment_type,
case when s.segment_type = 'TABLE'
then (select t.last_analyzed
from user_tables t
where t.table_name = s.segment_name)
when s.segment_type = 'INDEX'
then (select i.last_analyzed
from user_indexes i
where i.index_name = s.segment_name)
else NULL
end date_analyzed
from user_segments s