Strange pgsql query performance - sql

I have a relation like this
R ( EDGE INTEGER, DIHEDRAL INTEGER, FACE INTEGER , VALENCY INTEGER)
I tested twice, 64 rows table R and 128 rows table R. but the simpler one takes much more time than the second one. The explain is like below (It shows error on explain.depesz.com). Could anyone help me to check why? thanks.
plan for 64 rows:
HashAggregate (cost=260.16..260.17 rows=1 width=12) (actual rows=64 loops=1)
-> Nested Loop (cost=89.44..260.15 rows=1 width=12) (actual rows=256 loops=1)
Join Filter: ((f1.face < f2.face) AND (e3.edge <> f1.edge) AND (e4.edge <> e3.edge) AND (f1.edge = f2.edge) AND (f1.face =
e3.face))
Rows Removed by Join Filter: 142606080
-> Nested Loop (cost=41.91..167.59 rows=1 width=16) (actual rows=557056 loops=1)
-> Nested Loop (cost=41.91..125.71 rows=1 width=8) (actual rows=256 loops=1)
Join Filter: ((e5.edge <> f2.edge) AND (e5.edge <> e2.edge) AND (e2.face = e5.face))
Rows Removed by Join Filter: 1113856
-> Hash Join (cost=41.91..83.73 rows=1 width=16) (actual rows=512 loops=1)
Hash Cond: (f2.face = e2.face)
Join Filter: (e2.edge <> f2.edge)
Rows Removed by Join Filter: 256
-> Seq Scan on r f2 (cost=0.00..41.76 rows=12 width=8) (actual rows=384 loops=1)
Filter: (valency = 3)
Rows Removed by Filter: 1920
-> Hash (cost=41.76..41.76 rows=12 width=8) (actual rows=2176 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 85kB
-> Seq Scan on r e2 (cost=0.00..41.76 rows=12 width=8) (actual rows=2176 loops=1)
Filter: (dihedral = 2)
Rows Removed by Filter: 128
-> Seq Scan on r e5 (cost=0.00..41.76 rows=12 width=8) (actual rows=2176 loops=512)
Filter: (dihedral = 2)
Rows Removed by Filter: 128
-> Seq Scan on r e3 (cost=0.00..41.76 rows=12 width=8) (actual rows=2176 loops=256)
Filter: (dihedral = 2)
Rows Removed by Filter: 128
-> Hash Join (cost=47.53..92.32 rows=11 width=16) (actual rows=256 loops=557056)
Hash Cond: (e4.face = f1.face)
Join Filter: (e4.edge <> f1.edge)
Rows Removed by Join Filter: 128
-> Seq Scan on r e4 (cost=0.00..36.01 rows=2301 width=8) (actual rows=2304 loops=557056)
-> Hash (cost=47.52..47.52 rows=1 width=8) (actual rows=128 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 5kB
-> Seq Scan on r f1 (cost=0.00..47.52 rows=1 width=8) (actual rows=128 loops=1)
Filter: ((valency = 3) AND (dihedral = 1))
Rows Removed by Filter: 2176
Total runtime: 159268.541 ms
(37 rows)
plan for 128 rows
HashAggregate (cost=501.28..501.29 rows=1 width=12) (actual rows=128 loops=1)
-> Nested Loop (cost=171.98..501.27 rows=2 width=12) (actual rows=512 loops=1)
Join Filter: ((e3.edge <> f1.edge) AND (e4.edge <> e3.edge) AND (f1.face = e3.face))
Rows Removed by Join Filter: 2227712
-> Seq Scan on r e3 (cost=0.00..80.31 rows=22 width=8) (actual rows=4352 loops=1)
Filter: (dihedral = 2)
Rows Removed by Filter: 256
-> Materialize (cost=171.98..420.08 rows=2 width=20) (actual rows=512 loops=4352)
-> Nested Loop (cost=171.98..420.07 rows=2 width=20) (actual rows=512 loops=1)
Join Filter: ((f1.face < f2.face) AND (f1.edge = f2.edge))
Rows Removed by Join Filter: 261632
-> Nested Loop (cost=80.59..242.23 rows=1 width=8) (actual rows=512 loops=1)
Join Filter: ((e5.edge <> f2.edge) AND (e5.edge <> e2.edge) AND (e2.face = e5.face))
Rows Removed by Join Filter: 4455936
-> Seq Scan on r e5 (cost=0.00..80.31 rows=22 width=8) (actual rows=4352 loops=1)
Filter: (dihedral = 2)
Rows Removed by Filter: 256
-> Materialize (cost=80.59..161.05 rows=2 width=16) (actual rows=1024 loops=4352)
-> Hash Join (cost=80.59..161.04 rows=2 width=16) (actual rows=1024 loops=1)
Hash Cond: (f2.face = e2.face)
Join Filter: (e2.edge <> f2.edge)
Rows Removed by Join Filter: 512
-> Seq Scan on r f2 (cost=0.00..80.31 rows=22 width=8) (actual rows=768 loops=1)
Filter: (valency = 3)
Rows Removed by Filter: 3840
-> Hash (cost=80.31..80.31 rows=22 width=8) (actual rows=4352 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 170kB
-> Seq Scan on r e2 (cost=0.00..80.31 rows=22 width=8) (actual rows=4352 loops=1)
Filter: (dihedral = 2)
Rows Removed by Filter: 256
-> Hash Join (cost=91.39..177.51 rows=22 width=16) (actual rows=512 loops=512)
Hash Cond: (e4.face = f1.face)
Join Filter: (e4.edge <> f1.edge)
Rows Removed by Join Filter: 256
-> Seq Scan on r e4 (cost=0.00..69.25 rows=4425 width=8) (actual rows=4608 loops=512)
-> Hash (cost=91.38..91.38 rows=1 width=8) (actual rows=256 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 10kB
-> Seq Scan on r f1 (cost=0.00..91.38 rows=1 width=8) (actual rows=256 loops=1)
Filter: ((valency = 3) AND (dihedral = 1))
Rows Removed by Filter: 4352
Total runtime: 1262.761 ms
(41 rows)

The query planner uses statistics on row counts/index sizes/etc. to estimate how to get the best performance out of a query. A bulk insertion of rows immediately followed by a query may not show best performance, because these statistics may be out of date.
To make sure the planner makes informed choices, you need to issue a call to ANALYZE prior to running your EXPLAIN query.
In your specific scenario, chances are the planner made a bad choice in the first case (the 64 rows) and a good one in the second case (the 128 rows).

Related

Need to improve count performance in PostgreSQL for this query

I have this query in PostgreSQL:
SELECT COUNT("contacts"."id")
FROM "contacts"
INNER JOIN "phone_numbers" ON "phone_numbers"."id" = "contacts"."phone_number_id"
INNER JOIN "companies" ON "companies"."id" = "contacts"."company_id"
WHERE (
(
(
CAST("phone_numbers"."value" AS VARCHAR) ILIKE '%a%'
OR CAST("contacts"."first_name" AS VARCHAR) ILIKE '%a%'
)
OR CAST("contacts"."last_name" AS VARCHAR) ILIKE '%a%'
)
OR CAST("companies"."name" AS VARCHAR) ILIKE '%a%'
)
When I run the query it is taking 19secs to run. I need to improve the performance.
Note: I already have the index for the columns.
EXPLAIN ANALYZE report
Finalize Aggregate (cost=209076.49..209076.54 rows=1 width=8) (actual time=6117.381..6646.477 rows=1 loops=1)
-> Gather (cost=209076.42..209076.48 rows=4 width=8) (actual time=6117.370..6646.473 rows=5 loops=1)
Workers Planned: 4
Workers Launched: 4
-> Partial Aggregate (cost=209066.42..209066.47 rows=1 width=8) (actual time=5952.710..5952.723 rows=1 loops=5)
-> Hash Join (cost=137685.37..208438.42 rows=251200 width=8) (actual time=3007.055..5945.571 rows=39193 loops=5)
Hash Cond: (contacts.company_id = companies.id)
Join Filter: (((phone_numbers.value)::text ~~* '%as%'::text) OR ((contacts.first_name)::text ~~* '%as%'::text) OR ((contacts.last_name)::text ~~* '%as%'::text) OR ((companies.name)::text ~~* '%as%'::text))
Rows Removed by Join Filter: 763817
-> Parallel Hash Join (cost=137684.86..201964.34 rows=1003781 width=41) (actual time=3006.633..4596.987 rows=803010 loops=5)
Hash Cond: (contacts.phone_number_id = phone_numbers.id)
-> Parallel Seq Scan on contacts (cost=0.00..59316.85 rows=1003781 width=37) (actual time=11.032..681.124 rows=803010 loops=5)
-> Parallel Hash (cost=68914.22..68914.22 rows=1295458 width=20) (actual time=1632.770..1632.770 rows=803184 loops=5)
Buckets: 65536 Batches: 64 Memory Usage: 4032kB
-> Parallel Seq Scan on phone_numbers (cost=0.00..68914.22 rows=1295458 width=20) (actual time=10.780..1202.242 rows=803184 loops=5)
-> Hash (cost=0.30..0.30 rows=4 width=40) (actual time=0.258..0.258 rows=4 loops=5)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Seq Scan on companies (cost=0.00..0.30 rows=4 width=40) (actual time=0.247..0.248 rows=4 loops=5)
Planning Time: 1.895 ms
Execution Time: 6646.558 ms
Please help me on this performance issue.
I tried FUNCTION row_count_estimate (query text) and it is not giving the exact count.
Solution Tried:
I tried the Robert solution and got 16 Secs to run
My Query is:
SELECT Count(id) AS id
FROM (
SELECT contacts.id AS id
FROM contacts
WHERE (
contacts.last_name ilike '%as%')
OR (
contacts.last_name ilike '%as%')
UNION
SELECT contacts.id AS id
FROM contacts
WHERE contacts.phone_number_id IN
(
SELECT phone_numbers.id AS phone_number_id
FROM phone_numbers
WHERE phone_numbers.value ilike '%as%')
UNION
SELECT contacts.id AS id
FROM contacts
WHERE contacts.company_id IN
(
SELECT companies.id AS company_id
FROM companies
WHERE companies.name ilike '%as%' )) AS ID
Report:
Aggregate (cost=395890.08..395890.13 rows=1 width=8) (actual time=5942.601..5942.667 rows=1 loops=1)
-> Unique (cost=332446.76..337963.57 rows=1103362 width=8) (actual time=5929.800..5939.658 rows=101989 loops=1)
-> Sort (cost=332446.76..335205.17 rows=1103362 width=8) (actual time=5929.799..5933.823 rows=101989 loops=1)
Sort Key: contacts.id
Sort Method: external merge Disk: 1808kB
-> Append (cost=10.00..220843.02 rows=1103362 width=8) (actual time=1.158..5900.926 rows=101989 loops=1)
-> Gather (cost=10.00..61935.48 rows=99179 width=8) (actual time=1.158..569.412 rows=101989 loops=1)
Workers Planned: 4
Workers Launched: 4
-> Parallel Seq Scan on contacts (cost=0.00..61826.30 rows=24795 width=8) (actual time=0.446..477.276 rows=20398 loops=5)
Filter: ((last_name)::text ~~* '%as%'::text)
Rows Removed by Filter: 782612
-> Nested Loop (cost=0.84..359.91 rows=402 width=8) (actual time=5292.088..5292.089 rows=0 loops=1)
-> Index Scan using idx_phone_value on phone_numbers (cost=0.41..64.13 rows=402 width=8) (actual time=5292.087..5292.087 rows=0 loops=1)
Index Cond: ((value)::text ~~* '%as%'::text)
Rows Removed by Index Recheck: 4015921
-> Index Scan using index_contacts_on_phone_number_id on contacts contacts_1 (cost=0.43..0.69 rows=1 width=16) (never executed)
Index Cond: (phone_number_id = phone_numbers.id)
-> Gather (cost=10.36..75795.48 rows=1003781 width=8) (actual time=26.298..26.331 rows=0 loops=1)
Workers Planned: 4
Workers Launched: 4
-> Hash Join (cost=0.36..74781.70 rows=250945 width=8) (actual time=3.758..3.758 rows=0 loops=5)
Hash Cond: (contacts_2.company_id = companies.id)
-> Parallel Seq Scan on contacts contacts_2 (cost=0.00..59316.85 rows=1003781 width=16) (actual time=0.128..0.128 rows=1 loops=5)
-> Hash (cost=0.31..0.31 rows=1 width=8) (actual time=0.726..0.727 rows=0 loops=5)
Buckets: 1024 Batches: 1 Memory Usage: 8kB
-> Seq Scan on companies (cost=0.00..0.31 rows=1 width=8) (actual time=0.726..0.726 rows=0 loops=5)
Filter: ((name)::text ~~* '%as%'::text)
Rows Removed by Filter: 4
Planning Time: 0.846 ms
Execution Time: 5948.330 ms
I tried the below also:
EXPLAIN ANALYZE SELECT
count(id) AS id
FROM
(SELECT
contacts.id AS id
FROM
contacts
WHERE
(
position('as' in LOWER(last_name)) > 0
)
UNION
SELECT
contacts.id AS id
FROM
contacts
WHERE
EXISTS (
SELECT
1
FROM
phone_numbers
WHERE
(
position('as' in LOWER(phone_numbers.value)) > 0
)
AND (
contacts.phone_number_id = phone_numbers.id
)
)
UNION
SELECT
contacts.id AS id
FROM
contacts
WHERE
EXISTS (
SELECT
1
FROM
companies
WHERE
(
position('as' in LOWER(companies.name)) > 0
)
AND (
contacts.company_id = companies.id
)
)
UNION DISTINCT SELECT
contacts.id AS id
FROM
contacts
WHERE
(
position('as' in LOWER(first_name)) > 0
)
) AS ID;
Report
Aggregate (cost=1609467.66..1609467.71 rows=1 width=8) (actual time=1039.249..1039.330 rows=1 loops=1)
-> Unique (cost=1320886.03..1345980.09 rows=5018811 width=8) (actual time=999.363..1030.500 rows=195963 loops=1)
-> Sort (cost=1320886.03..1333433.06 rows=5018811 width=8) (actual time=999.362..1013.818 rows=198421 loops=1)
Sort Key: contacts.id
Sort Method: external merge Disk: 3520kB
-> Gather (cost=10.00..754477.62 rows=5018811 width=8) (actual time=0.581..941.210 rows=198421 loops=1)
Workers Planned: 4
Workers Launched: 4
-> Parallel Append (cost=0.00..749448.80 rows=5018811 width=8) (actual time=290.521..943.736 rows=39684 loops=5)
-> Parallel Hash Join (cost=101469.35..164569.24 rows=334587 width=8) (actual time=724.841..724.843 rows=0 loops=2)
Hash Cond: (contacts.phone_number_id = phone_numbers.id)
-> Parallel Seq Scan on contacts (cost=0.00..59315.91 rows=1003762 width=16) (never executed)
-> Parallel Hash (cost=78630.16..78630.16 rows=431819 width=8) (actual time=723.735..723.735 rows=0 loops=2)
Buckets: 131072 Batches: 32 Memory Usage: 0kB
-> Parallel Seq Scan on phone_numbers (cost=0.00..78630.16 rows=431819 width=8) (actual time=723.514..723.514 rows=0 loops=2)
Filter: ("position"(lower((value)::text), 'as'::text) > 0)
Rows Removed by Filter: 2007960
-> Hash Join (cost=0.38..74780.48 rows=250940 width=8) (actual time=0.888..0.888 rows=0 loops=1)
Hash Cond: (contacts_1.company_id = companies.id)
-> Parallel Seq Scan on contacts contacts_1 (cost=0.00..59315.91 rows=1003762 width=16) (actual time=0.009..0.009 rows=1 loops=1)
-> Hash (cost=0.33..0.33 rows=1 width=8) (actual time=0.564..0.564 rows=0 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 8kB
-> Seq Scan on companies (cost=0.00..0.33 rows=1 width=8) (actual time=0.563..0.563 rows=0 loops=1)
Filter: ("position"(lower((name)::text), 'as'::text) > 0)
Rows Removed by Filter: 4
-> Parallel Seq Scan on contacts contacts_2 (cost=0.00..66844.13 rows=334588 width=8) (actual time=0.119..315.032 rows=20398 loops=5)
Filter: ("position"(lower((last_name)::text), 'as'::text) > 0)
Rows Removed by Filter: 782612
-> Parallel Seq Scan on contacts contacts_3 (cost=0.00..66844.13 rows=334588 width=8) (actual time=0.510..558.791 rows=32144 loops=3)
Filter: ("position"(lower((first_name)::text), 'as'::text) > 0)
Rows Removed by Filter: 1306206
Planning Time: 2.115 ms
Execution Time: 1040.620 ms
It's hard to help you, because I don't have acces to your data. Let me try...
EXPLAIN ANALYZE report shows that:
Yor query doesn't using indexes. Full scan on table phone_numbers tooks 1.202 second, and 0.681 senod on contacts table.
"Rows Removed by Join Filter: 763817".
"Parallel Hash Join (cost=137684.86..201964.34 rows=1003781 width=41) (actual time=3006.633..4596.987 rows=803010 loops=5)" . So this query joins ~800k rows and then filter 763k of it.
Maybe you can reverse that. This should speed up (but that needs to be checked).
For example you can test this - rewrite your query in this direction:
SELECT COUNT( ID)
FROM
(
SELECT "contacts"."id"
FROM "contacts"
Where <filters on contract here>
union
SELECT "contacts"."id"
FROM "contacts"
where phone_number_id in ( select "phone_numbers"."id"
from "phone_numbers"
where <filters on phone_numbers here>
) as A
union
SELECT "contacts"."id"
FROM "contacts"
where company_id in ( select "companies"."id"
from "companies"
where <filters on companies here> )
) as B
Two indexes: one on column contacts.phone_number_id and another on contacts.company_id might help.
EDIT:
It using index on "phone_numbers"."id" with nested loop it tooks 5 seconds.
Try to avoid this.
Please check, what it will do for this:
SELECT Count(id) AS id
FROM (
SELECT contacts.id AS id
FROM contacts
WHERE (
contacts.last_name ilike '%as%')
OR (
contacts.last_name ilike '%as%')
UNION
SELECT contacts.id AS id
FROM contacts
WHERE contacts.phone_number_id IN
(
SELECT to_number(to_char(phone_numbers.id))) /* just for disable index scan for that column */ AS phone_number_id
FROM phone_numbers
WHERE phone_numbers.value ilike '%as%')
UNION
SELECT contacts.id AS id
FROM contacts
WHERE contacts.company_id IN
(
SELECT companies.id AS company_id
FROM companies
WHERE companies.name ilike '%as%' )) AS ID
Aggregate (cost=419095.35..419095.40 rows=1 width=8) (actual time=13235.986..13236.335 rows=1 loops=1)
-> Unique (cost=346875.23..353155.24 rows=1256002 width=8) (actual time=13211.350..13230.729 rows=195963 loops=1)
-> Sort (cost=346875.23..350015.24 rows=1256002 width=8) (actual time=13211.349..13219.607 rows=195963 loops=1)
Sort Key: contacts.id
Sort Method: external merge Disk: 3472kB
-> Append (cost=2249.63..218658.27 rows=1256002 width=8) (actual time=5927.019..13164.421 rows=195963 loops=1)
-> Gather (cost=2249.63..48279.58 rows=251838 width=8) (actual time=5927.019..6911.795 rows=195963 loops=1)
Workers Planned: 4
Workers Launched: 4
-> Parallel Bitmap Heap Scan on contacts (cost=2239.63..48017.74 rows=62960 width=8) (actual time=5861.480..6865.957 rows=39193 loops=5)
Recheck Cond: (((first_name)::text ~~* '%as%'::text) OR ((last_name)::text ~~* '%as%'::text))
Rows Removed by Index Recheck: 763815
Heap Blocks: exact=10860 lossy=6075
-> BitmapOr (cost=2239.63..2239.63 rows=255705 width=0) (actual time=5917.966..5917.966 rows=0 loops=1)
-> Bitmap Index Scan on idx_trgm_contacts_first_name (cost=0.00..1291.57 rows=156527 width=0) (actual time=2972.404..2972.404 rows=4015039 loops=1)
Index Cond: ((first_name)::text ~~* '%as%'::text)
-> Bitmap Index Scan on idx_trgm_contacts_last_name (cost=0.00..822.14 rows=99177 width=0) (actual time=2945.560..2945.560 rows=4015038 loops=1)
Index Cond: ((last_name)::text ~~* '%as%'::text)
-> Nested Loop (cost=81.96..384.33 rows=402 width=8) (actual time=6213.028..6213.028 rows=0 loops=1)
-> Unique (cost=81.52..83.53 rows=402 width=8) (actual time=6213.027..6213.027 rows=0 loops=1)
-> Sort (cost=81.52..82.52 rows=402 width=8) (actual time=6213.027..6213.027 rows=0 loops=1)
Sort Key: ((NULLIF((phone_numbers.id)::text, ''::text))::integer)
Sort Method: quicksort Memory: 25kB
-> Index Scan using idx_trgm_phone_value on phone_numbers (cost=0.41..64.13 rows=402 width=8) (actual time=6213.006..6213.006 rows=0 loops=1)
Index Cond: ((value)::text ~~* '%as%'::text)
Rows Removed by Index Recheck: 4015921
-> Index Scan using index_contacts_on_phone_number_id on contacts contacts_1 (cost=0.44..0.70 rows=1 width=16) (never executed)
Index Cond: (phone_number_id = (NULLIF((phone_numbers.id)::text, ''::text))::integer)
-> Gather (cost=10.36..75794.22 rows=1003762 width=8) (actual time=25.691..25.709 rows=0 loops=1)
Workers Planned: 4
Workers Launched: 4
-> Hash Join (cost=0.36..74780.46 rows=250940 width=8) (actual time=2.653..2.653 rows=0 loops=5)
Hash Cond: (contacts_2.company_id = companies.id)
-> Parallel Seq Scan on contacts contacts_2 (cost=0.00..59315.91 rows=1003762 width=16) (actual time=0.244..0.244 rows=1 loops=5)
-> Hash (cost=0.31..0.31 rows=1 width=8) (actual time=0.244..0.244 rows=0 loops=5)
Buckets: 1024 Batches: 1 Memory Usage: 8kB
-> Seq Scan on companies (cost=0.00..0.31 rows=1 width=8) (actual time=0.244..0.244 rows=0 loops=5)
Filter: ((name)::text ~~* '%as%'::text)
Rows Removed by Filter: 4
Planning Time: 1.458 ms
Execution Time: 13236.949 ms
I tried below,
SELECT Count(id) AS id
FROM (
SELECT contacts.id AS id
FROM contacts
WHERE (substring(LOWER(contacts.first_name), position('as' in LOWER(first_name)), 2) = 'as')
OR (substring(LOWER(contacts.last_name), position('as' in LOWER(last_name)), 2) = 'as')
UNION
SELECT contacts.id AS id
FROM contacts
WHERE contacts.phone_number_id IN
(
SELECT NULLIF(CAST(phone_numbers.id AS text), '')::int AS phone_number_id
FROM phone_numbers
WHERE (substring(LOWER(phone_numbers.value), position('as' in LOWER(phone_numbers.value)), 2) = 'as'))
UNION
SELECT contacts.id AS id
FROM contacts
WHERE contacts.company_id IN
(
SELECT companies.id AS company_id
FROM companies
WHERE (substring(LOWER(companies.name), position('as' in LOWER(companies.name)), 2) = 'as') )) AS ID
Aggregate (cost=508646.88..508646.93 rows=1 width=8) (actual time=1455.892..1455.995 rows=1 loops=1)
-> Unique (cost=447473.09..452792.55 rows=1063892 width=8) (actual time=1431.464..1450.434 rows=195963 loops=1)
-> Sort (cost=447473.09..450132.82 rows=1063892 width=8) (actual time=1431.464..1439.267 rows=195963 loops=1)
Sort Key: contacts.id
Sort Method: external merge Disk: 3472kB
-> Append (cost=10.00..340141.41 rows=1063892 width=8) (actual time=0.391..1370.557 rows=195963 loops=1)
-> Gather (cost=10.00..84460.02 rows=40050 width=8) (actual time=0.391..983.457 rows=195963 loops=1)
Workers Planned: 4
Workers Launched: 4
-> Parallel Seq Scan on contacts (cost=0.00..84409.97 rows=10012 width=8) (actual time=1.696..987.285 rows=39193 loops=5)
Filter: (("substring"(lower((first_name)::text), "position"(lower((first_name)::text), 'as'::text), 2) = 'as'::text) OR ("substring"(lower((last_name)::text), "position"(lower((last_name)::text), 'as'::text), 2) = 'as'::text))
Rows Removed by Filter: 763817
-> Nested Loop (cost=85188.17..100095.23 rows=20080 width=8) (actual time=364.076..364.125 rows=0 loops=1)
-> HashAggregate (cost=85187.73..86191.73 rows=20080 width=8) (actual time=364.074..364.123 rows=0 loops=1)
Group Key: (NULLIF((phone_numbers.id)::text, ''::text))::integer
Batches: 1 Memory Usage: 793kB
-> Gather (cost=10.00..85137.53 rows=20080 width=8) (actual time=363.976..364.025 rows=0 loops=1)
Workers Planned: 3
Workers Launched: 3
-> Parallel Seq Scan on phone_numbers (cost=0.00..85107.45 rows=6477 width=8) (actual time=357.030..357.031 rows=0 loops=4)
Filter: ("substring"(lower((value)::text), "position"(lower((value)::text), 'as'::text), 2) = 'as'::text)
Rows Removed by Filter: 1003980
-> Index Scan using index_contacts_on_phone_number_id on contacts contacts_1 (cost=0.44..0.64 rows=1 width=16) (never executed)
Index Cond: (phone_number_id = (NULLIF((phone_numbers.id)::text, ''::text))::integer)
-> Gather (cost=10.40..75794.26 rows=1003762 width=8) (actual time=6.889..6.910 rows=0 loops=1)
Workers Planned: 4
Workers Launched: 4
-> Hash Join (cost=0.40..74780.50 rows=250940 width=8) (actual time=0.138..0.139 rows=0 loops=5)
Hash Cond: (contacts_2.company_id = companies.id)
-> Parallel Seq Scan on contacts contacts_2 (cost=0.00..59315.91 rows=1003762 width=16) (actual time=0.004..0.004 rows=1 loops=5)
-> Hash (cost=0.35..0.35 rows=1 width=8) (actual time=0.081..0.081 rows=0 loops=5)
Buckets: 1024 Batches: 1 Memory Usage: 8kB
-> Seq Scan on companies (cost=0.00..0.35 rows=1 width=8) (actual time=0.081..0.081 rows=0 loops=5)
Filter: ("substring"(lower((name)::text), "position"(lower((name)::text), 'as'::text), 2) = 'as'::text)
Rows Removed by Filter: 4
Planning Time: 0.927 ms
Execution Time: 1456.742 ms

Postgresql Query Performance drop down from 4 secs to 16 minutes just by adding one filter criteria

I wrote a simple query that involves 2 views.
I need to find, using self join on "contratti_gas_attivi" (which doesn't contain information on any offer,settled or not), the supplies that doesn't have a settled offer (which I found defined in "offerte_valide_dettaglio" which contains ONLY the informations for all the supplies where an offer is settled) but for which an offers exists for the same end-user supplied till a day before the starting of the new end-user.
So the following code gets me a resutl of almost 50 rows in 4 to 6 seconds (which I dont consider fast but this is not something that should be run frequentely) but I only found the supplies which haven't an offer associated to them but only 3 of those have a previous settled offer.
select ovd2.cod_offerta ,ovd2.id_contratto ,ovd2.id_offerta, cga.*,cga2.* from contratti_gas_attivi cga left join offerte_valide_dettaglio ovd
on ovd.id_contratto = cga.id
left join contratti_gas_attivi cga2 on cga.cod_pdr =cga2.cod_pdr and cga.data_inizio = (cga2.data_fine +'1 day'::interval)::date
left join offerte_valide_dettaglio ovd2 on ovd2.id_contratto = cga2.id
left join crm.customer_offers co on co.id = ovd2.id_offerta
where cga.tipo_inizio_contratto = 'VOLTURA' and ovd.id_offerta is null;
So at the end I just add and ovd2.cod_offerta is not null to take out the other 47 rows and the query now takes almost 16 minutes to complete ! Just by adding a filtering clause at the end !
How is that possible ? The query plan get completely screwed...
Before last filter
Nested Loop Left Join (cost=4472.03..6947.83 rows=1 width=251) (actual time=3052.330..4196.372 rows=54 loops=1)
-> Nested Loop Left Join (cost=3000.74..4218.60 rows=1 width=112) (actual time=1652.151..1655.099 rows=49 loops=1)
Join Filter: ((cc2.piva)::text = (cg.piva_cc)::text)
Rows Removed by Join Filter: 2349
Filter: (cc2.data_replaced IS NULL)
Rows Removed by Filter: 3
-> Nested Loop Left Join (cost=3000.74..4215.59 rows=1 width=94) (actual time=1652.133..1653.982 rows=49 loops=1)
Join Filter: ((cc.piva)::text = (cg.piva_cliente)::text)
Rows Removed by Join Filter: 2349
Filter: ((cc.data_replaced IS NULL) AND (cc.data_deleted IS NULL) AND (cc.data_deleted IS NULL))
Rows Removed by Filter: 3
-> Hash Right Join (cost=3000.74..4212.58 rows=1 width=76) (actual time=1652.098..1652.268 rows=49 loops=1)
Hash Cond: (ocg.id_contratto = cg.id)
Filter: (ocg.id_offerta IS NULL)
Rows Removed by Filter: 2352
-> Hash Left Join (cost=1457.68..2561.97 rows=40966 width=16) (actual time=1479.736..1501.558 rows=39890 loops=1)
Hash Cond: (ocg.id_contratto = cg_1.id)
-> Seq Scan on offerte_contratti_gas ocg (cost=0.00..950.66 rows=40966 width=16) (actual time=0.033..8.184 rows=39890 loops=1)
-> Hash (cost=1457.66..1457.66 rows=1 width=8) (actual time=1479.633..1479.635 rows=36517 loops=1)
Buckets: 65536 (originally 1024) Batches: 1 (originally 1) Memory Usage: 1939kB
-> Nested Loop Left Join (cost=0.00..1457.66 rows=1 width=8) (actual time=0.080..1466.558 rows=36517 loops=1)
Join Filter: ((cc2_1.piva)::text = (cg_1.piva_cc)::text)
Rows Removed by Join Filter: 1747843
Filter: (cc2_1.data_replaced IS NULL)
Rows Removed by Filter: 4973
-> Nested Loop Left Join (cost=0.00..1454.65 rows=1 width=20) (actual time=0.052..743.117 rows=36517 loops=1)
Join Filter: ((cc_1.piva)::text = (cg_1.piva_cliente)::text)
Rows Removed by Join Filter: 1747827
Filter: ((cc_1.data_replaced IS NULL) AND (cc_1.data_deleted IS NULL) AND (cc_1.data_deleted IS NULL))
Rows Removed by Filter: 4989
-> Seq Scan on contratti_gas cg_1 (cost=0.00..1451.64 rows=1 width=32) (actual time=0.024..13.750 rows=36517 loops=1)
Filter: ((delated_at IS NULL) AND (replaced_at IS NULL))
Rows Removed by Filter: 47
-> Seq Scan on controparti_commerciali cc_1 (cost=0.00..2.45 rows=45 width=20) (actual time=0.001..0.005 rows=49 loops=36517)
-> Seq Scan on controparti_commerciali cc2_1 (cost=0.00..2.45 rows=45 width=16) (actual time=0.001..0.005 rows=49 loops=36517)
-> Hash (cost=1543.05..1543.05 rows=1 width=76) (actual time=145.878..145.878 rows=2104 loops=1)
Buckets: 4096 (originally 1024) Batches: 1 (originally 1) Memory Usage: 253kB
-> Seq Scan on contratti_gas cg (cost=0.00..1543.05 rows=1 width=76) (actual time=0.035..144.183 rows=2104 loops=1)
Filter: ((delated_at IS NULL) AND (replaced_at IS NULL) AND ((tipo_inizio_contratto)::text = 'VOLTURA'::text))
Rows Removed by Filter: 34460
-> Seq Scan on controparti_commerciali cc (cost=0.00..2.45 rows=45 width=38) (actual time=0.002..0.006 rows=49 loops=49)
-> Seq Scan on controparti_commerciali cc2 (cost=0.00..2.45 rows=45 width=34) (actual time=0.001..0.006 rows=49 loops=49)
-> Hash Right Join (cost=1471.29..2729.21 rows=1 width=139) (actual time=51.523..51.845 rows=1 loops=49)
Hash Cond: (ocg_1.id_contratto = cg_2.id)
-> Hash Left Join (cost=1457.68..2561.97 rows=40966 width=27) (actual time=28.165..48.297 rows=39890 loops=49)
Hash Cond: (ocg_1.id_contratto = cg_3.id)
-> Seq Scan on offerte_contratti_gas ocg_1 (cost=0.00..950.66 rows=40966 width=27) (actual time=0.002..7.578 rows=39890 loops=49)
-> Hash (cost=1457.66..1457.66 rows=1 width=8) (actual time=1379.882..1379.883 rows=36517 loops=1)
Buckets: 65536 (originally 1024) Batches: 1 (originally 1) Memory Usage: 1939kB
-> Nested Loop Left Join (cost=0.00..1457.66 rows=1 width=8) (actual time=0.041..1368.418 rows=36517 loops=1)
Join Filter: ((cc2_3.piva)::text = (cg_3.piva_cc)::text)
Rows Removed by Join Filter: 1747843
Filter: (cc2_3.data_replaced IS NULL)
Rows Removed by Filter: 4973
-> Nested Loop Left Join (cost=0.00..1454.65 rows=1 width=20) (actual time=0.026..696.145 rows=36517 loops=1)
Join Filter: ((cc_3.piva)::text = (cg_3.piva_cliente)::text)
Rows Removed by Join Filter: 1747827
Filter: ((cc_3.data_replaced IS NULL) AND (cc_3.data_deleted IS NULL) AND (cc_3.data_deleted IS NULL))
Rows Removed by Filter: 4989
-> Seq Scan on contratti_gas cg_3 (cost=0.00..1451.64 rows=1 width=32) (actual time=0.013..12.852 rows=36517 loops=1)
Filter: ((delated_at IS NULL) AND (replaced_at IS NULL))
Rows Removed by Filter: 47
-> Seq Scan on controparti_commerciali cc_3 (cost=0.00..2.45 rows=45 width=20) (actual time=0.001..0.005 rows=49 loops=36517)
-> Seq Scan on controparti_commerciali cc2_3 (cost=0.00..2.45 rows=45 width=16) (actual time=0.001..0.005 rows=49 loops=36517)
-> Hash (cost=13.60..13.60 rows=1 width=112) (actual time=0.460..0.460 rows=1 loops=49)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Hash Right Join (cost=10.97..13.60 rows=1 width=112) (actual time=0.439..0.449 rows=1 loops=49)
Hash Cond: ((cc2_2.piva)::text = (cg_2.piva_cc)::text)
Filter: (cc2_2.data_replaced IS NULL)
Rows Removed by Filter: 0
-> Seq Scan on controparti_commerciali cc2_2 (cost=0.00..2.45 rows=45 width=34) (actual time=0.002..0.006 rows=49 loops=49)
-> Hash (cost=10.96..10.96 rows=1 width=94) (actual time=0.427..0.427 rows=1 loops=49)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Hash Right Join (cost=8.33..10.96 rows=1 width=94) (actual time=0.405..0.417 rows=1 loops=49)
Hash Cond: ((cc_2.piva)::text = (cg_2.piva_cliente)::text)
Filter: ((cc_2.data_replaced IS NULL) AND (cc_2.data_deleted IS NULL) AND (cc_2.data_deleted IS NULL))
Rows Removed by Filter: 0
-> Seq Scan on controparti_commerciali cc_2 (cost=0.00..2.45 rows=45 width=38) (actual time=0.003..0.008 rows=49 loops=49)
-> Hash (cost=8.31..8.31 rows=1 width=76) (actual time=0.391..0.391 rows=1 loops=49)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Index Scan using pk_codpdr_datainizio_iserror on contratti_gas cg_2 (cost=0.29..8.31 rows=1 width=76) (actual time=0.382..0.384 rows=1 loops=49)
Index Cond: ((cod_pdr)::text = (cg.cod_pdr)::text)
Filter: ((delated_at IS NULL) AND (replaced_at IS NULL) AND (cg.data_inizio = ((data_fine + '1 day'::interval))::date))
Rows Removed by Filter: 2
Planning Time: 5.997 ms
Execution Time: 4196.992 ms
After last filter:
Nested Loop (cost=4461.49..6786.31 rows=1 width=251) (actual time=483943.882..1016665.139 rows=43 loops=1)
Join Filter: (((cg_2.cod_pdr)::text = (cg.cod_pdr)::text) AND (((cg_2.data_fine + '1 day'::interval))::date = cg.data_inizio))
Rows Removed by Join Filter: 1954567
-> Nested Loop Left Join (cost=1460.75..2567.69 rows=1 width=139) (actual time=1416.580..5211.218 rows=39890 loops=1)
-> Hash Join (cost=1457.68..2561.97 rows=1 width=139) (actual time=1416.502..1649.064 rows=39890 loops=1)
Hash Cond: (ocg_1.id_contratto = cg_2.id)
-> Seq Scan on offerte_contratti_gas ocg_1 (cost=0.00..950.66 rows=40966 width=27) (actual time=0.030..31.748 rows=39890 loops=1)
Filter: (cod_offerta IS NOT NULL)
-> Hash (cost=1457.66..1457.66 rows=1 width=112) (actual time=1416.000..1419.044 rows=36517 loops=1)
Buckets: 32768 (originally 1024) Batches: 2 (originally 1) Memory Usage: 3841kB
-> Nested Loop Left Join (cost=0.00..1457.66 rows=1 width=112) (actual time=0.072..1383.415 rows=36517 loops=1)
Join Filter: ((cc2_2.piva)::text = (cg_2.piva_cc)::text)
Rows Removed by Join Filter: 1747843
Filter: (cc2_2.data_replaced IS NULL)
Rows Removed by Filter: 4973
-> Nested Loop Left Join (cost=0.00..1454.65 rows=1 width=94) (actual time=0.043..704.608 rows=36517 loops=1)
Join Filter: ((cc_2.piva)::text = (cg_2.piva_cliente)::text)
Rows Removed by Join Filter: 1747827
Filter: ((cc_2.data_replaced IS NULL) AND (cc_2.data_deleted IS NULL) AND (cc_2.data_deleted IS NULL))
Rows Removed by Filter: 4989
-> Seq Scan on contratti_gas cg_2 (cost=0.00..1451.64 rows=1 width=76) (actual time=0.016..14.948 rows=36517 loops=1)
Filter: ((delated_at IS NULL) AND (replaced_at IS NULL))
Rows Removed by Filter: 47
-> Seq Scan on controparti_commerciali cc_2 (cost=0.00..2.45 rows=45 width=38) (actual time=0.001..0.005 rows=49 loops=36517)
-> Seq Scan on controparti_commerciali cc2_2 (cost=0.00..2.45 rows=45 width=34) (actual time=0.001..0.005 rows=49 loops=36517)
-> Hash Right Join (cost=3.08..5.71 rows=1 width=8) (actual time=0.061..0.073 rows=1 loops=39890)
Hash Cond: ((cc2_3.piva)::text = (cg_3.piva_cc)::text)
Filter: (cc2_3.data_replaced IS NULL)
Rows Removed by Filter: 0
-> Seq Scan on controparti_commerciali cc2_3 (cost=0.00..2.45 rows=45 width=16) (actual time=0.001..0.005 rows=49 loops=39890)
-> Hash (cost=3.06..3.06 rows=1 width=20) (actual time=0.048..0.048 rows=1 loops=39890)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Hash Right Join (cost=0.44..3.06 rows=1 width=20) (actual time=0.027..0.036 rows=1 loops=39890)
Hash Cond: ((cc_3.piva)::text = (cg_3.piva_cliente)::text)
Filter: ((cc_3.data_replaced IS NULL) AND (cc_3.data_deleted IS NULL) AND (cc_3.data_deleted IS NULL))
Rows Removed by Filter: 0
-> Seq Scan on controparti_commerciali cc_3 (cost=0.00..2.45 rows=45 width=20) (actual time=0.001..0.005 rows=49 loops=39890)
-> Hash (cost=0.42..0.42 rows=1 width=32) (actual time=0.018..0.018 rows=1 loops=39890)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Index Scan using uq_id on contratti_gas cg_3 (cost=0.29..0.42 rows=1 width=32) (actual time=0.013..0.013 rows=1 loops=39890)
Index Cond: (id = ocg_1.id_contratto)
Filter: ((delated_at IS NULL) AND (replaced_at IS NULL))
-> Nested Loop Left Join (cost=3000.74..4218.60 rows=1 width=112) (actual time=23.552..25.302 rows=49 loops=39890)
Join Filter: ((cc2.piva)::text = (cg.piva_cc)::text)
Rows Removed by Join Filter: 2349
Filter: (cc2.data_replaced IS NULL)
Rows Removed by Filter: 3
-> Nested Loop Left Join (cost=3000.74..4215.59 rows=1 width=94) (actual time=23.533..24.446 rows=49 loops=39890)
Join Filter: ((cc.piva)::text = (cg.piva_cliente)::text)
Rows Removed by Join Filter: 2349
Filter: ((cc.data_replaced IS NULL) AND (cc.data_deleted IS NULL) AND (cc.data_deleted IS NULL))
Rows Removed by Filter: 3
-> Hash Right Join (cost=3000.74..4212.58 rows=1 width=76) (actual time=23.493..23.561 rows=49 loops=39890)
Hash Cond: (ocg.id_contratto = cg.id)
Filter: (ocg.id_offerta IS NULL)
Rows Removed by Filter: 2352
-> Hash Left Join (cost=1457.68..2561.97 rows=40966 width=16) (actual time=0.034..19.423 rows=39890 loops=39890)
Hash Cond: (ocg.id_contratto = cg_1.id)
-> Seq Scan on offerte_contratti_gas ocg (cost=0.00..950.66 rows=40966 width=16) (actual time=0.002..7.327 rows=39890 loops=39890)
-> Hash (cost=1457.66..1457.66 rows=1 width=8) (actual time=1236.665..1238.748 rows=36517 loops=1)
Buckets: 65536 (originally 1024) Batches: 1 (originally 1) Memory Usage: 1939kB
-> Nested Loop Left Join (cost=0.00..1457.66 rows=1 width=8) (actual time=0.052..1230.365 rows=36517 loops=1)
Join Filter: ((cc2_1.piva)::text = (cg_1.piva_cc)::text)
Rows Removed by Join Filter: 1747843
Filter: (cc2_1.data_replaced IS NULL)
Rows Removed by Filter: 4973
-> Nested Loop Left Join (cost=0.00..1454.65 rows=1 width=20) (actual time=0.035..625.434 rows=36517 loops=1)
Join Filter: ((cc_1.piva)::text = (cg_1.piva_cliente)::text)
Rows Removed by Join Filter: 1747827
Filter: ((cc_1.data_replaced IS NULL) AND (cc_1.data_deleted IS NULL) AND (cc_1.data_deleted IS NULL))
Rows Removed by Filter: 4989
-> Seq Scan on contratti_gas cg_1 (cost=0.00..1451.64 rows=1 width=32) (actual time=0.012..11.876 rows=36517 loops=1)
Filter: ((delated_at IS NULL) AND (replaced_at IS NULL))
Rows Removed by Filter: 47
-> Seq Scan on controparti_commerciali cc_1 (cost=0.00..2.45 rows=45 width=20) (actual time=0.001..0.004 rows=49 loops=36517)
-> Seq Scan on controparti_commerciali cc2_1 (cost=0.00..2.45 rows=45 width=16) (actual time=0.001..0.004 rows=49 loops=36517)
-> Hash (cost=1543.05..1543.05 rows=1 width=76) (actual time=8.053..8.053 rows=2104 loops=1)
Buckets: 4096 (originally 1024) Batches: 1 (originally 1) Memory Usage: 253kB
-> Seq Scan on contratti_gas cg (cost=0.00..1543.05 rows=1 width=76) (actual time=0.014..7.482 rows=2104 loops=1)
Filter: ((delated_at IS NULL) AND (replaced_at IS NULL) AND ((tipo_inizio_contratto)::text = 'VOLTURA'::text))
Rows Removed by Filter: 34460
-> Seq Scan on controparti_commerciali cc (cost=0.00..2.45 rows=45 width=38) (actual time=0.001..0.004 rows=49 loops=1954610)
-> Seq Scan on controparti_commerciali cc2 (cost=0.00..2.45 rows=45 width=34) (actual time=0.001..0.004 rows=49 loops=1954610)
Planning Time: 7.946 ms
Execution Time: 1016666.928 ms
I am not very familiar in reading query plans but I can see that adding the last clause completely ruin the "fast" query plan that was running before! Any advice ?
edit:
Holly molly jjanes that worked wonderfully !
I read that vacuum permanentely delete obsolete deleted/updated tuples and vaccum analyze update the statistic used by the planner. Still I'm a bit confused on why that worked ! The DB is on local and I do only work on it, I did some crud operation on the underlying tables but going from 4 seconds to 16 just because I didn't run the vacuum sounds strange to me! Now the full query only take 187ms !!! Do you have any resource to share beside official docs about vacuum to better understand ?,Many thanks, unfortunately I cannot upvote the answer yet.

SQL (Postgres) Optimal Number of Joins

This query is being performed in postgres version 12. This query poses 8 joins, and lasts approximately 5 seconds.
Query 1
select *
from "public"."products" "P"
inner join "system"."categories" "C" on "C"."id" = "P"."id_category"
inner join "public"."businesses" "E" on "E"."id" = "P"."id_business"
left join "public"."product_files" "pf" on "pf"."id_product" = "P"."id"
left join "system"."files" "f" on "f"."name" = "pf"."img_code"
left join "public"."product_variations" "pv" on ("pv"."id_product" = "P"."id" and "pv"."status" <> 'Deleted')
left join "public"."product_stocks" "ps" on ("ps"."id_product_variation" = "pv"."id" and "ps"."status" <> 'Deleted')
left join "public"."product_stocks" "pps" on ("pps"."id_product" = "P"."id" and "pps"."status" <> 'Deleted')
inner join search_products( array['tires'], 8, 1, 'es') "search" on search.id = "P"."id"
where "P"."status" <> 'Deleted'
Postgres Query EXPLAIN(ANALYZE, BUFFERS) for Query 1
Merge Join (cost=112948.60..121805.61 rows=4996 width=1145) (actual time=2003.599..2426.892 rows=40 loops=1)
Merge Cond: ("P".id = search.id)
Buffers: shared hit=760531, temp read=16912 written=18837
-> Merge Left Join (cost=112888.52..120950.73 rows=287945 width=1105) (actual time=1607.013..2093.722 rows=380961 loops=1)
Merge Cond: ("P".id = pf.id_product)
Buffers: shared hit=752079, temp read=15561 written=15606
-> Merge Left Join (cost=16288.22..19167.29 rows=57631 width=771) (actual time=165.803..271.662 rows=76193 loops=1)
Merge Cond: ("P".id = pps.id_product)
Buffers: shared hit=3820, temp read=2706 written=2733
-> Merge Left Join (cost=16287.81..16577.01 rows=57631 width=686) (actual time=165.787..217.878 rows=56921 loops=1)
Merge Cond: ("P".id = pv.id_product)
Buffers: shared hit=2058, temp read=2706 written=2733
-> Sort (cost=14888.93..15033.01 rows=57631 width=514) (actual time=156.825..175.154 rows=56920 loops=1)
Sort Key: "P".id
Sort Method: external merge Disk: 21840kB
Buffers: shared hit=1430, temp read=2706 written=2733
-> Hash Join (cost=43.49..2484.49 rows=57631 width=514) (actual time=0.266..64.052 rows=57631 loops=1)
Hash Cond: ("P".id_business = "E".id)
Buffers: shared hit=1430
-> Hash Join (cost=37.81..2322.14 rows=57631 width=374) (actual time=0.214..39.402 rows=57631 loops=1)
Hash Cond: ("P".id_category = "C".id)
Buffers: shared hit=1427
-> Seq Scan on products "P" (cost=0.00..2132.41 rows=57631 width=252) (actual time=0.009..12.754 rows=57631 loops=1)
Filter: ((status)::text <> 'Deleted'::text)
Rows Removed by Filter: 2
Buffers: shared hit=1412
-> Hash (cost=25.14..25.14 rows=1014 width=122) (actual time=0.201..0.201 rows=1014 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 124kB
Buffers: shared hit=15
-> Seq Scan on categories "C" (cost=0.00..25.14 rows=1014 width=122) (actual time=0.007..0.078 rows=1014 loops=1)
Buffers: shared hit=15
-> Hash (cost=4.19..4.19 rows=119 width=140) (actual time=0.047..0.048 rows=119 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 29kB
Buffers: shared hit=3
-> Seq Scan on businesses "E" (cost=0.00..4.19 rows=119 width=140) (actual time=0.013..0.024 rows=119 loops=1)
Buffers: shared hit=3
-> Sort (cost=1398.88..1399.05 rows=70 width=172) (actual time=8.956..8.958 rows=3 loops=1)
Sort Key: pv.id_product
Sort Method: quicksort Memory: 43kB
Buffers: shared hit=628
-> Hash Right Join (cost=4.58..1396.73 rows=70 width=172) (actual time=8.853..8.912 rows=70 loops=1)
Hash Cond: (ps.id_product_variation = pv.id)
Buffers: shared hit=628
-> Seq Scan on product_stocks ps (cost=0.00..1259.35 rows=50589 width=85) (actual time=0.009..7.030 rows=50595 loops=1)
Filter: ((status)::text <> 'Deleted'::text)
Rows Removed by Filter: 73
Buffers: shared hit=626
-> Hash (cost=3.70..3.70 rows=70 width=87) (actual time=0.048..0.049 rows=70 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 16kB
Buffers: shared hit=2
-> Seq Scan on product_variations pv (cost=0.00..3.70 rows=70 width=87) (actual time=0.020..0.039 rows=70 loops=1)
Filter: ((status)::text <> 'Deleted'::text)
Rows Removed by Filter: 66
Buffers: shared hit=2
-> Index Scan using product_stocks_id_product_id_product_variation_id_location_key on product_stocks pps (cost=0.41..1819.97 rows=50589 width=85) (actual time=0.013..17.822 rows=49924 loops=1)
Filter: ((status)::text <> 'Deleted'::text)
Rows Removed by Filter: 1
Buffers: shared hit=1762
-> Materialize (cost=96600.25..98040.03 rows=287955 width=334) (actual time=1441.203..1613.160 rows=380961 loops=1)
Buffers: shared hit=748259, temp read=12855 written=12873
-> Sort (cost=96600.25..97320.14 rows=287955 width=334) (actual time=1441.198..1567.183 rows=284596 loops=1)
Sort Key: pf.id_product
Sort Method: external merge Disk: 102840kB
Buffers: shared hit=748259, temp read=12855 written=12873
-> Merge Left Join (cost=0.84..44546.48 rows=287955 width=334) (actual time=0.021..1013.742 rows=287955 loops=1)
Merge Cond: ((pf.img_code)::text = (f.name)::text)
Buffers: shared hit=748259
-> Index Scan using product_files_pkey on product_files pf (cost=0.42..10516.05 rows=287955 width=66) (actual time=0.005..184.173 rows=287955 loops=1)
Buffers: shared hit=289884
-> Index Scan using files_pkey on files f (cost=0.42..29304.42 rows=455180 width=268) (actual time=0.005..338.206 rows=455178 loops=1)
Buffers: shared hit=458375
-> Sort (cost=60.08..62.58 rows=1000 width=40) (actual time=313.554..313.558 rows=36 loops=1)
Sort Key: search.id
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=8452, temp read=1351 written=3231
-> Function Scan on search_products search (cost=0.25..10.25 rows=1000 width=40) (actual time=313.544..313.545 rows=8 loops=1)
Buffers: shared hit=8452, temp read=1351 written=3231
Planning Time: 2.632 ms
Execution Time: 2440.414 ms
I was reviewing the way to optimize the query, so I was doing the joins one by one to see where the problem was, and among so many permutations in order of join, I realized that postgres from join number 7, apparently stops find the best way to run the query. So, when i delete any (randomly) join, the query lasts 300ms
Query 2
select *
from "public"."products" "P"
inner join "system"."categories" "C" on "C"."id" = "P"."id_category"
left join "public"."product_files" "pf" on "pf"."id_product" = "P"."id"
left join "system"."files" "f" on "f"."name" = "pf"."img_code"
left join "public"."product_variations" "pv" on ("pv"."id_product" = "P"."id" and "pv"."status" <> 'Deleted')
left join "public"."product_stocks" "ps" on ("ps"."id_product_variation" = "pv"."id" and "ps"."status" <> 'Deleted')
left join "public"."product_stocks" "pps" on ("pps"."id_product" = "P"."id" and "pps"."status" <> 'Deleted')
inner join search_products( array['tires'], 8, 1, 'es') "search" on search.id = "P"."id"
where "P"."status" <> 'Deleted'
Postgres Query EXPLAIN(ANALYZE, BUFFERS) for Query 2
Nested Loop Left Join (cost=1365.30..6482.09 rows=4996 width=1005) (actual time=349.888..350.399 rows=40 loops=1)
Buffers: shared hit=9339, temp read=1351 written=3231
-> Nested Loop Left Join (cost=1364.88..3893.89 rows=4996 width=737) (actual time=349.866..349.957 rows=40 loops=1)
Buffers: shared hit=9179, temp read=1351 written=3231
-> Nested Loop Left Join (cost=1364.46..3250.90 rows=1000 width=671) (actual time=349.857..349.899 rows=8 loops=1)
Buffers: shared hit=9147, temp read=1351 written=3231
-> Hash Join (cost=1364.04..2759.11 rows=1000 width=586) (actual time=349.839..349.853 rows=8 loops=1)
Hash Cond: ("P".id_category = "C".id)
Buffers: shared hit=9119, temp read=1351 written=3231
-> Hash Right Join (cost=1326.23..2718.65 rows=1000 width=464) (actual time=349.566..349.574 rows=8 loops=1)
Hash Cond: (pv.id_product = "P".id)
Buffers: shared hit=9104, temp read=1351 written=3231
-> Hash Right Join (cost=4.58..1396.73 rows=70 width=172) (actual time=8.953..9.013 rows=70 loops=1)
Hash Cond: (ps.id_product_variation = pv.id)
Buffers: shared hit=628
-> Seq Scan on product_stocks ps (cost=0.00..1259.35 rows=50589 width=85) (actual time=0.008..7.060 rows=50595 loops=1)
Filter: ((status)::text <> 'Deleted'::text)
Rows Removed by Filter: 73
Buffers: shared hit=626
-> Hash (cost=3.70..3.70 rows=70 width=87) (actual time=0.047..0.048 rows=70 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 16kB
Buffers: shared hit=2
-> Seq Scan on product_variations pv (cost=0.00..3.70 rows=70 width=87) (actual time=0.015..0.033 rows=70 loops=1)
Filter: ((status)::text <> 'Deleted'::text)
Rows Removed by Filter: 66
Buffers: shared hit=2
-> Hash (cost=1309.15..1309.15 rows=1000 width=292) (actual time=340.542..340.543 rows=8 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 10kB
Buffers: shared hit=8476, temp read=1351 written=3231
-> Nested Loop (cost=0.54..1309.15 rows=1000 width=292) (actual time=340.505..340.535 rows=8 loops=1)
Buffers: shared hit=8476, temp read=1351 written=3231
-> Function Scan on search_products search (cost=0.25..10.25 rows=1000 width=40) (actual time=340.483..340.485 rows=8 loops=1)
Buffers: shared hit=8452, temp read=1351 written=3231
-> Index Scan using products_pkey on products "P" (cost=0.29..1.30 rows=1 width=252) (actual time=0.005..0.005 rows=1 loops=8)
Index Cond: (id = search.id)
Filter: ((status)::text <> 'Deleted'::text)
Buffers: shared hit=24
-> Hash (cost=25.14..25.14 rows=1014 width=122) (actual time=0.268..0.268 rows=1014 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 124kB
Buffers: shared hit=15
-> Seq Scan on categories "C" (cost=0.00..25.14 rows=1014 width=122) (actual time=0.012..0.110 rows=1014 loops=1)
Buffers: shared hit=15
-> Index Scan using product_stocks_id_product_id_product_variation_id_location_key on product_stocks pps (cost=0.41..0.47 rows=2 width=85) (actual time=0.005..0.005 rows=1 loops=8)
Index Cond: (id_product = "P".id)
Filter: ((status)::text <> 'Deleted'::text)
Buffers: shared hit=28
-> Index Scan using idx_product_files_product on product_files pf (cost=0.42..0.59 rows=5 width=66) (actual time=0.004..0.005 rows=5 loops=8)
Index Cond: (id_product = "P".id)
Buffers: shared hit=32
-> Index Scan using files_pkey on files f (cost=0.42..0.52 rows=1 width=268) (actual time=0.010..0.010 rows=1 loops=40)
Index Cond: ((name)::text = (pf.img_code)::text)
Buffers: shared hit=160
Planning Time: 2.581 ms
Execution Time: 350.525 ms
Is there an article that explains this behavior to me? and how to fix it?
That is because join_collapse_limit has a default value of 8. The optimizer tries all permutations only for the first 8 tables, the rest is joined as written. The rationale is to keep planning time reasonably short, which increases exponentially with the number of tables.
Options:
increase the parameter
figure out a good join order ans rewrite the query to join in that order

SQL query running very slow - postrges

This query currently take 4 minutes to run:
with name1 as (
select col1 as a1, col2 as a2, sum(FEE) as a3
from s1, date
where return_date = datesk and year = 2000
group by col1, col2
)
select c_id
from name1 ala1, ss, cc
where ala1.a3 > (
select avg(a3) * 1.2 from name1 ctr2
where ala1.a2 = ctr2.a2
)
and s_sk = ala1.a2
and s_state = 'TN'
and ala1.a1 = c_sk
order by c_id
limit 100;
I have set work_mem=’1000MB’ and enable-nestloop=off
EXPLAIN ANALYZE of this query is: http://explain.depesz.com/s/DUa
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------
--------------------
Limit (cost=59141.02..59141.09 rows=28 width=17) (actual time=253707.928..253707.940 rows=100 loops=1)
CTE name1
-> HashAggregate (cost=11091.33..11108.70 rows=1390 width=14) (actual time=105.223..120.358 rows=50441 loops=1)
Group Key: s1.col1, s1.col2
-> Hash Join (cost=2322.69..11080.90 rows=1390 width=14) (actual time=10.390..79.897 rows=55820 loops=1)
Hash Cond: (s1.return_date = date.datesk)
-> Seq Scan on s1 (cost=0.00..7666.14 rows=287514 width=18) (actual time=0.005..33.801 rows=287514 loops=1)
-> Hash (cost=2318.11..2318.11 rows=366 width=4) (actual time=10.375..10.375 rows=366 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 13kB
-> Seq Scan on date (cost=0.00..2318.11 rows=366 width=4) (actual time=5.224..10.329 rows=366 loops=1)
Filter: (year = 2000)
Rows Removed by Filter: 72683
-> Sort (cost=48032.32..48032.39 rows=28 width=17) (actual time=253707.923..253707.930 rows=100 loops=1)
Sort Key: cc.c_id
Sort Method: top-N heapsort Memory: 32kB
-> Hash Join (cost=43552.37..48031.65 rows=28 width=17) (actual time=253634.511..253696.291 rows=18976 loops=1)
Hash Cond: (cc.c_sk = ala1.a1)
-> Seq Scan on cc (cost=0.00..3854.00 rows=100000 width=21) (actual time=0.009..18.527 rows=100000 loops=1)
-> Hash (cost=43552.02..43552.02 rows=28 width=4) (actual time=253634.420..253634.420 rows=18976 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 668kB
-> Hash Join (cost=1.30..43552.02 rows=28 width=4) (actual time=136.819..253624.375 rows=18982 loops=1)
Hash Cond: (ala1.a2 = ss.s_sk)
-> CTE Scan on name1 ala1 (cost=0.00..43548.70 rows=463 width=8) (actual time=136.756..253610.817 rows=18982 loops=1)
Filter: (a3 > (SubPlan 2))
Rows Removed by Filter: 31459
SubPlan 2
-> Aggregate (cost=31.29..31.31 rows=1 width=32) (actual time=5.025..5.025 rows=1 loops=50441)
-> CTE Scan on name1 ctr2 (cost=0.00..31.27 rows=7 width=32) (actual time=0.032..3.860 rows=8241 loops=50441)
Filter: (ala1.a2 = a2)
Rows Removed by Filter: 42200
-> Hash (cost=1.15..1.15 rows=12 width=4) (actual time=0.036..0.036 rows=12 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 1kB
-> Seq Scan on ss (cost=0.00..1.15 rows=12 width=4) (actual time=0.025..0.033 rows=12 loops=1)
Filter: (s_state = 'TN'::bpchar)
Planning time: 0.316 ms
Execution time: 253708.351 ms
(36 rows)
With enable_nestloop=on;
EXPLAIN ANLYZE result is : http://explain.depesz.com/s/NPo
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------
--------------
Limit (cost=54916.36..54916.43 rows=28 width=17) (actual time=257869.004..257869.015 rows=100 loops=1)
CTE name1
-> HashAggregate (cost=11091.33..11108.70 rows=1390 width=14) (actual time=92.354..104.103 rows=50441 loops=1)
Group Key: s1.col1, s1.col2
-> Hash Join (cost=2322.69..11080.90 rows=1390 width=14) (actual time=9.371..68.156 rows=55820 loops=1)
Hash Cond: (s1.return_date = date.datesk)
-> Seq Scan on s1 (cost=0.00..7666.14 rows=287514 width=18) (actual time=0.011..25.637 rows=287514 loops=1)
-> Hash (cost=2318.11..2318.11 rows=366 width=4) (actual time=9.343..9.343 rows=366 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 13kB
-> Seq Scan on date (cost=0.00..2318.11 rows=366 width=4) (actual time=4.796..9.288 rows=366 loops=1)
Filter: (year = 2000)
Rows Removed by Filter: 72683
-> Sort (cost=43807.66..43807.73 rows=28 width=17) (actual time=257868.994..257868.998 rows=100 loops=1)
Sort Key: cc.c_id
Sort Method: top-N heapsort Memory: 32kB
-> Nested Loop (cost=0.29..43806.98 rows=28 width=17) (actual time=120.358..257845.941 rows=18976 loops=1)
-> Nested Loop (cost=0.00..43633.22 rows=28 width=4) (actual time=120.331..257692.654 rows=18982 loops=1)
Join Filter: (ala1.a2 = ss.s_sk)
Rows Removed by Join Filter: 208802
-> CTE Scan on name1 ala1 (cost=0.00..43548.70 rows=463 width=8) (actual time=120.316..257652.636 rows=18982 loops=1)
Filter: (a3 > (SubPlan 2))
Rows Removed by Filter: 31459
SubPlan 2
-> Aggregate (cost=31.29..31.31 rows=1 width=32) (actual time=5.105..5.105 rows=1 loops=50441)
-> CTE Scan on name1 ctr2 (cost=0.00..31.27 rows=7 width=32) (actual time=0.032..3.952 rows=8241 loops=50441)
Filter: (ala1.a2 = a2)
Rows Removed by Filter: 42200
-> Materialize (cost=0.00..1.21 rows=12 width=4) (actual time=0.000..0.001 rows=12 loops=18982)
-> Seq Scan on ss (cost=0.00..1.15 rows=12 width=4) (actual time=0.007..0.012 rows=12 loops=1)
Filter: (s_state = 'TN'::bpchar)
-> Index Scan using cc_pkey on cc (cost=0.29..6.20 rows=1 width=21) (actual time=0.007..0.007 rows=1 loops=18982)
Index Cond: (c_sk = ala1.a1)
Planning time: 0.453 ms
Execution time: 257869.554 ms
(34 rows)
Many other queries run quickly with enable_nestloop=off, there is no big difference for this query. Raw data is not really big, so 4 minutes is too much. I was expecting around 4-5 seconds.
Why is it taking so long !?
I tried this in both postgres versions 9.4 and 9.5. It is same. Maybe I can create brin indexes. But I am not sure for which columns to create.
Configuration setting:
effective_cache_size | 89GB
shared_buffers | 18GB
work_mem | 1000MB
maintenance_work_mem | 500MB
checkpoint_segments | 32
constraint_exclusion | on
checkpoint_completion_target | 0.5
Like John Bollinger commented, your sub-query gets evaluated for each row of the main query. But since you are averaging on a simple column, you can easily move the sub-query out to a CTE and calculate the average once, which should speed up things tremendously:
with name1 as (
select col1 as a1, col2 as a2, sum(FEE) as a3
from s1, date
where return_date = datesk and year = 2000
group by col1, col2
), avg_a3_by_a2 as (
select a2, avg(a3) * 1.2 as avg12
from name1
group by a2
)
select c_id
from name1, avg_a3_by_a2, ss, cc
where name1.a3 > avg_a3_by_a2.avg12
and name1.a2 = avg_a3_by_a2.a2
and s_sk = name1.a2
and s_state = 'TN'
and name1.a1 = c_sk
order by c_id
limit 100;
The new CTE calculates the average + 20% for every distinct value of a2.
Please also use the JOIN syntax instead of comma-separated FROM items as it makes your code far more readable. And if you start using aliases in your query, use them consistently on all tables and columns. I could correct neither of these two issues because of lack of information.

QSqlQuery bindValue slow

I have a query like this
select Count(1) as Count, pt.Name as TypeName, pt.ID as TypeID, pc.ID as CatID,
o.Name as OffName, o.ID as OffID, pc.Color as Color, s.ID, s.ActionType,
s.EndTime, pt.Size, pt.Price, pt.Unit, pt.OffID as ProdOffID
from sess s
inner join off o on o.id = s.offid
inner join act a on a.sessid = s.id
inner join prod p on p.tagid = a.prodid
inner join ProdType pt on pt.id = p.prodtypeid and pt.offid = p.Offid
left join prodcat pc on pc.id = pt.prodcatid and pc.offid = pt.offid
where s.offid = ? and s.acttype in (?, ?)
Group By pt.Name, pt.ID, pc.ID, o.Name,
o.ID, pc.Color, s.ID, s.ActType,
s.EndTime, pt.Size, pt.Price, pt.Unit, pt.OffID
If I use bindValue for parameters, code block below takes lots of time (about 2 seconds)
QSqlQuery newQuery(db);
newQuery.prepare(queryString);
for (int parameterIndex=0;parameterIndex<values.count();parameterIndex++) {
newQuery.bindValue(parameterIndex,values[parameterIndex]);
}
newQuery.exec();
But if I replace ?'s with values and if I don’t use bindValue code block below takes about 50ms.
QSqlQuery newQuery(db);
newQuery.prepare(queryString);
newQuery.exec();
Is this normal? What makes this difference?
Note that these tables have btree indexes for their FK’s.
Using Qt 4.7.4 compiled with VC2008SP1. Database is PostgreSQL.
Answering to my own question (thanks to Mat):
PostgreSQL optimizes this query's plan according to values. So, prepared statements block these kind of optimizations and gives this query plan:
GroupAggregate (cost=581209.52..615986.02 rows=695530 width=72) (actual time=4067.645..4069.321 rows=101 loops=1)
-> Sort (cost=581209.52..582948.35 rows=695530 width=72) (actual time=4067.637..4067.719 rows=1832 loops=1)
Sort Key: pt.name, pt.id, pc.id, o.name, o.id, pc.color, s.id, s.actiontype, s.endtime, pt.size, pt.price, pt.unit, pt.officeid
Sort Method: quicksort Memory: 276kB
-> Hash Join (cost=49529.53..456659.15 rows=695530 width=72) (actual time=765.864..4047.298 rows=1832 loops=1)
Hash Cond: ((a.productid)::text = (p.tagid)::text)
-> Hash Join (cost=10640.07..391699.07 rows=555317 width=48) (actual time=41.884..3236.878 rows=2197 loops=1)
Hash Cond: (a.sessionid = s.id)
-> Seq Scan on action a (cost=0.00..280038.20 rows=15274820 width=29) (actual time=0.026..1586.065 rows=15274820 loops=1)
-> Hash (cost=10603.35..10603.35 rows=2938 width=23) (actual time=0.787..0.787 rows=116 loops=1)
-> Nested Loop (cost=208.16..10603.35 rows=2938 width=23) (actual time=0.234..0.747 rows=116 loops=1)
-> Seq Scan on office o (cost=0.00..4.26 rows=1 width=7) (actual time=0.012..0.019 rows=1 loops=1)
Filter: (id = $1)
-> Bitmap Heap Scan on session s (cost=208.16..10569.70 rows=2938 width=20) (actual time=0.216..0.701 rows=116 loops=1)
Recheck Cond: (s.officeid = $1)
Filter: (s.actiontype = ANY (ARRAY[$2, $3]))
-> Bitmap Index Scan on idx_session_officeid (cost=0.00..207.43 rows=11075 width=0) (actual time=0.103..0.103 rows=862 loops=1)
Index Cond: (s.officeid = $1)
-> Hash (cost=32726.06..32726.06 rows=244592 width=74) (actual time=707.589..707.589 rows=195238 loops=1)
-> Merge Join (cost=26994.35..32726.06 rows=244592 width=74) (actual time=383.882..595.784 rows=195238 loops=1)
Merge Cond: ((p.officeid = pt.officeid) AND (p.producttypeid = pt.id))
-> Sort (cost=26468.63..26956.84 rows=195284 width=33) (actual time=376.428..476.264 rows=195284 loops=1)
Sort Key: p.officeid, p.producttypeid
Sort Method: external merge Disk: 8776kB
-> Seq Scan on product p (cost=0.00..3966.84 rows=195284 width=33) (actual time=0.031..40.185 rows=195284 loops=1)
-> Sort (cost=525.72..536.77 rows=4421 width=49) (actual time=7.447..23.291 rows=199050 loops=1)
Sort Key: pt.officeid, pt.id
Sort Method: quicksort Memory: 618kB
-> Hash Left Join (cost=15.15..258.02 rows=4421 width=49) (actual time=0.194..3.094 rows=4421 loops=1)
Hash Cond: ((pt.productcategoryid = pc.id) AND (pt.officeid = pc.officeid))
-> Seq Scan on producttype pt (cost=0.00..112.21 rows=4421 width=41) (actual time=0.008..0.412 rows=4421 loops=1)
-> Hash (cost=8.46..8.46 rows=446 width=16) (actual time=0.175..0.175 rows=446 loops=1)
-> Seq Scan on productcategory pc (cost=0.00..8.46 rows=446 width=16) (actual time=0.005..0.075 rows=446 loops=1)
Total runtime: 4073.490 ms
But ordinary queries changes query plan in optimized way:
HashAggregate (cost=14152.70..14164.53 rows=947 width=72) (actual time=38.517..38.555 rows=101 loops=1)
-> Hash Left Join (cost=247.52..14119.55 rows=947 width=72) (actual time=3.163..35.021 rows=1832 loops=1)
Hash Cond: ((pt.productcategoryid = pc.id) AND (pt.officeid = pc.officeid))
-> Hash Join (cost=232.37..14076.41 rows=947 width=64) (actual time=2.984..33.823 rows=1832 loops=1)
Hash Cond: ((p.producttypeid = pt.id) AND (p.officeid = pt.officeid))
-> Nested Loop (cost=53.85..13699.42 rows=756 width=31) (actual time=0.288..29.579 rows=1833 loops=1)
-> Nested Loop (cost=53.85..8111.65 rows=756 width=48) (actual time=0.222..2.292 rows=2197 loops=1)
-> Nested Loop (cost=53.85..6293.69 rows=4 width=23) (actual time=0.216..0.661 rows=116 loops=1)
-> Seq Scan on office o (cost=0.00..4.26 rows=1 width=7) (actual time=0.013..0.020 rows=1 loops=1)
Filter: (id = 1)
-> Bitmap Heap Scan on session s (cost=53.85..6289.39 rows=4 width=20) (actual time=0.196..0.613 rows=116 loops=1)
Recheck Cond: (s.officeid = 1)
Filter: (s.actiontype = ANY ('{0,2}'::integer[]))
-> Bitmap Index Scan on idx_session_officeid (cost=0.00..53.84 rows=2864 width=0) (actual time=0.099..0.099 rows=862 loops=1)
Index Cond: (s.officeid = 1)
-> Index Scan using idx_action_sessionid on action a (cost=0.00..452.13 rows=189 width=29) (actual time=0.004..0.010 rows=19 loops=116)
Index Cond: (a.sessionid = s.id)
-> Index Scan using product_pkey on product p (cost=0.00..7.38 rows=1 width=33) (actual time=0.011..0.011 rows=1 loops=2197)
Index Cond: ((p.tagid)::text = (a.productid)::text)
-> Hash (cost=112.21..112.21 rows=4421 width=41) (actual time=2.686..2.686 rows=4421 loops=1)
-> Seq Scan on producttype pt (cost=0.00..112.21 rows=4421 width=41) (actual time=0.003..1.169 rows=4421 loops=1)
-> Hash (cost=8.46..8.46 rows=446 width=16) (actual time=0.173..0.173 rows=446 loops=1)
-> Seq Scan on productcategory pc (cost=0.00..8.46 rows=446 width=16) (actual time=0.003..0.067 rows=446 loops=1)
Total runtime: 38.728 ms