1
Connor McDonald
bit.ly/techguysong
2 2
3 3
4 4
5
Stuff
youtube bit.ly/youtube-connor
blog connor-mcdonald.com
twitter @connor_mc_d
400+ posts mainly on database & development
300+ technical videos, new uploads every week
rants and raves on tech and the world :-)
6
etc...
facebook bit.ly/facebook-connor
linkedin bit.ly/linkedin-connor
instagram bit.ly/instagram-connor
slideshare bit.ly/slideshare-connor
7 7https://asktom.oracle.com
10
SO
THE
BIG
QUESTION
IS
16
17
why talk about SQL ?
18
it is old !
19
20
why talk about SQL # 1
21
NoSQL
non relational
22
23
why talk about SQL # 2
24
developers love cool stuff
25
MICROSERVICES
26
SQL is microservices !
27
"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'
28
even cooler stuff
29
API
30
SQL is entirely APIs !
31
"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;
40
key point
41
this session is not about ...
42
being a smart-ass
43
we can do anything ...
44
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)
45
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
46
SQL> variable board varchar2(1000)
SQL> begin :board :=
2 '53.|.7.|...
3 6..|195|...
4 .98|...|.6.
5 -----------
6 8..|.61|..3
7 4..|8.3|..1
8 7..|.2.|..6
9 -----------
10 .6.|...|28.
11 ...|419|..5
12 ...|.8.|.79
13 ';
14 end;
5 3 7
6 1 9 5
9 8 6
8 6 1 3
4 8 3 1
7 2 6
6 2 8
4 1 9 5
8 7 9
47
SOLUTION
-----------
534|678|912
672|195|348
198|342|567
-----------
859|761|423
426|853|791
713|924|856
-----------
961|537|284
287|419|635
345|286|179
-----------
sud.sql
48
100%
% of developers that
will need to solve Sudoku
as part of their job
49
100%
% of developers that need
to get real s*#t done
50
real stuff
51
52
some controversy...
DBA
54
55
Public
56
Public
57
DBA friendship is important
oc00.sql
58
59
query block naming
60
C#, C++ ESB Tuxedo
Weblogic
Stored
Procedure
61
for (int i = 0; i < WinningCombinations.Count; ++i)
{
if (WinningCombinations[i].Investment > 0 &&
WinningCombinations[i].PrimaryDividend > MinDividendDeadHeat)
{
maxDivisor =
Math.Max(maxDivisor, WinningCombinations[i].Divisor);
}
}
for (int i = 0; i < WinningCombinations.Count; ++i)
{
if (WinningCombinations[i].Investment > 0 &&
WinningCombinations[i].PrimaryDividend > MinDividendDeadHeat)
{
WinningCombinations[i].Divisor =
maxDivisor / WinningCombinations[i].Divisor;
sumNewDivisors += WinningCombinations[i].Divisor;
}
}
62
no comments
64
"C# is self-documenting"
65
SQL can be complex
66
SQL should "self
document"
67
query blocks = self-documenting SQL
68
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
69
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 |
70
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 |
71
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
72
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
73
74
error logging
75
SQL> insert into MY_TABLE
2 select *
3 from MY_HUGE_GREAT_FAT_TABLE;MY_HUGE_GREAT_FAT_TABLE;
76
77
78
79
80
81
SQL> insert into MY_TABLE
2 select *
3 from MY_HUGE_GREAT_FAT_TABLE;
Elapsed: 06:12:34.00
82
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
83
83
oooooooo !
84
84
85
and then we do this :-)
86
SQL> select count(*) from MY_TABLE;
COUNT(*)
----------
0
87
what we would like
88
keep successful rows
89
skip / bypass bad rows
90
hard
91
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"
92
SQL> exec DBMS_ERRLOG.CREATE_ERROR_LOG('EMP')
93
ERR$_EMP
94
DBMS_ERRLOG.CREATE_ERROR_LOG (
dml_table_name IN VARCHAR2,
err_log_table_name IN VARCHAR2 := NULL,
err_log_table_owner IN VARCHAR2 := NULL,
...
95
SQL> desc ERR$_EMP
Name Null? Type
----------------------------- -------- ----------------
ORA_ERR_NUMBER$ NUMBER
ORA_ERR_MESG$ VARCHAR2(2000)
ORA_ERR_ROWID$ ROWID
ORA_ERR_OPTYP$ VARCHAR2(2)
ORA_ERR_TAG$ VARCHAR2(2000)
EMPNO VARCHAR2(4000)
ENAME VARCHAR2(4000)
JOB VARCHAR2(4000)
MGR VARCHAR2(4000)
HIREDATE VARCHAR2(4000)
SAL VARCHAR2(4000)
COMM VARCHAR2(4000)
DEPTNO VARCHAR2(4000)
96
96
97
example
98
add to EMP from NEW_DATA
99
SQL> select * from NEW_DATA;
EMPNO SAL DEPTNO
---------- ---------- ----------
1000 5000 20
100X 3550 10
2000 2500 50
7934 4000 20
non-numeric
no dept 50
duplicate
100
SQL> exec dbms_errlog.create_error_log('EMP');
PL/SQL procedure successfully completed.
SQL> insert into EMP (empno,sal,deptno)
2 select empno,sal,deptno
3 from NEW_DATA
4 LOG ERRORS REJECT LIMIT UNLIMITED;
1 row created.
50
101
SQL> select ORA_ERR_OPTYP$ op, ORA_ERR_MESG$, EMPNO
2 from ERR$_EMP
OP ORA_ERR_MESG$ EMPNO
-- ------------------------------------------------------------ -----
I ORA-01722: invalid number 100X
I ORA-02291: integrity constraint (SCOTT.FK_DEPTNO) violated 2000
I ORA-00001: unique constraint (SCOTT.PK_EMP) violated 7934
102
103
partitioned outer join
104
104
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
105
bookings by hour
conventional outer join
106
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
107
bookings by hour per room
108
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
109
SQL> select *
2 from timeslots;
HR
--
8
9
10
11
12
13
14
15
16
x "Room 1"
x "Room 2"
...
x "Room n"
110
partitioned outer join
111
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
112
113
subquery factoring
114
common table expressions
115
WITH clause
116
WITH last_hire AS
(
select deptno, max(hiredate)
from emp
group by deptno
)
select * from last_hire;
117
"who cares?....... more code, same result"
118
why is it cool ?
119
good solution metaphor
120
relational is a rigorous model ...
121
relational is the dominant model ...
122
relational ... can sortta suck :-)
123
not our fault
124
Codd & Date
125
"data is represented as mathematical n-ary
relations, an n-ary relation being a subset of
the Cartesian product of n domains."
126
127
procedural world
128
step by step
129
"First, get the total salary paid by each department,
then get the average of these totals,
then list those departments above that average"
SQL ?
130
"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),
131
"...then get the average of these totals...
6 avg_sal AS ( SELECT AVG(dept_sal) avsal
7 FROM dept_salaries)
132
"...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;
133
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;
134
programmer's approach....
135
... relational solution
136
the "finishing touches"
137
everyone loves JSON
138
recall: partitioned outer join
139
SQL> with raw_data as (
2 select
3 hrs.hr,
4 t1.room,
5 t1.who
6 from bookings t1
7 partition by (t1.room)
8 right outer join hrs
9 on (hrs.hr = t1.hr)
10 order by
11 hr, room
12 )
13 select
14 json_arrayagg(
15 json_object(key room||to_char(hr) value who )
16 order by hr ) as meetings
17 from raw_data ;
[{"Room1-08":null},
{"Room1-09":"JOHN"},
{"Room1-10":null},
{"Room1-11":"MIKE"},
{"Room1-12":null},
...
...
{"Room2-14":"JILL"},
{"Room2-15":null},
{"Room2-16":"JANE"}]
140
141
speaking of procedural to relational
142
scalar queries
143
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
148
turn procedural into "relational"
149
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
150
different table per row
151
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;
152
SQL can still do it
153
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
154
"big deal..."
155
subquery caching
156
157
STOPKEY optimization
158
aka, pagination
159
"employees by hiredate, recent first"
160
SQL> select empno, ename, hiredate
2 from emp
3 where rownum <= 5
4 order by hiredate desc;
EMPNO ENAME HIREDATE
---------- ---------- -------------------
7654 MARTIN 28/09/1981 00:00:00
7566 JONES 02/04/1981 00:00:00
7521 WARD 22/02/1981 00:00:00
7499 ALLEN 20/02/1981 00:00:00
7369 SMITH 17/12/1980 00:00:00
161
inline view
162
SQL> select *
2 from (
3 select empno, ename, hiredate
4 from emp
5 order by hiredate desc
6 )
7 where rownum <= 5;
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
162
163
SQL> select *
2 from (
3 select
4 empno, ename, hiredate,
5 row_number() over ( order by hiredate desc) rn
6 from emp
7 )
8 where rn <= 5;
164
SQL> select empno, ename, hiredate
2 from emp
3 order by hiredate desc
4 fetch first 5 rows only;
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
165
"TL;DR ... my app can do it"
166
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() )
{
...
i = i + 1;
if (i > 5) {
break;
}
}
rset.close();
}
167
168
demo
oc01.sql
169
let the database know
170
SQL> select *
2 from (
3 select empno, ename, hiredate
4 from emp
5 order by hiredate desc
6 )
7 where rownum <= 5;
------------------------------------------------
| Id | Operation | Name | Rows |
------------------------------------------------
| 0 | SELECT STATEMENT | | 5 |
|* 1 | COUNT STOPKEY | | |
| 2 | VIEW | | 14 |
|* 3 | SORT ORDER BY STOPKEY| | 14 |
| 4 | TABLE ACCESS FULL | EMP | 14 |
------------------------------------------------
171
SQL> select empno, ename, hiredate
2 from emp
3 order by hiredate desc
4 fetch first 5 rows only;
-------------------------------------------------
| Id | Operation | Name | Rows |
-------------------------------------------------
| 0 | SELECT STATEMENT | | 14 |
|* 1 | VIEW | | 14 |
|* 2 | WINDOW SORT PUSHED RANK| | 14 |
| 3 | TABLE ACCESS FULL | EMP | 14 |
-------------------------------------------------
172
173
most of us know about analytics
174
SQL> select row_number() OVER ( order by sal )
2 from emp
3 ...
https://bit.ly/analytic_sql
175
"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
176
KEEP extension
177
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
Emp 7934 has
the lowest salary
of 1300 in dept 10
178
179
every system ...
180
... I've worked on
181
struggles with cAsE
182
no correlation ☺
but its my fault
183
SQL> select surname
2 from names;
SURNAME
------------------------------
jones
brown
SMITH
sigh...
184
SQL> select initcap(surname)
2 from names;
SURNAME
------------------------------
Jones
Brown
Smith
Mcdonald
185
and it just gets worse...
186
SQL> select *
2 from customers
3 where cust_name = 'ADAMS';
COUNTRY CREATED CUST_NAME
------------ --------- ------------
AUS 07-NOV-16 ADAMS
187
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
188
189
SQL> select * from customers
2 where upper(cust_name) = 'ADAMS';
191
SQL> select column_name
2 from user_ind_columns
3 where index_name = 'CUST_IX';
COLUMN_NAME
------------------------------
CUST_NAME
SQL> select * from customers
2 where upper(cust_name) = 'ADAMS';
-------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |
-------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 152 |
|* 1 | TABLE ACCESS FULL| CUSTOMERS | 1 | 152 |
-------------------------------------------------------
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
194
"not my problem"
195
196
SQL> alter table customers shrink space;
*
ERROR at line 1:
ORA-10631: SHRINK clause should not be specified
197
a better way
198
column level collation
199
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 |
-----------------------------------------------------------------
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
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
206
column | table | user
207
SQL> alter table people default collation binary_ai;
new columns only
208
210
210
211
"talking" to your database
... makes it faster
212
example
213
STORES
CUSTOMERS
SALES
214
select prod_id, max(amount)
from stores st,
customers c,
sales s
where s.cust_id = c.cust_id(+)
and c.store_id = st.store_id
and s.amount > 10
group by prod_id
hash outer join ?
nested loop ?
STORES first ?
sort merge ?
215
---------------------------------------------------
| Id | Operation | Name | Rows |
---------------------------------------------------
| 0 | SELECT STATEMENT | | 100 |
| 1 | HASH GROUP BY | | 100 |
|* 2 | HASH JOIN | | 990K |
| 3 | NESTED LOOPS SEMI | | 5000 |
| 4 | TABLE ACCESS FULL| CUSTOMERS | 5000 |
|* 5 | INDEX UNIQUE SCAN| STORE_IX | 50 |
|* 6 | TABLE ACCESS FULL | SALES | 990K |
---------------------------------------------------
216
can we do better ?
add indexes ?
rewrite query ?
result cache ?
materialized view ?
217
share your knowledge with the db
oc04.sql
218
wrap up
219
SQL
220
very cool
221
very powerful
222
less code
223
never too early to start
226
25 years of tips and techniques
Tuesday, 9am, Empress
227
Have a great conference !
youtube youtube.com/c/ConnorMcDonaldOracle
blog connor-mcdonald.com
twitter @connor_mc_d

