Copyright	©	2018, Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Top	10	SQL	Performance	tips	&	
tricks	for	Java	Developers
Gerald	Venzl	
Senior	Principal	Product	Manager
Server	Technologies
Oracle	Development
@GeraldVenzl
geraldonit.com
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Roundtrips
Confidential	– Oracle	Internal/Restricted/Highly	Restricted 2
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 3
Roundtrips
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Roundtrips
• Avoid	unnecessary	round	trips	to	the	database
• Every	round	trip	includes	time	that	you	spend	on	the	network
• That	does	not mean	to	avoid	processing	anything	at	the	database
• You	will	see	why	just	in	a	bit…
4
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Bind	variables
Confidential	– Oracle	Internal/Restricted/Highly	Restricted 5
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 6
Bind	variables
SELECT	text	FROM	TEST
WHERE	id	=	453;
SQL	ID:
5mwwhtqv204ba
SQL	ID:
06jc0z1kcuu6b
SELECT	text	FROM	TEST
WHERE	id	=	879;
SELECT	text	FROM	TEST
WHERE	id	=	?;
SELECT	text	FROM	TEST
WHERE	id	=	?;
SQL	ID:
cknumntjbx8u3
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		|
0
5000
10000
15000
20000
25000
30000
Test	run	1 Test	run	2 Test	run	3
Milliseconds
Select	using	literals
Select	using	bind	var
• Three	test	runs	selecting	1k	rows
• Dark	blue	axis	shows	elapsed	time	
of	SELECT	using	literals
• Light	blue	axis	shows	elapsed	time	
of	SELECT	using	bind	variable
7
Bind	variables
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Bind	variables
• SQL	statements	are	strings
• But	data	manipulated	via	SQL	may	be	of	a	different	types	(Number,	Date,	…)
• A	bind	variable	allows	you	to	bind	data	to	an	explicit	date	type
– Without	having	to	convert	the	data	type	from	string
– And	without	having	to	parse	the	SQL	statement	again
• Bind	variables	also	avoid	SQL	injections,	i.e.	tighter	security
What	are	bind	variables
8
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		| 9
for(int i = 1; i <= rows; i++) {
PreparedStatement stmt = conn.prepareStatement(
"SELECT text FROM TEST WHERE id = " + i);
ResultSet rslt = stmt.executeQuery();
rslt.next();
// Fetch the column value to include fetching time
rslt.getString(1);
rslt.close();
stmt.close();
}
Bind	variables
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		| 10
PreparedStatement stmt = conn.prepareStatement(
"SELECT text FROM TEST WHERE id = ?");
for(int i = 1; i <= rows; i++) {
stmt.setInt(1, i);
ResultSet rslt = stmt.executeQuery();
rslt.next();
// Fetch the column value to include fetching time
rslt.getString(1);
rslt.close();
}
stmt.close();
Bind	variables
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Autocommit
Confidential	– Oracle	Internal/Restricted/Highly	Restricted 11
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Autocommit
How	does	the	DB	make	sure	your	data	is	stored	on	disk?
12
INSERT	INTO	purchase	(…)
VALUES	(:1,	:2,	:3,	…);
COMMIT
Buffer	Cache REDO	logs
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		|
0
2000
4000
6000
8000
10000
12000
14000
16000
18000
20000
Test	run	1 Test	run	2 Test	run	3
Milliseconds
Commit	every	row
Commit	at	end
• Three	test	runs	inserting	10k	rows
• Dark	blue	axis	shows	elapsed	time	
when	committing	after	every	row
• Light	blue	axis	shows	elapsed	time	
when	committing	only	once	after	
all	data	is	loaded
13
Autocommit
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Autocommit
• The	driver	commits	every	DML	on	your	behalf,	implicitly
• That	is,	for	every single	INSERT,	UPDATE,	DELETE,	MERGE	will	be	a	COMMIT
• Implies	a	second	roundtrip to	the	databases	for	every DML	operation
• Forces	database	to	write	to	disk,	perhaps	unnecessarily
• Means	you	can	never	ROLLBACK	your	changes
• It	is	turned	on	by	default
– connection.setAutoCommit(false)
What	is	the	Autocommit?
14
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		| 15
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO TEST (id, text, created_tms, last_upd_tms) VALUES
(?,?,SYSDATE,SYSDATE)");
long start = System.currentTimeMillis();
for(int i=1;i<=rows;i++) {
stmt.setInt(1, i);
stmt.setString(2, "This is the row with the value of " + i);
stmt.executeUpdate();
conn.commit();
}
long end = System.currentTimeMillis();
System.out.println("Elapsed time(ms) for commit after every row: " + (end-start));
Autocommit
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		| 16
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO TEST (id, text, created_tms, last_upd_tms) VALUES
(?,?,SYSDATE,SYSDATE)");
long start = System.currentTimeMillis();
for(int i=1;i<=rows;i++) {
stmt.setInt(1, i);
stmt.setString(2, "This is the row with the value of " + i);
stmt.executeUpdate();
}
conn.commit();
long end = System.currentTimeMillis();
System.out.println("Elapsed time(ms) for commit at the end: " + (end-start));
Autocommit
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Bulk	processing
Confidential	– Oracle	Internal/Restricted/Highly	Restricted 17
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Bulk	processing
• What	would	you	rather	do	on	a	table	with	10	million	rows:
18
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Bulk	processing
• What	would	you	rather	do	on	a	table	with	10	million	rows:
PreparedStatement stmt = conn.prepareStatement(
"SELECT value FROM PURCHASE WHERE tms > '2016-09-01'");
ResultSet rslt = stmt.executeQuery();
while (rslt.next()) {
val += rslt.getInt(1);
}
19
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Bulk	processing
• What	would	you	rather	do	on	a	table	with	10	million	rows:
PreparedStatement stmt = conn.prepareStatement(
"SELECT value FROM PURCHASE WHERE tms > '2016-09-01'");
ResultSet rslt = stmt.executeQuery();
while (rslt.next()) {
val += rslt.getInt(1);
}
PreparedStatement stmt = conn.prepareStatement(
"SELECT SUM(value) FROM PURCHASE WHERE tms > '2016-09-01'");
ResultSet rslt = stmt.executeQuery();
rslt.next();
val = rslt.getInt(1);
20
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Bulk	processing
• What	would	you	rather	do	on	a	table	with	10	million	rows:
PreparedStatement stmt = conn.prepareStatement(
"SELECT value FROM PURCHASE WHERE tms > '2016-09-01'");
ResultSet rslt = stmt.executeQuery();
while (rslt.next()) {
val += rslt.getInt(1);
}
PreparedStatement stmt = conn.prepareStatement(
"SELECT SUM(value) FROM PURCHASE WHERE tms > '2016-09-01'");
ResultSet rslt = stmt.executeQuery();
rslt.next();
val = rslt.getInt(1);
21
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		| 22
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO TEST (id, text, created_tms, last_upd_tms) VALUES
(?,?,SYSDATE,SYSDATE)");
long start = System.currentTimeMillis();
for(int i=1;i<=rows;i++) {
stmt.setInt(1, i);
stmt.setString(2, "This is the row with the value of " + i);
stmt.executeUpdate();
}
conn.commit();
long end = System.currentTimeMillis();
System.out.println("Elapsed time(ms) for row by row insert: " + (end-start));
Bulk	processing
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		| 23
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO TEST (id, text, created_tms, last_upd_tms) VALUES
(?,?,SYSDATE,SYSDATE)");
long start = System.currentTimeMillis();
for(int i=1;i<=rows;i++) {
stmt.setInt(1, i);
stmt.setString(2, "This is the row with the value of " + i);
stmt.addBatch();
}
stmt.executeBatch();
conn.commit();
long end = System.currentTimeMillis();
System.out.println("Elapsed time(ms) for set based insert: " + (end-start));
Bulk	processing
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		|
4229
4055 4118
76 84 72
0
500
1000
1500
2000
2500
3000
3500
4000
4500
Test	run	1 Test	run	2 Test	run	3
Milliseconds
Insert	every	row
Set	based	insert
• Three	test	runs	inserting	10k	rows
• Dark	blue	axis	shows	elapsed	time	
of	individual	inserts
• Light	blue	axis	shows	elapsed	time	
of	set	based	inserts
24
Bulk	processing
Copyright	©	2016, Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Bulk	processing
• Databases	work	best	with	set	based	processing
• Process	as	much	as	possible	on	the	side	where	it	makes	most	sense
• Generally,	a	single	SQL	statement	execution	is	the	fastest
• Avoids	unnecessary	roundtrips
25
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Fetch	size
Confidential	– Oracle	Internal/Restricted/Highly	Restricted 26
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Fetch	size
• Driver	fetches	more	than	one	row	from	the	database	at	once
• Avoids	unnecessary	roundtrips
• Great	if	you	know	you	will	fetch	many	rows
• Default	is	set	to	10
• statement.setFetchSize(int rows);
Implicit	and	proactive	fetching
27
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Savepoints
Confidential	– Oracle	Internal/Restricted/Highly	Restricted 28
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 29
INSERT INTO…
UPDATE…
INSERT INTO…
SAVEPOINT gerald1;
UPDATE…
UPDATE…
SAVEPOINT gerald2;
DELETE FROM…
ROLLBACK TO SAVEPOINT gerald1;
INSERT INTO…
COMMIT;
Savepoints
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Savepoints
• Save	the	intermediate	state(s)	of	a	long	running	transaction
• If	workload	fails	you	don’t	have	to	start	from	scratch
• Avoids	redoing	work	that	has	already	happened
Safe	the	progress	of	your	transaction
30
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
KISS
Keep	it	simple	SQL
Confidential	– Oracle	Internal/Restricted/Highly	Restricted 31
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
KISS
• The	simpler	the	SQL,	the	easier	to	maintain
• The	simpler	the	SQL,	the	faster	to	optimize
• The	simpler	the	SQL,	the	less	likely	for	the	optimizer	to	get	it	wrong
Keep	it	simple	SQL
32
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 33
SELECT (CASE
WHEN v.code = ‘ABC’ THEN 1
WHEN v.code = ‘DEF’ THEN 2
WHEN v.code = ‘GHI’ THEN 3
WHEN v.code = ‘YYZ’ THEN 4
ELSE 5
END) AS code,
v.fk_column AS fk_column,
v.fk_list AS fk_list,
v.grp_id AS grp_id
FROM (SELECT S1.grp_id,
S1.code,
MIN (S1.fk_column) AS fk_column,
LTRIM (MAX (SYS_CONNECT_BY_PATH (S1.fk_column, ‘;’))
KEEP (DENSE_RANK LAST ORDER BY S1.curr),
‘;’) AS fk_list
FROM (SELECT grp_id,
code,
fk_column,
ROW_NUMBER ()
OVER (PARTITION BY grp_id, code
ORDER BY fk_column) AS curr,
ROW_NUMBER ()
OVER (PARTITION BY grp_id, code
ORDER BY fk_column) – 1 AS prev
FROM quite_a_big_table
WHERE grp_id > 0) S1
GROUP BY S1.grp_id, S1.code
CONNECT BY S1.prev = PRIOR S1.curr
AND S1.grp_id = PRIOR S1.grp_id
START WITH curr = 1) v
ORDER BY grp_id, code
KISS
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 34
SELECT (CASE
WHEN v.code = ‘ABC’ THEN 1
WHEN v.code = ‘DEF’ THEN 2
WHEN v.code = ‘GHI’ THEN 3
WHEN v.code = ‘YYZ’ THEN 4
ELSE 5
END) AS code,
v.fk_column AS fk_column,
v.fk_list AS fk_list,
v.grp_id AS grp_id
FROM (SELECT grp_id,
code,
MIN (fk_column) AS fk_column,
LISTAGG(fk_column, ‘;’)
WITHIN GROUP (ORDER BY fk_column) AS fk_list
FROM quite_a_big_table
WHERE grp_id > 0
GROUP BY grp_id, code) v
ORDER BY grp_id, code
KISS
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
KISS	- Common	table	expression
• Specifies	a	sub	query	as	a	common	table	in	a	SELECT	statement
– A	table	or	subquery	that	is	read	more	than	once	by	the	same	statement
• Caches	results	of	sub	query	in	memory	on	DB	server
• Avoids	reading	the	same	tables	more	than	once,	hence	reducing	I/O
• Better	readability	for	your	SQL
A	handy	and	fast	helper	for	common	subqueries
35
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 36
WITH active_users (id, http_session, last_active) AS
(
SELECT id, http_session_id, last_active_tms
FROM all_active_users
WHERE …
)
SELECT a.id, a.http_session, p.first_name, p.last_name, p.email_address
FROM active_users a
LEFT JOIN personal_accounts p ON p.email_address = a.id
LEFT JOIN business_accounts b ON b.business_id = a.id;
KISS	- Common	table	expression
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 37
WITH cte (n) AS
(
SELECT 1 FROM dual
UNION ALL
SELECT n + 1 FROM cte WHERE n < 5
)
SELECT * FROM cte;
N
----------
1
2
3
4
5
KISS	- Common	table	expression
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Parallel	queries
Confidential	– Oracle	Internal/Restricted/Highly	Restricted 38
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 39
SELECT id, create_date, first_name, last_name, email_address, http_session_id,
last_active, avatar, twitter_handle, github_profile, stars
FROM super_big_table
INNER JOIN another_super_big_table
ON (a.id = http_session_id)
WHERE id IN (SELECT id FROM active_logons_past_24_hours)
AND stars > 25
AND last_active > SYSDATE – 356;
SELECT /*+ PARALLEL */ id, create_date, first_name, last_name, email_address,
http_session_id, last_active, avatar, twitter_handle, github_profile, stars
FROM super_big_table
INNER JOIN another_super_big_table
ON (a.id = http_session_id)
WHERE id IN (SELECT id FROM active_logons_past_24_hours)
AND stars > 25
AND last_active > SYSDATE – 356;
Parallel	queries
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Parallel	queries
• Queries	by	default	run	single	threaded
– One	process/thread
– Running	on	one	CPU
– Doing	all	the	I/O	by	itself
• Long	running	and/or	I/O	intensive	queries	might	benefit	from	parallelization
• Easy	to	enable
• BUT not	a	universal	performance	booster!
Use	the	full	power	of	the	database	server
40
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Explain	plans
Confidential	– Oracle	Internal/Restricted/Highly	Restricted 41
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 42
EXPLAIN PLAN FOR
<your SQL statement here>
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());
Explain	plans
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 43
EXPLAIN PLAN FOR
WITH active_users (id, http_session, last_active) AS
(
SELECT id, http_session_id, last_active_tms
FROM all_active_users
WHERE …
)
SELECT a.id, a.http_session, p.first_name, p.last_name, p.email_address
FROM active_users a
LEFT JOIN personal_accounts p ON p.email_address = a.id
LEFT JOIN business_accounts b ON b.business_id = a.id;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());
Explain	plans
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 44
Plan hash value: 1707074151
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100M| 39G| 1169 (100)| 00:00:01 |
|* 1 | HASH JOIN RIGHT OUTER | | 100M| 39G| 1169 (100)| 00:00:01 |
| 2 | INDEX FULL SCAN | BA_IDX01 | 1 | 13 | 0 (0)| 00:00:01 |
|* 3 | HASH JOIN RIGHT OUTER| | 100M| 38G| 854 (100)| 00:00:01 |
| 4 | TABLE ACCESS FULL | PERSONAL_ACCOUNTS | 1 | 385 | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | ALL_ACTIVE_USERS | 100M| 2479M| 537 (100)| 00:00:01 |
-------------------------------------------------------------------------------------------
Explain	plans
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
Explain	plans
• Explain	plans	give	you	the	steps	that	are	executed	by	the	DB
• It	allows	you	to	spot	outliers	that	might	need	further	tuning
• Generating	explain	plans	is	easy
Look	beyond	your	SQL	statement
45
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
SQL	wait	events
Confidential	– Oracle	Internal/Restricted/Highly	Restricted 46
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		| 47
SQL> SELECT sid, event, wait_class
FROM v$session_wait
WHERE wait_class != 'Idle';
SID EVENT WAIT_CLASS
------- --------------------- ----------
265 direct path read User I/O
269 enq: TM - contention Application
274 direct path read User I/O
291 direct path read User I/O
293 enq: TM - contention Application
294 enq: TM – contention Application
296 enq: TM – contention Application
302 enq: TM - contention Application
SQL	wait	events
Copyright	©	2018,	Oracle	and/or	its	affiliates.	All	rights	reserved.		|
SQL	wait	events
• Every	SQL	statement	can	be	traced	on	its	activity
• Every	SQL	statement	records	on	what	it’s	waiting	for
• You	can	usually	just	query	that	information
Looking	beyond	the	SQL	statement
48

Top 10 SQL Performance tips & tricks for Java Developers