You are on page 1of 44

5/11/2017

Connor McDonald

...through SQL
1

3 4

https://www.youtube.com/c/ConnorMcDonaldOracle

@connor_mc_d connormcdonald.wordpress.com

https://www.facebook.com/connor.mcdonald42
5 6

1
5/11/2017

why talk about SQL

# 1
asktom.oracle.com
7 8

after all ...

9 10

NoSQL

non relational
11 12

2
5/11/2017

why talk about SQL coding for functionality ...

# 2
13 14

... not for data processing a little bit of SQL ...

15 16

... can make your apps awesome key point

17 18

3
5/11/2017

this session is about ... smart not smart-ass

19 20

SQL> with x( s, ind ) as


2 ( select sud, instr( sud, '.' )
3 from ( select replace(replace(
4 replace(replace(:board,'-'),'|'),' '),chr(10)) sud
5 from dual )
6 union all
7 select substr(s,1,ind-1)||z||substr(s,ind+1)
8 , instr(s,'.',ind+1)
9 from x
SQL can do anything ... 10
11
, ( select to_char( rownum ) z
from dual connect by rownum <= 9 ) z
12 where ind > 0
13 and not exists (
14 select null
15 from ( select rownum lp from dual
16 connect by rownum <= 9 )
17 where z = substr(s,trunc((ind-1)/9)*9+lp,1)

21 22

18 or z = substr(s,mod(ind-1,9)-8+lp*9,1) SQL> variable board varchar2(1000)


19 or z = substr(s,mod(trunc((ind-1)/3),3)*3 SQL> begin :board :=
20 +trunc((ind-1)/27)*27+lp
21 +trunc((lp-1)/3)*6,1) 2 '53.|.7.|... 5 3 7
22 ) 3 6..|195|...
23 ), 4 .98|...|.6. 6 1 9 5
24 result as ( 5 ----------- 9 8 6
25 select s
26 from x 6 8..|.6.|..3 8 6 3
27 where ind = 0 ) 7 4..|8.3|..1
28 select 8 7..|.2.|..6 4 8 3 1
29 regexp_replace(substr(s,(idx-1)*9+1,9), 9 ----------- 7 2 6
30 '(...)(...)(...)',
31 '\1|\2|\3')|| 10 .6.|...|28. 6 2 8
32 case when mod(idx,3)=0 then chr(10)||rpad('-',11,'-') end soln 11 ...|419|..5
33 from result, 12 ...|.8.|.79 4 1 9 5
34 ( select level idx 13 '; 8 7 9
35 from dual
36 connect by level <= 9 ) 14 end;
Ack: Anton Scheffer,
23 https://technology.amis.nl 24

4
5/11/2017

SOLUTION
----------- 100%
534|678|912
672|195|348
198|342|567
-----------
859|761|423
426|853|791
713|924|856
-----------
961|537|284 % of developers that
287|419|635 will need to solve Sudoku
345|286|179 as part of their job
-----------
sud.sql
25 26

100%

% of developers that need


to get real stuff done
real stuff

27 28

1 some controversy...

29 30

5
5/11/2017

DBA

31

DBA stuff matters

oc00.sql
2
33 34

insert into my_transactions1


select ...
from tab200k;

synthesizing rows "What if I don't have a TAB200K ?"

35 36

6
5/11/2017

only just enough


the problem ... databases store data

37 38

free free free free free Connor free


SQL> select * from meetings;
free free free free free Gerald free
SCHEDULED OWNER
--------- -------------------------
06-MAY-17 Connor free free Maria free free free free
13-MAY-17 Gerald
17-MAY-17 Maria
free free free free free free free

free free free


39
40

SQL> select * from meetings;

SCHEDULED OWNER
--------- -----------------------
01-MAY-17
02-MAY-17
03-MAY-17
04-MAY-17
05-MAY-17

what we need
06-MAY-17 Connor
07-MAY-17
08-MAY-17
...
16-MAY-17
17-MAY-17 Maria
18-MAY-17
19-MAY-17
...
30-MAY-17
41
31-MAY-17
42

7
5/11/2017

31 ? 365 ? any number of rows

43 44

SQL> select level seq


2 from DUAL
3 connect by level <= 10;

SEQ
----------
1
2
3
easy 4
5
6
7
8
9
10

10 rows selected. Oracle


45 46

select * SQL> select level seq


from generate_series(1, 10)
Postgres 2 from DUAL
3 connect by level <= 10;

-----------------------------------------------------
with gen(seq) as ( | Id | Operation | Name | Rows |
select 1 seq -----------------------------------------------------
union all | 0 | SELECT STATEMENT | | 1 |
select seq+1 from gen
where seq < 10
SQL Server |* 1 | CONNECT BY WITHOUT FILTERING| | |
| 2 | FAST DUAL | | 1 |
) -----------------------------------------------------
select * from gen;
Statistics
------------------------------
1 recursive calls
select seq 0 db block gets
from my_n_row_table MySQL 0 consistent gets
0 physical reads
47 48

8
5/11/2017

"free" if I have integers ...

49 50

SQL> select date '2017-04-30'+level dte


2 from dual
3 connect by level <= 31;

DTE
---------
01-MAY-17
02-MAY-17

... I have dates 03-MAY-17


04-MAY-17
...
...
29-MAY-17
30-MAY-17
31-MAY-17

31 rows selected.

51 52

SQL> select
2 dte scheduled, SCHEDULED OWNER
3 m.owner --------- -----------------------
4 from 01-MAY-17
5 02-MAY-17
( select date '2017-04-30'+level dte
6 from dual 03-MAY-17
7 04-MAY-17
connect by level <= 31 ) d
8 left outer join 05-MAY-17
9 meetings m 06-MAY-17 Connor

outer join 10 07-MAY-17


on d.dte = m.scheduled
11 order by 1; 08-MAY-17
...
16-MAY-17
17-MAY-17 Maria
18-MAY-17
19-MAY-17
...
30-MAY-17
31-MAY-17
54
53

9
5/11/2017

3 subquery factoring

55 56

common table expressions WITH clause

57 58

SQL> WITH last_hire AS


