I am trying to figure out why with the query below, adding the last join takes so much longer.
If I run it with the last join commented out ( level 3) it runs in less than a second. Adding the 3rd join in it takes 20+ seconds.
It appears that the last join uses materialize rather than a nested loop left join but why? Doing this adds a significant amount of time.
Query
select
t4.head_id
from
table_links t4
left join -- level 1
table_links t5 on t5.head_id = t4.tail_id and t5.linkdef_id = -10 and t5.latest = 1
left join -- level 2
table_links t6 on t6.head_id = t5.tail_id and t6.linkdef_id = -10 AND t6.latest = 1
left join -- level 3
table_links t7 on t7.head_id = t6.tail_id and t7.linkdef_id = -10 AND t7.latest = 1
where
t4.head_id = '1141399'::integer and t4.linkdef_id = -10 AND t4.latest = 1
System Version
PostgreSQL 9.5.17 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Table and index definition
CREATE TABLE public.table_links
(
link_id integer NOT NULL,
linkdef_id integer NOT NULL,
hierarchical integer,
latest integer NOT NULL,
version integer NOT NULL,
suppressed integer NOT NULL,
version_mode integer,
head_id integer NOT NULL,
tail_id integer NOT NULL,
orig_tail_name character varying(64) COLLATE pg_catalog."default"
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
CREATE INDEX idx_table_links
ON public.table_links USING btree
(head_id)
TABLESPACE pg_default WHERE linkdef_id = '-10'::integer AND latest = 1
;
Explain Analyze
"Merge Left Join (cost=925531.10..1076126.36 rows=9095384 width=4) (actual time=16950.609..20162.688 rows=513 loops=1)"
" Merge Cond: (t6.tail_id = t7.head_id)"
" -> Sort (cost=53175.43..53587.15 rows=164689 width=8) (actual time=31.134..31.230 rows=335 loops=1)"
" Sort Key: t6.tail_id"
" Sort Method: quicksort Memory: 40kB"
" -> Nested Loop Left Join (cost=1.30..38905.64 rows=164689 width=8) (actual time=1.331..30.969 rows=335 loops=1)"
" -> Nested Loop Left Join (cost=0.86..1124.63 rows=2982 width=8) (actual time=0.760..6.643 rows=125 loops=1)"
" -> Index Scan using idx_table_links on table_links t4 (cost=0.43..19.80 rows=54 width=8) (actual time=0.404..0.411 rows=13 loops=1)"
" Index Cond: (head_id = 1141399)"
" -> Index Scan using idx_table_links on table_links t5 (cost=0.43..19.91 rows=55 width=8) (actual time=0.403..0.474 rows=9 loops=13)"
" Index Cond: (head_id = t4.tail_id)"
" -> Index Scan using idx_table_links on table_links t6 (cost=0.43..12.12 rows=55 width=8) (actual time=0.173..0.192 rows=2 loops=125)"
" Index Cond: (head_id = t5.tail_id)"
" -> Materialize (cost=872355.67..899037.78 rows=5336422 width=4) (actual time=16640.144..19616.065 rows=5816034 loops=1)"
" -> Sort (cost=872355.67..885696.72 rows=5336422 width=4) (actual time=16640.141..19079.265 rows=5816012 loops=1)"
" Sort Key: t7.head_id"
" Sort Method: external merge Disk: 81168kB"
" -> Bitmap Heap Scan on table_links t7 (cost=44115.15..233344.48 rows=5336422 width=4) (actual time=681.658..7882.374 rows=5935232 loops=1)"
" Recheck Cond: ((linkdef_id = '-10'::integer) AND (latest = 1))"
" Heap Blocks: exact=101723"
" -> Bitmap Index Scan on idx_table_links (cost=0.00..42781.04 rows=5336422 width=0) (actual time=658.355..658.355 rows=5935232 loops=1)"
"Planning time: 1.496 ms"
"Execution time: 20173.394 ms"
UPDATE
Adding the index requested by @LuisAlbertoBarandiaran decreased the time to 2 seconds which is fabulous but I do wonder why it still isn't the same.
Added this index
CREATE INDEX idx_table_links2 ON public.table_links USING btree (head_id, tail_id, linkdef_id, latest ) TABLESPACE pg_default ;
Explain Analyze
"Merge Left Join (cost=26338.58..398495.40 rows=9095384 width=4) (actual time=175.809..2034.940 rows=513 loops=1)"
" Merge Cond: (t6.tail_id = t7.head_id)"
" -> Sort (cost=26338.02..26749.75 rows=164689 width=8) (actual time=0.405..0.480 rows=335 loops=1)"
" Sort Key: t6.tail_id"
" Sort Method: quicksort Memory: 40kB"
" -> Nested Loop Left Join (cost=1.68..12068.23 rows=164689 width=8) (actual time=0.015..0.370 rows=335 loops=1)"
" -> Nested Loop Left Join (cost=1.12..241.95 rows=2982 width=8) (actual time=0.012..0.069 rows=125 loops=1)"
" -> Index Only Scan using idx_table_links2 on table_links t4 (cost=0.56..3.81 rows=54 width=8) (actual time=0.006..0.007 rows=13 loops=1)"
" Index Cond: ((head_id = 1141399) AND (linkdef_id = '-10'::integer) AND (latest = 1))"
" Heap Fetches: 0"
" -> Index Only Scan using idx_table_links2 on table_links t5 (cost=0.56..3.86 rows=55 width=8) (actual time=0.003..0.004 rows=9 loops=13)"
" Index Cond: ((head_id = t4.tail_id) AND (linkdef_id = '-10'::integer) AND (latest = 1))"
" Heap Fetches: 0"
" -> Index Only Scan using idx_table_links2 on table_links t6 (cost=0.56..3.42 rows=55 width=8) (actual time=0.002..0.002 rows=2 loops=125)"
" Index Cond: ((head_id = t5.tail_id) AND (linkdef_id = '-10'::integer) AND (latest = 1))"
" Heap Fetches: 0"
" -> Materialize (cost=0.56..248244.23 rows=5336422 width=4) (actual time=0.012..1537.433 rows=5816034 loops=1)"
" -> Index Only Scan using idx_table_links2 on table_links t7 (cost=0.56..234903.17 rows=5336422 width=4) (actual time=0.012..874.000 rows=5816012 loops=1)"
" Index Cond: ((linkdef_id = '-10'::integer) AND (latest = 1))"
" Heap Fetches: 0"
"Planning time: 0.737 ms"
"Execution time: 2035.066 ms"
(linkdef_id, latest, head_id, tail_id)
. But please post the whole query, yourSELECT
list is incomplete.