UKOUG 2019 - SQL features

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
    5 Stuff youtube bit.ly/youtube-connor blog connor-mcdonald.com twitter@connor_mc_d 400+ posts mainly on database & development 300+ technical videos, new uploads every week rants and raves on tech and the world :-)
  • 6.
    6 etc... facebook bit.ly/facebook-connor linkedin bit.ly/linkedin-connor instagrambit.ly/instagram-connor slideshare bit.ly/slideshare-connor
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
    27 "fine-grained to performa single function" "Each service is ... minimal, and complete" https://en.wikipedia.org/wiki/Microservices select COUNT(*) from PEOPLE where GENDER = 'MALE'
  • 26.
  • 27.
  • 28.
  • 29.
    31 "By abstracting theunderlying 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;
  • 30.
  • 31.
    41 this session isnot about ...
  • 32.
  • 33.
    43 we can doanything ...
  • 34.
    44 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)
  • 35.
    45 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
  • 36.
    46 SQL> variable boardvarchar2(1000) SQL> begin :board := 2 '53.|.7.|... 3 6..|195|... 4 .98|...|.6. 5 ----------- 6 8..|.61|..3 7 4..|8.3|..1 8 7..|.2.|..6 9 ----------- 10 .6.|...|28. 11 ...|419|..5 12 ...|.8.|.79 13 '; 14 end; 5 3 7 6 1 9 5 9 8 6 8 6 1 3 4 8 3 1 7 2 6 6 2 8 4 1 9 5 8 7 9
  • 37.
  • 38.
    48 100% % of developersthat will need to solve Sudoku as part of their job
  • 39.
    49 100% % of developersthat need to get real s*#t done
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
    57 DBA friendship isimportant oc00.sql
  • 48.
  • 49.
  • 50.
    60 C#, C++ ESBTuxedo Weblogic Stored Procedure
  • 51.
    61 for (int i= 0; i < WinningCombinations.Count; ++i) { if (WinningCombinations[i].Investment > 0 && WinningCombinations[i].PrimaryDividend > MinDividendDeadHeat) { maxDivisor = Math.Max(maxDivisor, WinningCombinations[i].Divisor); } } for (int i = 0; i < WinningCombinations.Count; ++i) { if (WinningCombinations[i].Investment > 0 && WinningCombinations[i].PrimaryDividend > MinDividendDeadHeat) { WinningCombinations[i].Divisor = maxDivisor / WinningCombinations[i].Divisor; sumNewDivisors += WinningCombinations[i].Divisor; } }
  • 52.
  • 54.
  • 55.
  • 56.
  • 57.
    67 query blocks =self-documenting SQL
  • 58.
    68 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
  • 59.
    69 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 |
  • 60.
    70 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 |
  • 61.
    71 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
  • 62.
    72 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
  • 63.
  • 64.
  • 65.
    75 SQL> insert intoMY_TABLE 2 select * 3 from MY_HUGE_GREAT_FAT_TABLE;MY_HUGE_GREAT_FAT_TABLE;
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
    81 SQL> insert intoMY_TABLE 2 select * 3 from MY_HUGE_GREAT_FAT_TABLE; Elapsed: 06:12:34.00
  • 72.
    82 SQL> insert intoMY_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
  • 73.
  • 74.
  • 75.
    85 and then wedo this :-)
  • 76.
    86 SQL> select count(*)from MY_TABLE; COUNT(*) ---------- 0
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
    91 SQL> insert intoMY_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"
  • 82.
  • 83.
  • 84.
    94 DBMS_ERRLOG.CREATE_ERROR_LOG ( dml_table_name INVARCHAR2, err_log_table_name IN VARCHAR2 := NULL, err_log_table_owner IN VARCHAR2 := NULL, ...
  • 85.
    95 SQL> desc ERR$_EMP NameNull? Type ----------------------------- -------- ---------------- ORA_ERR_NUMBER$ NUMBER ORA_ERR_MESG$ VARCHAR2(2000) ORA_ERR_ROWID$ ROWID ORA_ERR_OPTYP$ VARCHAR2(2) ORA_ERR_TAG$ VARCHAR2(2000) EMPNO VARCHAR2(4000) ENAME VARCHAR2(4000) JOB VARCHAR2(4000) MGR VARCHAR2(4000) HIREDATE VARCHAR2(4000) SAL VARCHAR2(4000) COMM VARCHAR2(4000) DEPTNO VARCHAR2(4000)
  • 86.
  • 87.
  • 88.
    98 add to EMPfrom NEW_DATA
  • 89.
    99 SQL> select *from NEW_DATA; EMPNO SAL DEPTNO ---------- ---------- ---------- 1000 5000 20 100X 3550 10 2000 2500 50 7934 4000 20 non-numeric no dept 50 duplicate
  • 90.
    100 SQL> exec dbms_errlog.create_error_log('EMP'); PL/SQLprocedure successfully completed. SQL> insert into EMP (empno,sal,deptno) 2 select empno,sal,deptno 3 from NEW_DATA 4 LOG ERRORS REJECT LIMIT UNLIMITED; 1 row created. 50
  • 91.
    101 SQL> select ORA_ERR_OPTYP$op, ORA_ERR_MESG$, EMPNO 2 from ERR$_EMP OP ORA_ERR_MESG$ EMPNO -- ------------------------------------------------------------ ----- I ORA-01722: invalid number 100X I ORA-02291: integrity constraint (SCOTT.FK_DEPTNO) violated 2000 I ORA-00001: unique constraint (SCOTT.PK_EMP) violated 7934
  • 92.
  • 93.
  • 94.
    104 104 SQL> select * 2from 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
  • 95.
  • 96.
    106 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
  • 97.
  • 98.
    108 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
  • 99.
    109 SQL> select * 2from timeslots; HR -- 8 9 10 11 12 13 14 15 16 x "Room 1" x "Room 2" ... x "Room n"
  • 100.
  • 101.
    111 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
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
    116 WITH last_hire AS ( selectdeptno, max(hiredate) from emp group by deptno ) select * from last_hire;
  • 107.
    117 "who cares?....... morecode, same result"
  • 108.
  • 109.
  • 110.
    120 relational is arigorous model ...
  • 111.
    121 relational is thedominant model ...
  • 112.
    122 relational ... cansortta suck :-)
  • 113.
  • 114.
  • 115.
    125 "data is representedas mathematical n-ary relations, an n-ary relation being a subset of the Cartesian product of n domains."
  • 116.
  • 117.
  • 118.
  • 119.
    129 "First, get thetotal salary paid by each department, then get the average of these totals, then list those departments above that average" SQL ?
  • 120.
    130 "First, get thetotal 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),
  • 121.
    131 "...then get theaverage of these totals... 6 avg_sal AS ( SELECT AVG(dept_sal) avsal 7 FROM dept_salaries)
  • 122.
    132 "...then list thosedepartments above average." 8 SELECT * FROM dept_salaries d, avg_sal a 9 WHERE d.dept_sal > a.avsal 10 ORDER BY d.dname;
  • 123.
    133 SQL> WITH dept_salariesAS ( 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;
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
    139 SQL> with raw_dataas ( 2 select 3 hrs.hr, 4 t1.room, 5 t1.who 6 from bookings t1 7 partition by (t1.room) 8 right outer join hrs 9 on (hrs.hr = t1.hr) 10 order by 11 hr, room 12 ) 13 select 14 json_arrayagg( 15 json_object(key room||to_char(hr) value who ) 16 order by hr ) as meetings 17 from raw_data ; [{"Room1-08":null}, {"Room1-09":"JOHN"}, {"Room1-10":null}, {"Room1-11":"MIKE"}, {"Room1-12":null}, ... ... {"Room2-14":"JILL"}, {"Room2-15":null}, {"Room2-16":"JANE"}]
  • 130.
  • 131.
  • 132.
  • 133.
    143 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
  • 134.
  • 135.
    149 List all personsin 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
  • 136.
  • 137.
    151 4 begin 5 fori 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;
  • 138.
  • 139.
    153 SQL> select s.name, 2case 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
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
    160 SQL> select empno,ename, hiredate 2 from emp 3 where rownum <= 5 4 order by hiredate desc; EMPNO ENAME HIREDATE ---------- ---------- ------------------- 7654 MARTIN 28/09/1981 00:00:00 7566 JONES 02/04/1981 00:00:00 7521 WARD 22/02/1981 00:00:00 7499 ALLEN 20/02/1981 00:00:00 7369 SMITH 17/12/1980 00:00:00
  • 147.
  • 148.
    162 SQL> select * 2from ( 3 select empno, ename, hiredate 4 from emp 5 order by hiredate desc 6 ) 7 where rownum <= 5; 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 162
  • 149.
    163 SQL> select * 2from ( 3 select 4 empno, ename, hiredate, 5 row_number() over ( order by hiredate desc) rn 6 from emp 7 ) 8 where rn <= 5;
  • 150.
    164 SQL> select empno,ename, hiredate 2 from emp 3 order by hiredate desc 4 fetch first 5 rows only; 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
  • 151.
    165 "TL;DR ... myapp can do it"
  • 152.
    166 public static voidPaging(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() ) { ... i = i + 1; if (i > 5) { break; } } rset.close(); }
  • 153.
  • 154.
  • 155.
  • 156.
    170 SQL> select * 2from ( 3 select empno, ename, hiredate 4 from emp 5 order by hiredate desc 6 ) 7 where rownum <= 5; ------------------------------------------------ | Id | Operation | Name | Rows | ------------------------------------------------ | 0 | SELECT STATEMENT | | 5 | |* 1 | COUNT STOPKEY | | | | 2 | VIEW | | 14 | |* 3 | SORT ORDER BY STOPKEY| | 14 | | 4 | TABLE ACCESS FULL | EMP | 14 | ------------------------------------------------
  • 157.
    171 SQL> select empno,ename, hiredate 2 from emp 3 order by hiredate desc 4 fetch first 5 rows only; ------------------------------------------------- | Id | Operation | Name | Rows | ------------------------------------------------- | 0 | SELECT STATEMENT | | 14 | |* 1 | VIEW | | 14 | |* 2 | WINDOW SORT PUSHED RANK| | 14 | | 3 | TABLE ACCESS FULL | EMP | 14 | -------------------------------------------------
  • 158.
  • 159.
    173 most of usknow about analytics
  • 160.
    174 SQL> select row_number()OVER ( order by sal ) 2 from emp 3 ... https://bit.ly/analytic_sql
  • 161.
    175 "Show me lowestsalary 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
  • 162.
  • 163.
    177 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 Emp 7934 has the lowest salary of 1300 in dept 10
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
    183 SQL> select surname 2from names; SURNAME ------------------------------ jones brown SMITH sigh...
  • 170.
    184 SQL> select initcap(surname) 2from names; SURNAME ------------------------------ Jones Brown Smith Mcdonald
  • 171.
    185 and it justgets worse...
  • 172.
    186 SQL> select * 2from customers 3 where cust_name = 'ADAMS'; COUNTRY CREATED CUST_NAME ------------ --------- ------------ AUS 07-NOV-16 ADAMS
  • 173.
    187 SQL> select * 2from 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
  • 174.
  • 175.
    189 SQL> select *from customers 2 where upper(cust_name) = 'ADAMS';
  • 177.
    191 SQL> select column_name 2from user_ind_columns 3 where index_name = 'CUST_IX'; COLUMN_NAME ------------------------------ CUST_NAME SQL> select * from customers 2 where upper(cust_name) = 'ADAMS'; ------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | ------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 152 | |* 1 | TABLE ACCESS FULL| CUSTOMERS | 1 | 152 | -------------------------------------------------------
  • 178.
    192 SQL> create indexcust_ix 2 on customers ( cust_name ); Index created. SQL> create index cust_ix2 2 on customers ( upper(cust_name) ); Index created.
  • 179.
  • 180.
  • 181.
  • 182.
    196 SQL> alter tablecustomers shrink space; * ERROR at line 1: ORA-10631: SHRINK clause should not be specified
  • 183.
  • 184.
  • 185.
  • 186.
    200 SQL> CREATE TABLECUSTOMERS 2 ( 3 COUNTRY VARCHAR2(128), 4 CREATED DATE, 5 CUST_NAME VARCHAR2(150) COLLATE BINARY_CI 6 ); Table created. "case insenstive"
  • 187.
    201 SQL> create indexcust_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 | -----------------------------------------------------------------
  • 188.
    202 "what is sospecial about that?"
  • 189.
    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
  • 190.
    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
  • 191.
    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
  • 192.
  • 193.
    207 SQL> alter tablepeople default collation binary_ai; new columns only
  • 194.
  • 196.
  • 197.
    211 "talking" to yourdatabase ... makes it faster
  • 198.
  • 199.
  • 200.
    214 select prod_id, max(amount) fromstores st, customers c, sales s where s.cust_id = c.cust_id(+) and c.store_id = st.store_id and s.amount > 10 group by prod_id hash outer join ? nested loop ? STORES first ? sort merge ?
  • 201.
    215 --------------------------------------------------- | Id |Operation | Name | Rows | --------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | | 1 | HASH GROUP BY | | 100 | |* 2 | HASH JOIN | | 990K | | 3 | NESTED LOOPS SEMI | | 5000 | | 4 | TABLE ACCESS FULL| CUSTOMERS | 5000 | |* 5 | INDEX UNIQUE SCAN| STORE_IX | 50 | |* 6 | TABLE ACCESS FULL | SALES | 990K | ---------------------------------------------------
  • 202.
    216 can we dobetter ? add indexes ? rewrite query ? result cache ? materialized view ?
  • 203.
    217 share your knowledgewith the db oc04.sql
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 212.
    226 25 years oftips and techniques Tuesday, 9am, Empress
  • 213.
    227 Have a greatconference ! youtube youtube.com/c/ConnorMcDonaldOracle blog connor-mcdonald.com twitter @connor_mc_d