2 ( select deptno, max(hiredate)
3 from emp
4 group by deptno
5 )
6 select * from last_hire;
"gee....... more code, same result"
DEPTNO MAX(HIRED
---------- ---------
30 03-DEC-81
20 12-JAN-83
10 23-JAN-82

59 60

10
5/11/2017

WITH last_hire AS
(
select deptno, max(hiredate)
from emp
group by deptno why is it cool ?
)
select * from last_hire;

61 62

good solution metaphor relational is a rigorous model ...

63 64

relational is dominant ... relational ... can sortta suck :-)

65 66

11
5/11/2017

not our fault Codd & Date

67 68

huh ?
"data is represented as mathematical n-ary
relations, an n-ary relation being a subset of the
Cartesian product of n domains."

69 70

"First, get the total salary paid by each department,


procedural, linear world then get the average of these totals,
then list those departments above that average"

SQL ?
71 72

12
5/11/2017

"First, get the total salary paid by department... "...then get the average of these totals...

SQL> WITH dept_salaries AS ( 6 avg_sal AS ( SELECT AVG(dept_sal) avsal


2 SELECT dname, SUM(sal) dept_sal 7 FROM dept_salaries)
3 FROM emp e, dept d
4 WHERE e.deptno = d.deptno
5 GROUP BY dname),

73 74

SQL> WITH dept_salaries AS (


2 SELECT dname, SUM(sal) dept_sal
3 FROM emp e, dept d
4 WHERE e.deptno = d.deptno
5 GROUP BY dname),
"...then list those departments above average." 6 avg_sal AS ( SELECT AVG(dept_sal) avsal
7 FROM dept_salaries)
8 SELECT * FROM dept_salaries d, avg_sal a 8 SELECT * FROM dept_salaries d, avg_sal a
9 WHERE d.dept_sal > a.avsal 9 WHERE d.dept_sal > a.avsal
10 ORDER BY d.dname; 10 ORDER BY d.dname;

75 76

programmer's approach.... ... relational solution

77 78

13
5/11/2017

the "finishing touches" recall : 31 days of meetings

79 80

SQL> with raw_data as (


2 select
3 dte scheduled, [{"01-MAY-17":null},
4 m.owner {"02-MAY-17":null},
5 from {"03-MAY-17":null},
6 ( select date '2017-04-30'+level dte
{"04-MAY-17":null},
7 from dual {"05-MAY-17":null},
8 connect by level{"06-MAY-17":"Connor"},
<= 31 ) d
9 left outer join {"07-MAY-17":null},
10
11
meetings m {"08-MAY-17":null},
on d.dte = m.scheduled
{"09-MAY-17":null},
things to note...
12 ) {"10-MAY-17":null},
13 select ...
14 json_arrayagg( ...
15 json_object(key ...
to_char(scheduled) value owner )
16 order by scheduled ) as meetings
{"30-MAY-17":null},
17 from raw_data ; {"31-MAY-17":null}
]
81
82

execution plan two possibilities

83 84

14
5/11/2017

SQL> WITH dept_salaries AS (


2 SELECT dname, SUM(sal) dept_sal
3 FROM emp e, dept d
4 WHERE e.deptno = d.deptno
5 GROUP BY dname),

temporary storage 6 avg_sal AS ( SELECT AVG(dept_sal) avsal


7 FROM dept_salaries)

8 SELECT * FROM dept_salaries d, avg_sal a


9 WHERE d.dept_sal > a.avsal join
10 ORDER BY d.dname;

85 86

---------------------------------------------------------------------- SQL> alter session set sql_trace = true;


| Id | Operation | Name |
---------------------------------------------------------------------- Session altered.
| 0 | SELECT STATEMENT | |
| 1 | TEMP TABLE TRANSFORMATION | | SQL> WITH dept_salaries AS (
| 2 | LOAD AS SELECT | | 2 SELECT dname, SUM(sal) dept_sal
| 3 | HASH GROUP BY | | 3 FROM emp e, dept d
| 4 | MERGE JOIN | | 4 WHERE e.deptno = d.deptno
5 GROUP BY dname),
| 5 | TABLE ACCESS BY INDEX ROWID| DEPT | 6 avg_sal AS ( SELECT AVG(dept_sal) asal
| 6 | INDEX FULL SCAN | PK_DEPT | 7 FROM dept_salaries)
| 7 | SORT JOIN | | 8 SELECT * FROM dept_salaries d, avg_sal a
| 8 | TABLE ACCESS FULL | EMP | 9 WHERE d.dept_sal > a.asal
| 9 | SORT ORDER BY | | 10 ORDER BY d.dname;
| 10 | NESTED LOOPS | |
| 11 | VIEW | | DNAME DEPT_SAL ASAL
| 12 | SORT AGGREGATE | | -------------- ---------- ----------
| 13 | VIEW | | RESEARCH 21750 19350
| 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6624_AF422F5 |
| 15 | VIEW | | SQL> alter session set sql_trace = false;
| 16 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6624_AF422F5 |
---------------------------------------------------------------------- Session altered.

87 88

PARSING IN CURSOR #12 len=179 dep=1 uid=0 oct=1...

CREATE GLOBAL TEMPORARY


TABLE "SYS"."SYS_TEMP_0FD9D6625_AF422F5"
("C0" VARCHAR2(14),"C1" NUMBER )
IN_MEMORY_METADATA CURSOR_SPECIFIC_SEGMENT
no temporary storage
STORAGE (OBJNO 4254950949 ) NOPARALLEL

END OF STMT

89 90

15
5/11/2017

------------------------------------------------------
| Id | Operation | Name |
------------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | SORT ORDER BY | |
| 2 | NESTED LOOPS | |

4
| 3 | VIEW | |
| 4 | SORT AGGREGATE | |
| 5 | VIEW | |
| 6 | HASH GROUP BY | |
| 7 | MERGE JOIN | |
| 8 | TABLE ACCESS BY INDEX ROWID| DEPT |
| 9 | INDEX FULL SCAN | PK_DEPT |
| 10 | SORT JOIN | |
| 11 | TABLE ACCESS FULL | EMP |
| 12 | VIEW | |
| 13 | SORT GROUP BY | |
| 14 | MERGE JOIN | |
| 15 | TABLE ACCESS BY INDEX ROWID | DEPT |
| 16 | INDEX FULL SCAN | PK_DEPT |
| 17 | SORT JOIN | |
| 18 | TABLE ACCESS FULL | EMP |
------------------------------------------------------
91 92

