Optimization of SQL Queries in Firebird: Dmitry Yemanov, Firebird Alexey Kovyazin, Ibsurgeon
Optimization of SQL Queries in Firebird: Dmitry Yemanov, Firebird Alexey Kovyazin, Ibsurgeon
QUERIES IN FIREBIRD
Dmitry Yemanov, Firebird
Alexey Kovyazin, IBSurgeon
Firebird Conference 2019
Berlin, 17-19 October
PART 1: EXPLAINED
PLANS
Explained plans in Firebird 3
• Run ISQL
Select Expression
-> Sort (record length: 484, key length: 8)
-> Filter
-> Table "RDB$RELATIONS" Access By ID
-> Bitmap
-> Index "RDB$INDEX_0" Range Scan (lower bound: 1/1)
Old and new plans
SELECT * FROM RDB$RELATIONS
WHERE RDB$RELATION_NAME > :a
ORDER BY RDB$SYSTEM_FLAG
Select Expression
-> Sort (record length: 484, key length: 8)
-> Filter
-> Table "RDB$RELATIONS" Access By ID
-> Bitmap
-> Index "RDB$INDEX_0" Range Scan (lower bound: 1/1)
Old and new plans
• SELECT * FROM RDB$RELATIONS
• WHERE RDB$RELATION_NAME > :a
• ORDER BY RDB$SYSTEM_FLAG
Select Expression
-> Sort (record length: 484, key length: 8)
-> Filter
-> Table "RDB$RELATIONS" Access By ID
-> Bitmap
-> Index "RDB$INDEX_0" Range Scan (lower bound: 1/1)
Old and new plans
• SELECT * FROM RDB$RELATIONS
• WHERE RDB$RELATION_NAME > :a
• ORDER BY RDB$SYSTEM_FLAG
Select Expression
-> Sort (record length: 484, key length: 8)
-> Filter
-> Table "RDB$RELATIONS" Access By ID
-> Bitmap
-> Index "RDB$INDEX_0" Range Scan (lower bound: 1/1)
Index Range Scan options
• Lower bound - >, >= Index
• Full match - =
• For non-unique indices
create index ixname on T1(name1)
Select Expression
-> Filter
-> Table "T1" Access By ID
-> Bitmap
-> Index "IXNAME" Range Scan (lower bound: 1/1)
NAME1
==============================
Emmanuel
Dath
Example of Upper Bound
SQL> select * from t1 where name1<'Carlos';
Select Expression
-> Filter
-> Table "T1" Access By ID
-> Bitmap
-> Index "IXNAME" Range Scan (upper bound: 1/1)
NAME1
==============================
Carl
Alexandre
Boris
Example of Full match
SQL> select * from t1 where name1='Carlos';
Select Expression
-> Filter
-> Table "T1" Access By ID
-> Bitmap
-> Index "IXNAME" Range Scan (full match)
NAME1
==============================
Carlos
How data are stored in the index
Root Page Pointer Page Keys (Leaf page) Data Pages
Alexandre r55
Boris r28 R15
R28
Carl r44
R43
Carlos r68 R44
R55
Darth r15 R68
Emmanuel r43
Composite indices
CREATE INDEX BY_AB ON MYTABLE (A, B)
SELECT * FROM MYTABLE
WHERE A = 1 AND B > 5
PLAN (MYTABLE INDEX (BY_AB))
•A B • The second column depends on
1 1
1 2 the first column.
1 3
2 1 • where A > 1 and B > 5 - it will
2 2 not use the second condition
2 3
3 1
• where A = 1 and B = 5 it will use
both conditions
Index "RDB$INDEX_0" Range Scan (lower
bound: 1/1)
• For composite indices > 1.
• 1st value – how many segments is used
• 2nd value – total number of segments
• 1/3 – one of 3 segments is used (bad)
• 2/3 – 2 of 3 are used (better)
• 3/3 – all segments are used (best)
• In case of 1/3, 2/3 – better use 1-segment indices
2 indices together: Index bitmap
• select * from employee
where emp_no > 5 and last_name > 'b‘
rdb$primary7 namex
employee
emp_no > 5
last_name > b
AND, OR
2 indices together: index bitmap
select * from a
where name > 'b' and a.id > 5
PLAN (A INDEX (ANAME, PK_A))
Select Expression
-> Filter
-> Table "A" Access By ID
-> Bitmap And
-> Bitmap
-> Index "ANAME" Range Scan (lower bound: 1/1)
-> Bitmap
-> Index "PK_A" Range Scan (lower bound: 1/1)
Full Scan or (TABLE ORDER INDEX)
select * from employee order by last_name;
PLAN (EMPLOYEE ORDER NAMEX)
Select Expression
-> Table "EMPLOYEE" Access By ID
-> Index "NAMEX" Full Scan
Find the key corresponding to the
condition
Root Page Pointer Page Keys (Leaf page) Data Pages
Memory +
temporary file Output
sorting
ORDER vs SORT
PLAN (TABLE ORDER A) PLAN SORT ((A
Execute time = 1m 12s NATURAL))
Buffers = 2 048 Execute time = 35 s
Reads = 3 627 028 Buffers = 2 048
Fetches = 32 224 797 Reads = 119 915
Fetches 14 767 524
Execute time = 27s 518ms
Buffers = 150 000
Reads = 124 663
Fetches = 32 224 797
Select Expression
-> Nested Loop Join (inner)
-> Table "DEPARTMENT" as "D" Full Scan
-> Filter
-> Table "EMPLOYEE" as "E" Access By ID
-> Bitmap
-> Index "RDB$FOREIGN8" Range Scan (full match)
Join plan for LEFT/RIGHT
select e.emp_no, d.department from employee e
left join department d on (e.dept_no = d.dept_no);
Select Expression
-> Nested Loop Join (outer)
-> Table "EMPLOYEE" as "E" Full Scan
-> Filter
-> Table "DEPARTMENT" as "D" Access By ID
-> Bitmap
-> Index "RDB$PRIMARY5" Unique Scan
INNER JOIN + LEFT JOIN
select e.emp_no, d.department
from employee e inner join employee_project p
on (e.emp_no = ep.emp_no)
left join department d
on (e.dept_no = d.dept_no);
Old plan
PLAN JOIN (JOIN (EP NATURAL, E INDEX (RDB$PRIMARY7)),
D INDEX (RDB$PRIMARY5))
PLAN for INNER JOIN+LEFT
Select Expression
-> Nested Loop Join (outer)
-> Nested Loop Join (inner)
-> Table "EMPLOYEE_PROJECT" as "EP" Full Scan
-> Filter
-> Table "EMPLOYEE" as "E" Access By ID
-> Bitmap
-> Index "RDB$PRIMARY7" Unique Scan
-> Filter
-> Table "DEPARTMENT" as "D" Access By ID
-> Bitmap
-> Index "RDB$PRIMARY5" Unique Scan
HASH JOIN
select e.* from employee e, employee_project p
where e.emp_no+0 = p.emp_no+0
PLAN HASH (E NATURAL, P NATURAL)
Select Expression
-> Filter
-> Hash Join (inner)
-> Table "EMPLOYEE" as "E" Full Scan
-> Record Buffer (record length: 25)
-> Table "EMPLOYEE_PROJECT" as "P" Full Scan
Summary for new plans
• Natural • Sort
• FullScan • JOIN
• Index • Inner
• RangeScan • Outer
• FullScan • Hash
END OF PART 1