3. 9/17/2019
3
5 5
6
Stuff
youtube bit.ly/youtube-connor
blog bit.ly/blog-connor
twitter bit.ly/twitter-connor
400+ posts mainly on database & development
250 technical videos, new uploads every week
rants and raves on tech and the world :-)
5
6
13. 9/17/2019
13
27
SQL is microservices !
28
"fine-grained to perform a single function"
"Each service is ... minimal, and complete"
https://en.wikipedia.org/wiki/Microservices
select COUNT(*)
from PEOPLE
where GENDER = 'MALE'
27
28
15. 9/17/2019
15
31
SQL is entirely APIs !
32
"By abstracting the underlying implementation"
"describes the expected behaviour ...
but can have multiple implementations"
https://en.wikipedia.org/wiki/Application_programming_interface
select NAME, STREET_NO, ZIP_CODE
from PEOPLE p,
ADDRESS a
where p.AGE > 50
and p.ADDRESS_ID = a.ADDRESS_ID;
31
32
18. 9/17/2019
18
45
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
10 , ( select to_char( rownum ) z
11 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)
46
18 or z = substr(s,mod(ind-1,9)-8+lp*9,1)
19 or z = substr(s,mod(trunc((ind-1)/3),3)*3
20 +trunc((ind-1)/27)*27+lp
21 +trunc((lp-1)/3)*6,1)
22 )
23 ),
24 result as (
25 select s
26 from x
27 where ind = 0 )
28 select
29 regexp_replace(substr(s,(idx-1)*9+1,9),
30 '(...)(...)(...)',
31 '1|2|3')||
32 case when mod(idx,3)=0 then chr(10)||rpad('-',11,'-') end soln
33 from result,
34 ( select level idx
35 from dual
36 connect by level <= 9 )
Ack: Anton Scheffer,
https://technology.amis.nl
45
46
20. 9/17/2019
20
49
100%
% of developers that
will need to solve Sudoku
as part of their job
50
100%
% of developers that need
to get real work done
49
50
37. 9/17/2019
37
83
SQL> insert into MY_TABLE
2 select *
3 from MY_HUGE_GREAT_FAT_TABLE;
Elapsed: 06:12:34.00
ERROR at line 1:
ORA-01847: day of month must be between 1 and last day of month
84
84
83
84
41. 9/17/2019
41
91
hard
92
SQL> insert into MY_TABLE
2 select *
3 from MY_HUGE_GREAT_FAT_TABLE
4 where "not a duplicate"
5 and "datatypes are ok"
6 and "foreign keys are ok"
7 and "check constraints are ok"
91
92
48. 9/17/2019
48
105
105
SQL> select *
2 from timeslots;
HR
--
8
9
10
11
12
13
14
15
16
SQL> select *
2 from bookings;
HR ROOM WHO
------- ---------- -------
8 Room2 PETE
9 Room1 JOHN
11 Room1 MIKE
14 Room2 JILL
15 Room2 JANE
16 Room1 SAM
106
bookings by hour
conventional outer join
105
106
49. 9/17/2019
49
107
SQL> SELECT hrs.hr, t1.room, t1.who
2 from timeslots hrs
3 left outer join bookings t1
4 on hrs.hr = t1.hr
5 order by 1
HR ROOM WHO
------- ---------- ----------
8 Room2 PETE
9 Room1 JOHN
10
11 Room1 MIKE
12
13
14 Room2 JILL
15 Room2 JANE
16 Room1 SAM
108
bookings by hour per room
107
108
50. 9/17/2019
50
109
HR ROOM WHO
------- ---------- ----------
8 Room2 PETE
9
10
11
12
13
14 Room2 JILL
15 Room2 JANE
16
HR ROOM WHO
------- ---------- ----------
8
9 Room1 JOHN
10
11 Room1 MIKE
12
13
14
15
16 Room1 SAM
110
SQL> select *
2 from timeslots;
HR
--
8
9
10
11
12
13
14
15
16
x "Room 1"
x "Room 2"
...
x "Room n"
109
110
51. 9/17/2019
51
111
partitioned outer join
112
SQL> SELECT hrs.hr, t1.room, t1.who
2 FROM bookings t1
3 PARTITION BY (t1.room)
4 RIGHT OUTER JOIN timeslots ON (hrs.hr = t1.hr)
5 order by 1,2
HR ROOM WHO
--------- ---------- ----------
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
111
112
54. 9/17/2019
54
117
WITH last_hire AS
(
select deptno, max(hiredate)
from emp
group by deptno
)
select * from last_hire;
118
"who cares?....... more code, same result"
117
118
58. 9/17/2019
58
125
Codd & Date
126
"data is represented as mathematical n-ary
relations, an n-ary relation being a subset of the
Cartesian product of n domains."
125
126
60. 9/17/2019
60
129
step by step
130
"First, get the total salary paid by each department,
then get the average of these totals,
then list those departments above that average"
SQL ?
129
130
61. 9/17/2019
61
131
"First, get the total salary paid by department...
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),
132
"...then get the average of these totals...
6 avg_sal AS ( SELECT AVG(dept_sal) avsal
7 FROM dept_salaries)
131
132
62. 9/17/2019
62
133
"...then list those departments above average."
8 SELECT * FROM dept_salaries d, avg_sal a
9 WHERE d.dept_sal > a.avsal
10 ORDER BY d.dname;
134
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),
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
10 ORDER BY d.dname;
133
134
67. 9/17/2019
67
143
scalar queries
144
SQL> select
2 ( select dname
3 from dept
4 where deptno = e.deptno ) dname,
5 decode(empno, 7499,
6 ( select max(sal) from emp ),
7 -1)
8 from
9 ( select * from emp
10 where sal > 0 ) e
11 where
12 ( select max(hiredate) from emp ) < sysdate
13 /
scalar
anywhere an
expression could be
143
144
68. 9/17/2019
68
149
turn procedural into "relational"
150
List all persons in the STAFF table plus their salary calculated
based on CLASS_TYPE column value:
= ‘T’ (temp worker) salary is FIXED_PRICE from
PART_TIME_PACKAGE table
= ‘C’ (contractor) salary is the HRS * HRLY_RATE from
CONTRACT_PACKAGE table
= ‘P’ (permanent) salary is the ANNUAL_SAL + BONUS from
PERM_PACKAGE table
149
150
69. 9/17/2019
69
151
different table per row
152
4 begin
5 for i in ( select * from staff ) loop
6 if i.class_type = 'T' then -- part time
7 select fixed_price
8 into v_sal
9 from part_time_package
10 where id = i.class_id;
11 elsif i.class_type = 'C' then -- contractors
12 select hrs * hrly_rate
13 into v_sal
14 from contract_package
15 where id = i.class_id;
16 elsif i.class_type = 'P' then -- permanent
17 select annual_sal + bonus
18 into v_sal
19 from perm_package
20 where id = i.class_id;
21 end if;
22 dbms_output.put_line(rpad(i.name,20)||lpad(v_sal,10));
23 end loop;
151
152
70. 9/17/2019
70
153
SQL can still do it
154
SQL> select s.name,
2 case class_type
3 when 'T' then (
4 select fixed_price
5 from part_time_package
6 where id = s.class_id )
7 when 'C' then (
8 select hrs * hrly_rate
9 from contract_package
10 where id = s.class_id )
11 when 'P' then (
12 select annual_sal + bonus
13 from perm_package
14 where id = s.class_id )
15 end sal
16 from staff s;
Scalar subquery
NAME SAL
------------------------------ -------
JOHN SMITH 123.45
JOE BLOGGS 2435.54
153
154
78. 9/17/2019
78
174
most of us know about analytics
175
SQL> select row_number() OVER ( order by sal )
2 from emp
3 ...
https://bit.ly/analytic_sql
174
175
79. 9/17/2019
79
176
"Show me lowest salary for each department..."
SQL> select deptno, min(sal)
2 from emp
3 group by deptno;
"...and I need to know who has that lowest salary"
SQL> select deptno, empno, min(sal)
2 from emp
3 group by deptno;
ORA-00979: not a GROUP BY expression
177
KEEP extension
176
177
80. 9/17/2019
80
178
SQL> select deptno, min(sal),
2 min(empno)
3 KEEP ( dense_rank FIRST order by sal) empno
4 from emp
5 group by deptno;
DEPTNO MIN(SAL) EMPNO
---------- ---------- ----------
10 1300 7934
20 800 7369
30 950 7900
179
9
178
179
83. 9/17/2019
83
184
SQL> select surname
2 from names;
SURNAME
------------------------------
jones
brown
SMITH
sigh...
185
SQL> select initcap(surname)
2 from names;
SURNAME
------------------------------
Jones
Brown
Smith
Mcdonald
184
185
84. 9/17/2019
84
186
and it just gets worse...
187
SQL> select *
2 from customers
3 where cust_name = 'ADAMS';
COUNTRY CREATED CUST_NAME
------------ --------- ------------
AUS 07-NOV-16 ADAMS
186
187
85. 9/17/2019
85
188
SQL> select *
2 from customers
3 where upper(cust_name) = 'ADAMS';
COUNTRY CREATED CUST_NAME
------------ --------- ------------
AUS 07-NOV-16 Adams
AUS 07-NOV-16 ADAMS
AUS 07-NOV-16 adams
189
SQL> select * from customers
2 where upper(cust_name) = 'ADAMS';
188
189
87. 9/17/2019
87
192
SQL> create index cust_ix
2 on customers ( cust_name );
Index created.
SQL> create index cust_ix2
2 on customers ( upper(cust_name) );
Index created.
193
DML slower
more contention
more redo/undo
192
193
89. 9/17/2019
89
196
SQL> alter table customers shrink space;
*
ERROR at line 1:
ORA-10631: SHRINK clause should not be specified
197
a better way
196
197
91. 9/17/2019
91
200
SQL> CREATE TABLE CUSTOMERS
2 (
3 COUNTRY VARCHAR2(128),
4 CREATED DATE,
5 CUST_NAME VARCHAR2(150) COLLATE BINARY_CI
6 );
Table created.
"case insenstive"
201
SQL> create index cust_ix
2 on customers ( cust_name);
Index created.
SQL> set autotrace traceonly explain
SQL> select * from customers
2 where cust_name = 'ADAMS';
-----------------------------------------------------------------
| Id | Operation | Name | Rows |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| CUSTOMERS | 1 |
|* 2 | INDEX RANGE SCAN | CUST_IX | 1 |
-----------------------------------------------------------------
200
201
92. 9/17/2019
92
202
"what is so special about that?"
203
SQL> select * from customers
2 where cust_name = 'ADAMS';
COUNTRY CREATED CUST_NAME
------------ --------- ----------------
AUS 07-NOV-16 Adams
AUS 08-NOV-16 ADAMS
AUS 09-NOV-16 adams
202
203
93. 9/17/2019
93
204
binary_ci
SQL> select * from customers
2 where cust_name = 'ADAMS';
COUNTRY CREATED CUST_NAME
------------ --------- ------------
AUS 07-NOV-16 Adams
AUS 08-NOV-16 ADAMS
AUS 09-NOV-16 adams
205
binary_ai
SQL> select * from customers
2 where cust_name = 'ADAMS';
COUNTRY CREATED CUST_NAME
------------ --------- -----------
AUS 07-NOV-16 Adams
AUS 08-NOV-16 ADAMS
AUS 09-NOV-16 adams
AUS 10-NOV-16 adáms
adáms
204
205
100. 9/17/2019
100
218
select emp.*
from emp,
( select trunc(hiredate,'YYYY'), max(empno) empno
from emp
where empno > 0
group by trunc(hiredate,'YYYY') ) x,
( select deptno, avg(sal)
from emp
group by deptno ) y
where x.empno = emp.empno
and y.deptno = emp.deptno
219
Id | Operation | Name |
----------------------------------------------|
0 | SELECT STATEMENT | |
1 | HASH JOIN | |
2 | TABLE ACCESS BY INDEX ROWID | EMP |
3 | NESTED LOOPS | |
4 | VIEW | |
5 | SORT GROUP BY | |
6 | TABLE ACCESS BY INDEX ROWID| EMP |
7 | INDEX FULL SCAN | E2 |
8 | INDEX RANGE SCAN | E1 |
9 | VIEW | |
10 | SORT GROUP BY | |
11 | TABLE ACCESS BY INDEX ROWID | EMP |
12 | INDEX RANGE SCAN | E2 |
218
219
101. 9/17/2019
101
220
?
select emp.*
from emp,
( select trunc(hiredate,'YYYY'), max(empno) empno
from emp
where empno > 0
group by trunc(hiredate,'YYYY') ) x,
( select deptno, avg(sal)
from emp
group by deptno ) y
where x.empno = emp.empno
and y.deptno = emp.deptno
Id | Operation | Name |
----------------------------------------------|
0 | SELECT STATEMENT | |
1 | HASH JOIN | |
2 | TABLE ACCESS BY INDEX ROWID | EMP |
3 | NESTED LOOPS | |
4 | VIEW | |
5 | SORT GROUP BY | |
6 | TABLE ACCESS BY INDEX ROWID| EMP |
7 | INDEX FULL SCAN | E2 |
8 | INDEX RANGE SCAN | E1 |
9 | VIEW | |
10 | SORT GROUP BY | |
11 | TABLE ACCESS BY INDEX ROWID | EMP |
12 | INDEX RANGE SCAN | E2 |
221
select emp.*
from emp,
( select /*+ QB_NAME(YR_HIRE) */
trunc(hiredate,'YYYY'), max(empno) empno
from emp
where empno > 0
group by trunc(hiredate,'YYYY') ) x,
( select /*+ QB_NAME(AV_SAL) */
deptno, avg(sal)
from emp
group by deptno ) y
where x.empno = emp.empno
and y.deptno = emp.deptno
220
221
102. 9/17/2019
102
222
Id | Operation | Name | Query Block
----------------------------------------------|--------------
0 | SELECT STATEMENT | |
1 | HASH JOIN | |
2 | TABLE ACCESS BY INDEX ROWID | EMP |
3 | NESTED LOOPS | |
4 | VIEW | |
5 | SORT GROUP BY | |
6 | TABLE ACCESS BY INDEX ROWID| EMP |
7 | INDEX FULL SCAN | E2 |
8 | INDEX RANGE SCAN | E1 |
9 | VIEW | |
10 | SORT GROUP BY | |
11 | TABLE ACCESS BY INDEX ROWID | EMP |
12 | INDEX RANGE SCAN | E2 |
SEL$1
SEL$1
AV_SAL
AV_SAL
AV_SAL
AV_SAL
SEL$1
YR_HIRE
YR_HIRE
YR_HIRE
YR_HIRE
223
last
222
223
111. 9/17/2019
111
240
Proven Ways to Make Applications Slower and Less Secure [THT4796]
Tuesday, September 17, 04:30 PM - 04:50 PM, The Exchange - Theater 3
The Best Oracle Database Feature Ever Invented [THT4798]
Wednesday, September 18, 10:15 AM - 10:35 AM, The Exchange - Theater 3
Flashback - Not Just for DBAs
Wednesday, September 18, 04:00 PM - 04:45 PM, Moscone South - Room 306
PL/SQL: Still the Best Data Access Language
Thursday, September 19, 12:15 PM - 01:00 PM, Moscone South - Room 155A
241
Have a great OpenWorld !
youtube youtube.com/c/ConnorMcDonaldOracle
blog connor-mcdonald.com
twitter @connor_mc_d
240
241