SQL> select *
2 from timeslots;

HR
--
8
9
10 SQL> select *
11 2 from bookings;
12
partitioned outer join 13
14
HR
-------
ROOM
----------
WHO
-------
15 8 Room2 PETE
16 9 Room1 JOHN
11 Room1 MIKE
14 Room2 JILL
15 Room2 JANE
16 Room1 SAM

93 94

SQL> SELECT hrs.hr, t1.room, t1.who


2 from timeslots hrs
3 left outer join bookings t1
4 on hrs.hr = t1.hr

HR ROOM WHO
------- ---------- ----------

bookings by hour
8 Room2 PETE
9 Room1 JOHN
10
11 Room1 MIKE
12
13
14 Room2 JILL
conventional outer join 15
16
Room2
Room1
JANE
SAM

95 96 96

16
5/11/2017

HR ROOM WHO
------- ---------- ----------
8
9 Room1 JOHN
10
11 Room1 MIKE
12
13
14 HR ROOM WHO

bookings by hour per room


15 ------- ---------- ----------
16 Room1 SAM 8 Room2 PETE
9
10
11
12
13
14 Room2 JILL
15 Room2 JANE
16

97 98

SQL> select *
2 from timeslots; x "Room 1"
HR
--
8 x "Room 2"
partitioned outer join
9
10
11
12
...
13
14
15 x "Room n"
16

99 100

SQL> SELECT hrs.hr, t1.room, t1.who


2 FROM bookings t1
3 PARTITION BY (t1.room)
4 RIGHT OUTER JOIN hrs ON (hrs.hr = t1.hr);

HR ROOM WHO
--------- ---------- ----------

5
8 Room1
9 Room1 JOHN
10 Room1
11 Room1 MIKE
12 Room1
13 Room1
14 Room1
15 Room1
16 Room1 SAM
8 Room2 PETE
9 Room2
10 Room2
11 Room2
12 Room2
13 Room2
14 Room2 JILL
15 Room2 JANE
16 Room2
101 101 102

17
5/11/2017

pagination "employees by hiredate, recent first"

103 104

SQL> select empno, ename, hiredate SQL> select empno, ename, hiredate
2 from emp 2 from emp
3 limit 5 3 where rownum <= 5
4 order by hiredate desc; 4 order by hiredate desc;

EMPNO ENAME HIREDATE EMPNO ENAME HIREDATE


---------- ---------- ------------------- ---------- ---------- -------------------
7654 MARTIN 28/09/1981 00:00:00 7654 MARTIN 28/09/1981 00:00:00
7566 JONES 02/04/1981 00:00:00 7566 JONES 02/04/1981 00:00:00
7521 WARD 22/02/1981 00:00:00 7521 WARD 22/02/1981 00:00:00
7499 ALLEN 20/02/1981 00:00:00 7499 ALLEN 20/02/1981 00:00:00
7369 SMITH 17/12/1980 00:00:00 7369 SMITH 17/12/1980 00:00:00

105 106

SQL> select *
2 from (
3 select empno, ename, hiredate
4 from emp
5 order by hiredate desc
6 )
7 where rownum <= 5;
inline view EMPNO ENAME HIREDATE
---------- ---------- ---------
7876 ADAMS 12-JAN-83
7788 SCOTT 09-DEC-82
7934 MILLER 23-JAN-82
7900 JAMES 03-DEC-81
7902 FORD 03-DEC-81
107 108

18
5/11/2017

SQL> select * SQL> select empno, ename, hiredate


2 from ( 2 from emp
3 select 3 order by hiredate desc
4 empno, ename, hiredate, 4 fetch first 5 rows only;
5 row_number() over ( order by hiredate desc) rn
6 from emp EMPNO ENAME HIREDATE
7 ) ---------- ---------- ---------
8 where rn <= 5; 7876 ADAMS 12-JAN-83
7788 SCOTT 09-DEC-82
7934 MILLER 23-JAN-82
7900 JAMES 03-DEC-81
7902 FORD 03-DEC-81

109 110

public static void Paging(Connection conn ) throws Exception


{
PreparedStatement sql_stmt =
conn.prepareStatement(
"select empno, ename, hiredate
from emp
order by hiredate desc");

ResultSet rset = sql_stmt.executeQuery();


int i = 0;
while( rset.next() )
"TL;DR ... my app can do it" {
...
i = i + 1;
if (i > 5) {
break;
}
}
rset.close();
}

111 112

114

19
5/11/2017

demo let the database know

oc01.sql
115 116

SQL> select * SQL> select empno, ename, hiredate


2 from ( 2 from emp
3 select empno, ename, hiredate 3 order by hiredate desc
4 from emp 4 fetch first 5 rows only;
5 order by hiredate desc
------------------------------------------------ -------------------------------------------------
6 )
| Id | Operation | Name | Rows | | Id | Operation | Name | Rows |
7 where rownum <= 5;
------------------------------------------------ -------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | | 0 | SELECT STATEMENT | | 14 |
|* 1 | COUNT STOPKEY | | | |* 1 | VIEW | | 14 |
| 2 | VIEW | | 14 | |* 2 | WINDOW SORT PUSHED RANK| | 14 |
|* 3 | SORT ORDER BY STOPKEY| | 14 | | 3 | TABLE ACCESS FULL | EMP | 14 |
| 4 | TABLE ACCESS FULL | EMP | 14 | -------------------------------------------------
------------------------------------------------

117 118

you get other benefits "but what about the next page ?"

oc02.sql
119 120

20
5/11/2017

there
is
no new query
next
page
121 122

SQL> select empno, ename, hiredate


2 from emp
3 order by hiredate desc
4 offset 5 rows fetch first 5 rows only;

EMPNO ENAME HIREDATE


---------- ---------- --------- "an expensive query per page ?!?!
7839 KING 17-NOV-81
7654 MARTIN 28-SEP-81
7844 TURNER 08-SEP-81
7782 CLARK 09-JUN-81
7698 BLAKE 01-MAY-81

123 124

SQL> with first_200 as


2 /*+ result_cache
( select f.*, rownum r no_merge */ rownum r, f.*
3 from
4 ( select *
5 from t
6 order by owner, object_name desc
7 ) f
consider result caching 8
9 )
where rownum <= 200

