- Declarative: specify what you want, not how to get it
- Declarative statements create execution plans
- Cost: number of scanned rows
- Index reduces scanned rows
- Index are ordered
- B-tree: used for equality and range queries
- Hash: used for equality
- Bitmap: used for inclusion
- Specialized: geo-spatial or custom
- Nested loop join: compare all rows in both tables
- Hash join: compute hash value of key and join
- Sort merge join: sort both table and join
- Sort data in multiple sub-tables, known as partitions
- Improve queries
select * from staff;
SELECT * from staff
EXPLAIN SELECT * FROM STAFF
EXPLAIN ANALYZE select * from staff
EXPLAIN ANALYZE select last_name from staff
SELECT * FROM staff WHERE salary > 75000
EXPLAIN SELECT * FROM staff where salary > 75000
EXPLAIN SELECT * FROM staff
EXPLAIN ANALYZE SELECT * FROM staff where salary > 75000
CREATE INDEX idx_staff_salary ON staff(salary)
EXPLAIN SELECT * FROM staff
EXPLAIN ANALYZE SELECT * FROM staff where salary > 75000
EXPLAIN ANALYZE SELECT * FROM staff where salary > 150000
- Speed up data access
- Enforce constraints
- Ordered
- Smaller than tables
- Reduce table scans
- Duplicates data
- Different organization than table
- B-tree
- Bitmap
- Hash
- Special purpose
- Balanced tree
- Most common
- High cardinality
- Time based on tree depth
select count(*) from staff
select * from staff where email = 'bphillips5@time.com'
explain select * from staff where email = 'bphillips5@time.com'
create index idx_staff_email on staff(email);
explain select * from staff where email = 'bphillips5@time.com'
drop index idx_staff_email;
- Boolean operations
- Small number of possible values in a column
- Time based on bitwise operation to perform
select distinct job_title from staff order by job_title
select * from staff where job_title = 'operator'
create index idx_staff_job_title on staff(job_title);
explain select * from staff where job_title = 'operator'
- Maps data length to fixed string
- Virtually unique
- Input changes produce new hash
- Only for equality
- Smaller than B-tree
- As fast as B-tree
create index idx_staff_email on staff using hash (email)
explain select * from staff
where email = 'bphillips5@time.com'
- Probabilistic and space efficient
- Lossy reppresentation
- False positive
- Arbitrary combinations
- B-tree is faster but bigger
- GIS: generalize search tree
- SP_GIS: space partitioned gis
- GIN: text indexing
- BRIN: block range index
- Inner: all matching rows from both tables
- Left outer: all rows from left table and matching ones from right
- Right outer: all rows from right table and matching ones from left
- Full outer: all rows from both tables
- Works for all types of join
- 2 loops
- Outer loop: driver (it runs once)
- Inner loop: join (it runs for each row)
SELECT
s.id, s.last_name, s.job_title, cr.country
FROM
staff s
INNER JOIN
company_regions cr
ON
s.region_id = cr.region_id
EXPLAIN SELECT
s.id, s.last_name, s.job_title, cr.country
FROM
staff s
INNER JOIN
company_regions cr
ON
s.region_id = cr.region_id
set enable_nestloop=true;
set enable_hashjoin=false;
set enable_mergejoin=false;
SELECT
s.id, s.last_name, s.job_title, cr.country
FROM
staff s
INNER JOIN
company_regions cr
ON
s.region_id = cr.region_id
EXPLAIN SELECT
s.id, s.last_name, s.job_title, cr.country
FROM
staff s
INNER JOIN
company_regions cr
ON
s.region_id = cr.region_id
- Function that creates data for mapping data
- Can act as an index for fetching that data
- Virtually unique
- It uses the smaller table and it stores its values
- Equality only
- Time based on table size
- Fast lookup
set enable_nestloop=false;
set enable_hashjoin=true;
set enable_mergejoin=false;
EXPLAIN SELECT
s.id, s.last_name, s.job_title, cr.country
FROM
staff s
INNER JOIN
company_regions cr
ON
s.region_id = cr.region_id
- Sort merge
- First step i sorting both tables
- Sort reduces the number of checks
- Equality only
- Time based on table size
- Large tables join
set enable_nestloop=false;
set enable_hashjoin=false;
set enable_mergejoin=true;
EXPLAIN SELECT
s.id, s.last_name, s.job_title, cr.country
FROM
staff s
INNER JOIN
company_regions cr
ON
s.region_id = cr.region_id
SELECT s.id, s.last_name, s.department,
(SELECT
company_regions
FROM
company_regions cr)
WHERE
cr.region_id = s.region_staff s
- Same logical outcome
- More than one way to express same thing
- Join more efficient
- Choose more clarity one in code
- Large tables could lead to bad query performance
- Split tables by rows into partitions
- Treat each partition like a table
- Limit scan on subsets
- Local index for each partition
- Efficient add and delete operations
- Data warehouse
- Timeseries
- Naturally driven
- Separates columns into multiple tables
- Keep frequently queried columns togheter
- Same primary key
- More rows for data block
- Global indexes
- Reduce I/O
- Data analytics
- Tech data
- Horizontal partitioning
- Partition on non overlapping keys
- Partition by dates
- Numeric range
- Alphabetic range
- Partition key determines which partition
- Min and max value for each partition
- Each partition has its own constraints
- Queries latest data
- Comparative queries
- Report with ranges
CREATE TABLE iot_measurement
( location_id int not null,
measure_date timestamp not null,
temp_celcius int,
rel_humidity_pct int)
PARTITION BY RANGE (measure_date);
CREATE TABLE iot_measurement_wk1_2024 PARTITION OF iot_measurement
FOR VALUES FROM ('2024-01-01') TO ('2024-01-08');
CREATE TABLE iot_measurement_wk2_2024 PARTITION OF iot_measurement
FOR VALUES FROM ('2024-01-08') TO ('2024-01-15');
CREATE TABLE iot_measurement_wk3_2024 PARTITION OF iot_measurement
FOR VALUES FROM ('2024-01-15') TO ('2024-01-22');
- Horizontal partitioning
- On non overlapping keys
- On list of values
- Partition key determines which partition
- Partition bounds determines the portion on values
- Each partition has its own constraints
create table products
(prod_id int not null,
prod_name text not null,
prod_descr text not null,
prod_category text)
partition by list (prod_category);
create table product_clothing partition of products
for values in (‘casual_clothing’, ‘business_attire’, ‘formal_clothing);
create table product_electronics partition of products
for values in (‘mobile_phones’, ‘tablets’, ‘laptop_computers’);
create table product_kitches partition of products
for values in (‘food_processor, ‘cutlery’, ‘blenders’);
- Horizontal partitioning type
- Hash as partition key
create table customer_interaction
(ci_id int not null,
ci_url text not null,
time_at_url int not null,
click_sequence int not null)
partition by hash(ci_id);
create table customer_interactions_1 partition of customer_interaction
for values with (modulus 5 remainder 0);
create table customer_interactions_2 partition of customer_interaction
for values with (modulus 5 remainder 1);
create table customer_interactions_3 partition of customer_interaction
for values with (modulus 5 remainder 2);
create table customer_interactions_4 partition of customer_interaction
for values with (modulus 5 remainder 3);
create table customer_interactions_5 partition of customer_interaction
for values with (modulus 5 remainder 4);
- Pre computed queries
- Join and store results
- Apply other operations
- Duplicates data
- Potential inconsistency
create materialized view mv_staff as
select
s.last_name, s.department, s.job_title,
cr.company_regions
from
staff s
inner join
company_regioins cr
on
s.region_id = cr.region_id
select * from mv_staff
refresh materialized view mv_staff;
REINDEX INDEX INDEX_NAME
REINDEX TABLE TABLE_NAME
REINDEX SCHEMA SCHEMA_NAME
pg_stat_statements
auto_explain.min_duration
auto_explain.log_nested_statements
SELECT * from PG_STATS;
- Computed once (default)
- Muliple teimes in line
with cte items AS (...)
select ... FROM items WHERE ...
with cte items AS MATERIALIZED (...)
select ... FROM items WHERE ...
- Suggestion to quesry builder
SET command
SET enabled_nestloop=on
- Optimizer detects parallel queries
- Executes them in parallel
- Gathers results
- May be less efficient
- B-trees only
GATHER
GATHER MERGE
- Stores results in order to reuse them
- Explain plan: hits from cache, read from disk
- Shared memoru buffer: 128Mb by default