10 select *
11 from first_200
12 where rr between
<= 10 11 and 20

oc03.sql
125 126

21
5/11/2017

6 query block naming

127 128

C#, C++ OSB Tuxedo


for (int i = 0; i < WinningCombinations.Count; ++i)
{
if (WinningCombinations[i].Investment > 0 &&
WinningCombinations[i].PrimaryDividend > MinDividendDeadHeat)
{
maxDivisor =

Weblogic
Math.Max(maxDivisor, WinningCombinations[i].Divisor);
}
}

for (int i = 0; i < WinningCombinations.Count; ++i)


{
if (WinningCombinations[i].Investment > 0 &&
WinningCombinations[i].PrimaryDividend > MinDividendDeadHeat)
PL/SQL {
WinningCombinations[i].Divisor =
maxDivisor / WinningCombinations[i].Divisor;
sumNewDivisors += WinningCombinations[i].Divisor;
129 130
}

no comments

131 132

22
5/11/2017

"C# is self-documenting" "uh huh"

133 134

SQL can be complex SQL should "self document"

135 136

select emp.*
from emp,
( select trunc(hiredate,'YYYY'), max(empno) empno
from emp
query blocks = where empno > 0
group by trunc(hiredate,'YYYY') ) x,
( select deptno, avg(sal)
from emp
self- documenting SQL group by deptno ) y
where x.empno = emp.empno
and y.deptno = emp.deptno

137 138

23
5/11/2017

Id | Operation | Name | select emp.*


from emp,
----------------------------------------------| ( select trunc(hiredate,'YYYY'), max(empno) empno
0 | SELECT STATEMENT | | from emp
where empno > 0
1 | HASH JOIN | |

?
group by trunc(hiredate,'YYYY') ) x,
( select deptno, avg(sal)
2 | TABLE ACCESS BY INDEX ROWID | EMP | from emp
group by deptno ) y
3 | NESTED LOOPS | | where x.empno = emp.empno
and y.deptno = emp.deptno
4 | VIEW | |
5 | SORT GROUP BY | |
6 | TABLE ACCESS BY INDEX ROWID| EMP | Id | Operation | Name |
----------------------------------------------|
7 | INDEX FULL SCAN | E2 | 0 | SELECT STATEMENT | |
1 | HASH JOIN | |
8 | INDEX RANGE SCAN | E1 | 2 | TABLE ACCESS BY INDEX ROWID | EMP |
9 | VIEW | | 3 |
4 |
NESTED LOOPS
VIEW
|
|
|
|
10 | SORT GROUP BY | | 5 |
6 |
SORT GROUP BY |
TABLE ACCESS BY INDEX ROWID| EMP |
|

11 | TABLE ACCESS BY INDEX ROWID | EMP | 7 | INDEX FULL SCAN | E2 |


8 | INDEX RANGE SCAN | E1 |
12 | INDEX RANGE SCAN | E2 | 9 | VIEW | |
10 | SORT GROUP BY | |
11 | TABLE ACCESS BY INDEX ROWID | EMP |
12 | INDEX RANGE SCAN | E2 |

139 140

Id | Operation | Name | Query Block


select emp.* ----------------------------------------------|--------------
from emp, 0 | SELECT STATEMENT | |
( select /*+ QB_NAME(YR_HIRE) */ 1 | HASH JOIN | | SEL$1
2 | TABLE ACCESS BY INDEX ROWID | EMP | SEL$1
trunc(hiredate,'YYYY'), max(empno) empno
3 | NESTED LOOPS | |
from emp 4 | VIEW | |
AV_SAL
where empno > 0 5 | SORT GROUP BY | | AV_SAL
group by trunc(hiredate,'YYYY') ) x, 6 | TABLE ACCESS BY INDEX ROWID| EMP | AV_SAL
( select /*+ QB_NAME(AV_SAL) */ 7 | INDEX FULL SCAN | E2 | AV_SAL
SEL$1
deptno, avg(sal) 8 | INDEX RANGE SCAN | E1 |
YR_HIRE
from emp 9 | VIEW | |
10 | SORT GROUP BY | | YR_HIRE
group by deptno ) y YR_HIRE
where x.empno = emp.empno 11 | TABLE ACCESS BY INDEX ROWID | EMP |
12 | INDEX RANGE SCAN | E2 | YR_HIRE
and y.deptno = emp.deptno

141 142

SQL> alter session set

assist with query trace


2 events = '10053 trace name context forever, level 1';

Session altered.

143 144

24
5/11/2017

************************** *********************************
Query transformations (QT) Number of join permutations tried: 1
************************** *********************************
CBQT: Validity checks passed for 7jpzpr2475cqw. GROUP BY adjustment factor: 1.000000
GROUP BY cardinality: 3.000000, TABLE cardinality: 14.000000
CSE: Sub-expression elimination in query block SEL$1 (#0)
SORT ressource Sort statistics
Sort width: 598 Area size: 1048576 Max Area: 104857600
************************* Degree: 1
Common Subexpression elimination (CSE) Blocks to Sort: 1 Row size: 18 Total Rows: 14
************************* Initial runs: 1 Merge passes: 0 IO Cost / pass: 0
CSE: CSE not performed on query block YR_HIRE (#0). Total IO sort cost: 0 Total CPU sort cost: 14312402
CSE: Sub-expression elimination in query block AV_SAL (#0) Total Temp space used: 0
CSE: CSE not performed on query block AV_SAL (#0). Trying or-Expansion on query block AV_SAL (#3)
Transfer Optimizer annotations for query block AV_SAL (#3)
CSE: CSE not performed on query block SEL$1 (#0).
GROUP BY adjustment factor: 1.000000
Final cost for query block AV_SAL (#3) - All Rows Plan:
*************************** Best join order: 1
Order-by elimination (OBYE) Cost: 4.0029 Degree: 1 Card: 14.0000 Bytes: 98
*************************** Resc: 4.0029 Resc_io: 3.0000 Resc_cpu: 14352069
OBYE: Considering Order-by Elimination from view SEL$1 (#0) Resp: 4.0029 Resp_io: 3.0000 Resc_cpu: 14352069
145 146

SQL> select
2 /*+ QB_NAME(top)
3 INDEX(@yr_hire emp (empno))
4 FULL(@av_sal emp)
5 */ emp.*
6 from emp,
7 ( select /*+ QB_NAME(YR_HIRE) */
8 trunc(hiredate,'YYYY'), max(empno) empno
assist with hints 9
10
from emp
where empno > 0
11 group by trunc(hiredate,'YYYY') ) x,
12 ( select /*+ QB_NAME(AV_SAL) */
13 deptno, avg(sal)
14 from emp
15 group by deptno ) y
16 where x.empno = emp.empno
17 and y.deptno = emp.deptno
147 148

select
/*+ QB_NAME(top)
INDEX(@yr_hire emp (empno))

footnote
FULL(@av_sal emp)
*/
INDEX(emp emp_ix)

150
149

25
5/11/2017

7 totals / subtotals

151 152

SQL> select empno, ename, sal, deptno from emp


2 order by deptno;

EMPNOSQL>
ENAMEselect deptno,SAL DEPTNO
---------- ----------
2 ---------- ----------
sum(sal)
7782 CLARK
3 from emp 2450 10
"Employee salary list, 7839 KING
4 group by deptno
7934 MILLER
5000
1300
10
10
5 order by deptno;
plus department total, ...
7900 JAMES 950 30
DEPTNO SUM(SAL)
plus grand total" 7698----------
BLAKE
7654 MARTIN
SQL>
2850
----------
1250
30
from 30
10 select 8750
sum(sal) emp;
20 10875
SUM(SAL)
30 9400
----------
29025

153 154

SQL> select empno, ename, sal, deptno from emp


2 order by deptno;

EMPNO ENAME SAL DEPTNO


----------SQL> select deptno,
---------- ---------- ----------
2
7782 CLARK sum(sal)
2450 10
3 from
7839 KING emp 5000 10
4 group by rollup(deptno)
7934 MILLER 1300 10
from 3 to 2 ... 5 order by deptno;
7900 JAMES 950 30
DEPTNO
7698 BLAKE SUM(SAL)
2850 30
7654----------
MARTIN ----------
1250 30
10 8750

rollup
20 10875
30 9400
29025

155 156

26
5/11/2017

EMPNO SAL DEPTNO


---------- ---------- ----------
7782 2450 10 DEPTNO SUM(SAL)
7839 5000 10 ---------- ----------
7934 1300 10 10 8750
7566 2975 20 20 10875
7902 3000 20 30 9400
7876 1100 20 29025
7369 800 20
still messy...

157 158

SQL> select deptno,


2 nvl2(rownum,max(empno),null) empno,
3 nvl2(rownum,max(ename),null) ename,
4 sum(sal)
5 from emp
6 group by rollup(deptno,rownum)
7 order by deptno,empno;

DEPTNO EMPNO ENAME SUM(SAL)

from 2 to 1 ---------- ----------


10 7782
---------- ----------
CLARK 2450
10 7839 KING 5000
10 7934 MILLER 1300
10 8750
20 7369 SMITH 800
20 7566 JONES 2975
...
30 7900 JAMES 950
30 9400
159 160
29025

SQL> select deptno,job,sum(sal) from scott.emp


2 group by CUBE(deptno,job)
3 order by deptno,job;

DEPTNO JOB SUM(SAL)


---------- --------- ----------
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
10 8750
20 ANALYST 6000

the whole lot ! 20


20
20
CLERK
MANAGER
1900
2975
10875
30 CLERK 950
30 MANAGER 2850
30 SALESMAN 5600
30 9400
ANALYST 6000
CLERK 4150
MANAGER 8275
PRESIDENT 5000
SALESMAN 5600
29025

161 162

27
5/11/2017

SQL> select deptno, job, mgr, sum(sal) from emp


2 group by grouping sets (
3 (deptno),
4 (job,mgr), () ) ;

DEPTNO JOB MGR SUM(SAL)


---------- --------- ---------- ----------
CLERK 7902 800
PRESIDENT 5000

totally customisable CLERK


CLERK
7698
7788
950
1100
CLERK 7782 1300
SALESMAN 7698 5600
MANAGER 7839 8275
ANALYST 7566 6000
10 8750
20 10875
30 9400
29025
163 164

8 pattern matching

165 166

AML

anti money laundering

167 168

28
5/11/2017

ACCT TSTAMP TYP AMT LOC


---------- ------------------ --- ---------- ----
54261 25/01/13 17:20:55 Dep 100 Perth
54261 25/01/13 17:56:58 Dep 165 Perth
54261 26/01/13 11:24:14 Dep 30 Subiaco
"Find 10 consecutive deposits in a 54261 26/01/13 11:47:53 Dep 45 Guildford
54261 26/01/13 12:59:38 Dep 100 Claremont
24 hour period, then 54261 26/01/13 13:26:04 Dep 80 Perth
54261 26/01/13 14:41:09 Dep 50 Perth
a withdrawal within three days of 54261 26/01/13 14:53:12 Dep 50 Nedlands
the last deposit, at a different retail outlet" 54261
54261
26/01/13 15:15:05
26/01/13 15:51:17
Dep
Dep
50
50
Leederville
Guildford
54261 26/01/13 16:15:02 Dep 120 Perth
54261 26/01/13 16:36:51 Dep 100 Perth
54261 26/01/13 16:55:09 Dep 100 Perth
54261 26/01/13 18:07:17 Wth -500 Nedlands

169 170

hard... pattern matching

171 172

SQL> select acct, tstamp, wthd_tstamp, txn_type, amt


2 from account_txns
3 MATCH_RECOGNIZE
4 ( partition by acct

9
5 order by tstamp
6 measures
7 dep.tstamp dep_tstamp,
8 wthd.tstamp wthd_tstamp
9 all rows per match
10 pattern ( dep{10,} wthd )
11 define
12 dep as
13 txn_type = 'Dep',
14 wthd as
15 txn_type = 'Wth'
16 and last(dep.tstamp)-first(dep.tstamp) < interval '1' day
17 and wthd.tstamp - last(dep.tstamp) < interval '3' day
18 and wthd.location != last(dep.location)
19 )
173 174

29
5/11/2017

"talking" to your database

... makes it faster

175 176

STORES

CUSTOMERS

example
SALES

177 178

hash outer join ? nested loop ?


---------------------------------------------------
select prod_id, max(amount) | Id | Operation | Name | Rows |
from stores st, ---------------------------------------------------
| 0 | SELECT STATEMENT | | 100 |
customers c, STORES first ? | 1 | HASH GROUP BY | | 100 |
sales s
|* 2 | HASH JOIN | | 990K |
where s.cust_id = c.cust_id(+) | 3 | NESTED LOOPS SEMI | | 5000 |
and c.store_id = st.store_id | 4 | TABLE ACCESS FULL| CUSTOMERS | 5000 |
and s.amount > 10 |* 5 | INDEX UNIQUE SCAN| STORE_IX | 50 |
group by prod_id |* 6 | TABLE ACCESS FULL | SALES | 990K |
---------------------------------------------------

sort merge ?
179 180

30
5/11/2017

add indexes ?

rewrite query ?

can we do better ? share your knowledge with the db

materialized view ?
result cache ?
oc04.sql
181 182

10 transposition

183 184

"I need sales by product


rows to columns, colums to rows for each quarter....now"

185 186

31
5/11/2017

SQL> select product, trunc(txn_date,'Q') mth, sum(quantity) total


3 from SALES
4 group by product,trunc(txn_date,'Q')
5 order by 1,2;

PRODUCT MTH TOTAL


-------------------- --- ----------
CHAINSAW JAN 251473
CHAINSAW APR 254830
CHAINSAW
CHAINSAW
HAMMER
JUL
OCT
JAN
251994
243748
249889
"That’s wrong!...surely you know
HAMMER
HAMMER
HAMMER
APR
JUL
OCT
256566
252992
249104
I wanted it ACROSS the page"
SCREW DRIVER JAN 245988
SCREW DRIVER APR 249219
SCREW DRIVER JUL 252128
SCREW DRIVER OCT 244721
SPADE JAN 242434
SPADE APR 254090
SPADE JUL 259613
...

187 188

pivot clause

189 190

SQL> select *
2 from (select product,
3 trunc(txn_date,'Q') mth,
4 quantity
5 from sales )
6 pivot( sum(quantity) for mth in
7 ( 'JAN',
8 'APR',
9 'JUL',

or ...
10 'OCT') )
11 order by 1
12 /

PRODUCT 'JAN' 'APR' 'JUL' 'OCT'


-------------------- ---------- ---------- ---------- ----------
CHAINSAW 251473 254830 251994 243748
HAMMER 249889 256566 252992 249104
SCREW DRIVER 245988 249219 252128 244721
SPADE 242434 254090 259613 248428
WHEEL BARROW 243899 249327 252296 254137
191 192

32
5/11/2017

SQL> desc SALES_ACROSS

Name Null? Type


"That’s wrong!...surely you know ----------------------------- --------
PRODUCT
-------------
VARCHAR2(20)
I wanted it DOWN the page" Q1 NUMBER
Q2 NUMBER
Q3 NUMBER
Q4 NUMBER

193 194

SQL> select * column values


2 from SALES_ACROSS become "quantity"
3 UNPIVOT
4 ( quantity for quarter in (Q1,Q2,Q3,Q4) )
5 /

PRODUCT QUARTER QUANTITY column names


-------------------- ------- ----------
CHAINSAW Q1 251473 become "quarter"

unpivot clause
CHAINSAW Q2 254830
CHAINSAW Q3 251994
CHAINSAW Q4 243748
HAMMER Q1 249889
HAMMER Q2 256566
HAMMER Q3 252992
HAMMER Q4 249104
SCREW DRIVER Q1 245988
SCREW DRIVER Q2 249219
SCREW DRIVER Q3 252128
SCREW DRIVER Q4 244721
...
195 196

elements must be known in advance


11
197 198

33
5/11/2017

SQL> select
scalar
2 ( select dname
3 from dept
4 where deptno = e.deptno ) dname,
5 decode(empno, 7499,
6 ( select max(sal) from emp ),
scalar queries 7 -1)
8 from anywhere an
9 ( select * from emp
expression could be
10 where sal > 0 ) e
11 where
12 ( select max(hiredate) from emp ) < sysdate
13 /

199 200

SQL> create or replace


2 function CALC_TAX(p int) return number is
3 begin
4 dbms_lock.sleep(1);
5 return p;
6 end;
7 /
"big deal" Function created. 1,2,3,..., 30
SQL> create table T
2 as select rownum r, mod(rownum,5) r1 from dual
3 connect by level <= 30;

Table created. 0,1,2,3,4,0,1,2,3,4, ...


201 202

SQL> select r, calc_tax(r1) tax from T; SQL> select r,


2 ( select calc_tax(r1) from dual) as tax
R TAX 3 from T;
---------- ----------
1 1 R TAX
2 2 ---------- ----------
3 3 1 1
4 4 2 2
... ...
28 3 28 3
29 4 29 4
30 0 30 0
30 rows selected. 30 rows selected.
Elapsed: 00:00:30.01 Elapsed: 00:00:05.01
203 204

34
5/11/2017

subquery caching what about multiple values

... any subquery

205 206

SQL> select
2 empno,
3 ( select dname
4 from dept
SQL> select 5 where deptno = e.deptno ) dname,
2 empno, 6 ( select loc
3 ( select dname, loc, revenue 7 from dept
8 where deptno = e.deptno ) loc,
4 from dept
9 ( select revenue
5 where deptno = e.deptno ) 10 from dept
6 from EMP e; 11 where deptno = e.deptno ) rev
( select dname, loc, revenue 12 from EMP e;
*
EMPNO DNAME LOC REV
ERROR at line 3: ---------- -------------- ------------- ----------
ORA-00913: too many values 7839 ACCOUNTING NEW YORK 10000
7698 SALES CHICAGO 30000
7782 ACCOUNTING NEW YORK 10000
...
207 208

SQL> create type multi_attrib as object (


2 dname varchar2(10),
3 loc varchar2(10),
use a type 4
5 /
rev number );

Type created.

209 210

35
5/11/2017

SQL> select empno,


2 x.attr.dname,
3 x.attr.loc,
4 x.attr.rev
5 from

12
6 (
7 select
8 empno,
9 ( select multi_attrib(dname,loc,revenue)
10 from dept
11 where deptno = e.deptno ) attr
12 from EMP e
13 ) x;

EMPNO ATTR.DNAME ATTR.LOC ATTR.REV


---------- ---------- ---------- ----------
7839 ACCOUNTING NEW YORK 10000
7698 SALES CHICAGO 30000
7782 ACCOUNTING NEW YORK 10000
211 212

first principles

213 214

SQL> select rpad(' ',level)||ename,


2 from emp
3 start with mgr is null
4 connect by prior empno = mgr
SQL> select empno, ename, mgr
ENAME 2 from emp
--------------------------- 3 where mgr is null;
KING
BLAKE EMPNO ENAME MGR
JAMES ---------- -------------------- ----------
ALLEN 7839 KING
WARD
CLARK
MILLER

215 216

36
5/11/2017

SQL> select e3.empno, e3.ename, e3.mgr


2 from emp e3,
SQL> select e2.empno, e2.ename, e2.mgr 3 ( select e2.empno, e2.ename, e2.mgr
4 from emp e2,
2 from emp e2,
5 ( select empno, mgr
3 ( select empno, mgr 6 from emp
4 from emp 7 where mgr is null ) inner
5 where mgr is null ) inner 8 where e2.mgr = inner.empno ) inner
9 where e3.mgr = inner.empno;
6 where e2.mgr = inner.empno;
EMPNO ENAME MGR
EMPNO ENAME MGR ---------- -------------------- ----------
---------- -------------------- ---------- 7902 FORD 7566
7788 SCOTT 7566
7566 JONES 7839
7900 JAMES 7698
7698 BLAKE 7839 7844 TURNER 7698
7782 CLARK 7839 7654 MARTIN 7698
7521 WARD 7698
7934 MILLER 7782
217 218

SQL> select e4.empno, e4.ename, e4.mgr


2 from emp e4,
3 ( select e3.empno, e3.ename, e3.mgr
4 from emp e3,
5 ( select e2.empno, e2.ename, e2.mgr
6 from emp e2,
7 ( select empno, mgr

recall: WITH
8 from emp
9 where mgr is null ) inner
10 where e2.mgr = inner.empno ) inner
11 where e3.mgr = inner.empno ) inner
12 where e4.mgr = inner.empno;

EMPNO ENAME MGR


---------- -------------------- ----------
7876 ADAMS 7788
recursive...
219 220

SQL> with EACH_LEVEL (empno, name, mgr) as


2 ( --
3 -- start with
4 --
5 select empno, ename, mgr
6 from emp
7 where mgr is null
8 --

WITH can be recursive 9


10
-- connect by
--
11 union all
12 select emp.empno, emp.ename, emp.mgr
13 from emp, EACH_LEVEL
14 where emp.mgr = each_level.empno
15 )
16 select *
17 from each_level;
221 222

37
5/11/2017

EMPNO NAME MGR


---------- --------------- ----------
7839 KING
7566 JONES 7839
7698 BLAKE 7839
7782 CLARK 7839
7499 ALLEN 7698
7521 WARD 7698
7654
7788
MARTIN
SCOTT
7698
7566
two part structure
7844 TURNER 7698
7900 JAMES 7698
7902 FORD 7566
7934 MILLER 7782
7369 SMITH 7902
7876 ADAMS 7788
223 224

with RECURSIVE_WITH (c1,c2,c3) as


(
<starting/entry point>

union all

<recursive relationship> you control pseudo- functions


)
select ...
from RECURSIVE_WITH

225 226

SQL> with each_level (empno, name, mgr, rlevel) as SQL> with each_level (empno, name) as
2 ( select empno, ename, mgr, 1 rlevel 2 ( select empno, ename from emp
3 from emp 3 where mgr is null
4 where mgr is null 4 union all
5 union all 5 select e.empno,
6 select emp.empno, emp.ename, emp.mgr, rlevel+1 6 each_level.name||'-'||e.ename
7 from emp, each_level 7 from emp e, each_level
8 where emp.mgr = each_level.empno 8 where e.mgr = each_level.empno
9 ) 9 )
10 select * from each_level; 10 select empno, name from each_level;

EMPNO NAME MGR RLEVEL EMPNO NAME


---------- --------------- ---------- ---------- ---------- --------------------------------
7839 KING 1 7839 KING
7566 JONES 7839 2 7566 KING-JONES
7698 BLAKE 7839 2 7698 KING-BLAKE
7782 CLARK 7839 2 7782 KING-CLARK
7499 ALLEN 7698 3 7499 KING-BLAKE-ALLEN
... 7521 KING-BLAKE-WARD
227 228 ...

38
5/11/2017

SQL> with each_level (empno, name, mgr) as


2 ( select empno, ename, mgr
3 from emp
4 where ename = 'KING'
5 union all
6 select emp.empno, emp.ename, emp.mgr
7 from emp, each_level
8 where emp.mgr = each_level.empno
cycle detection 9 )
10 select *
11 from each_level;

ERROR:
ORA-32044: cycle detected while executing recursive WITH query

229 230

SQL> with each_level (empno, name, mgr) as


2 ( select empno, ename, mgr from emp
3 where ename = 'KING'
4 union all
5 select emp.empno, emp.ename, emp.mgr
6 from emp, each_level
7 where emp.mgr = each_level.empno )
8 CYCLE mgr SET is_cycle TO 'Y' DEFAULT 'N'
9 select * from each_level;

EMPNO NAME MGR IS_CYCLE traversal


---------- ---------- ---------- ----------
7839 KING 7499 N
7566 JONES 7839 N
7521 WARD 7698 N
7839 KING 7499 Y
7876 ADAMS 7788 N
...

231 232

SQL> with each_level (empno, name, hiredate, mgr) as SQL> with each_level (empno, name, hiredate, mgr) as
2 ( select empno, ename, hiredate, mgr from emp 2 ( select empno, ename, hiredate, mgr from emp
3 where ename = 'KING' 3 where ename = 'KING'
4 union all 4 union all
5 select e.empno, 5 select e.empno,
6 each_level.name||'-'||e.ename, e.hiredate, e.mgr 6 each_level.name||'-'||e.ename, e.hiredate, e.mgr
7 from emp e, each_level 7 from emp e, each_level
8 where e.mgr = each_level.empno ) 8 where e.mgr = each_level.empno )
9 SEARCH BREADTH FIRST BY HIREDATE SET IDX 9 SEARCH DEPTH FIRST BY HIREDATE SET IDX
10 select name, hiredate, idx from each_level; 10 select name, hiredate, idx from each_level;

NAME HIREDATE IDX NAME HIREDATE IDX


-------------------------------- --------- ---------- -------------------------------- --------- ----------
KING 17-NOV-81 1 KING 17-NOV-81 1
KING-JONES 02-APR-81 2 KING-JONES 02-APR-81 2
KING-BLAKE 01-MAY-81 3 KING-JONES-FORD 03-DEC-81 3
KING-CLARK 09-JUN-81 4 KING-JONES-FORD-SMITH 17-DEC-80 4
KING-BLAKE-ALLEN 20-FEB-81 5 KING-JONES-SCOTT 19-APR-87 5
KING-BLAKE-WARD 22-FEB-81 6 KING-JONES-SCOTT-ADAMS 23-MAY-87 6
... KING-BLAKE 01-MAY-81 7
KING-JONES-FORD-SMITH 17-DEC-80 13 KING-BLAKE-ALLEN 20-FEB-81 8
KING-JONES-SCOTT-ADAMS 23-MAY-87 14 etc
233 234

39
5/11/2017

iteration / nesting

235 236

SQL> select * from twitter_handles;

SQL> select * from messages; ID TERM HANDLE


---- ------------------------------ ---------------
TXT 1 Connor McDonald @connor_mc_d
---------------------------------------------------------- 2 Connor @connor_mc_d
I caught up with Connor and Maria Colgan today. They have 3 Maria Colgan @sqlmaria
taken over AskTOM for Oracle Developers 4 Oracle Developers @otndev
5 Oracle @oracle
6 AskTOM @oracleasktom

237 238

SQL> with
2 tweetised(ind,tweet_txt) as
3 (
IND TWEET_TXT
4 select 1 ind, txt tweet_txt --------- -----------------------------------------------------------------------------------------
1 I caught up with Connor and Maria Colgan today. They have taken over AskTOM for...
5 from messages 2 I caught up with Connor and Maria Colgan today. They have taken over AskTOM for...
6 union all 3 I caught up with @connor_mc_d and Maria Colgan today. They have taken over AskTOM for...
4 I caught up with @connor_mc_d and @sqlmaria today. They have taken over AskTOM for ...
7 select ind+1, replace(tweet_txt,term,handle) 5 I caught up with @connor_mc_d and @sqlmaria today. They have taken over AskTOM for @otndev
8 from tweetised, twitter_handles 6 I caught up with @connor_mc_d and @sqlmaria today. They have taken over AskTOM for @otndev
7 I caught up with @connor_mc_d and @sqlmaria today. They have taken over @oracleasktom ...
9 where ind = id
10 )
11 select * from tweetised;

239 240

40
5/11/2017

SQL> with
2 tweetised(ind,tweet_txt) as
3 (
4 select 1 ind, txt tweet_txt
5 from messages

last
6 union all
7 select ind+1, replace(tweet_txt,term,handle)
8 from tweetised, twitter_handles
9 where ind = id
10 )
11 select * from tweetised
12 order by ind desc
13 fetch first 1 row only;

IND TWEET_TXT
---------- ------------------------------------------------------
7 I caught up with @connor_mc_d and @sqlmaria today. They...

241 242

staying up to date "fill in the blanks"

243 244

SQL> select dt, val,


SQL> select dt, val 2 case when val is not null
2 from t 3 then to_char(row_number() over (order by dt),'fm0000')||'-'||val
3 order by dt; 4 end max_val
5 from t
6 order by dt;
DT VAL
--------- ---------- DT VAL MAX_VAL
02-JAN-11 195 --------- ---------- -----------
03-JAN-11 195 02-JAN-11 195 0001-195
04-JAN-11 195 03-JAN-11
05-JAN-11 195 04-JAN-11
05-JAN-11
06-JAN-11 129
06-JAN-11 129 0005-129
07-JAN-11 129 07-JAN-11
08-JAN-11 129 08-JAN-11
09-JAN-11 129 09-JAN-11
10-JAN-11 87 10-JAN-11 87 0009-87
11-JAN-11 87 11-JAN-11
... ...

245 246

41
5/11/2017

SQL> select dt, val, SQL> select dt, val,


2 max(max_val) over (order by dt) max_val_str 2 to_number(substr(max(max_val) over (order by dt),5)) max_val
3 from ( select dt, val, 3 from ( select dt, val,
4 case when val is not null 4 case when val is not null
5 then to_char(row_number() over (order by dt),'fm0000')||'-'||val 5 then to_char(row_number() over (order by dt),'fm0000')||val
6 end max_val 6 end max_val
7 from t ) order by dt; 7 from t ) order by dt
8 /
DT VAL MAX_VAL_STR
--------- ---------- ------------------ DT VAL MAX_VAL
02-JAN-11 195 0001-195 --------- ---------- ----------
03-JAN-11 0001-195 02-JAN-11 195 195
04-JAN-11 0001-195 03-JAN-11 195
05-JAN-11 0001-195 04-JAN-11 195
06-JAN-11 129 0005-129 05-JAN-11 195
07-JAN-11 0005-129 06-JAN-11 129 129
08-JAN-11 0005-129 07-JAN-11 129
09-JAN-11 0005-129 08-JAN-11 129
10-JAN-11 87 0009-87 09-JAN-11 129
11-JAN-11 0009-87 10-JAN-11 87 87
... 11-JAN-11 87

247 248

SQL> select dt, val,


2 last_value(val ignore nulls) over (order by dt) val
3 from t
4 order by dt;

DT VAL VAL
--------- ---------- ----------
02-JAN-11 195 195
03-JAN-11 195

wrap up
04-JAN-11 195
05-JAN-11 195
06-JAN-11 129 129
07-JAN-11 129
08-JAN-11 129
09-JAN-11 129
10-JAN-11 87 87
11-JAN-11 87

249 250

SQL very cool

251 252

42
5/11/2017

very powerful defacto language polyglot world

253 254

less code never too early to start

255 256

258

43
5/11/2017

@connor_mc_d

#MakeDataGreatAgain

44

You might also like