I was working on optimising a query, with dumb luck I tried something and it improved the query but I am unable to explain why.
Below is the query with poor performance
with ctedata1 as(
select
sum(total_visit_count) as total_visit_count,
sum(sh_visit_count) as sh_visit_count,
sum(ec_visit_count) as ec_visit_count,
sum(total_like_count) as total_like_count,
sum(sh_like_count) as sh_like_count,
sum(ec_like_count) as ec_like_count,
sum(total_order_count) as total_order_count,
sum(sh_order_count) as sh_order_count,
sum(ec_order_count) as ec_order_count,
sum(total_sales_amount) as total_sales_amount,
sum(sh_sales_amount) as sh_sales_amount,
sum(ec_sales_amount) as ec_sales_amount,
sum(ec_order_online_count) as ec_order_online_count,
sum(ec_sales_online_amount) as ec_sales_online_amount,
sum(ec_order_in_store_count) as ec_order_in_store_count,
sum(ec_sales_in_store_amount) as ec_sales_in_store_amount,
table2.im_name,
table2.brand as kpibrand,
table2.id_region as kpiregion
from
table2
where
deleted_at is null
and id_region = any('{1}')
group by
im_name,
kpiregion,
kpibrand ),
ctedata2 as (
select
ctedata1.*,
rank() over (partition by (kpiregion,
kpibrand)
order by
coalesce(ctedata1.total_sales_amount, 0) desc) rank,
count(*) over (partition by (kpiregion,
kpibrand)) as total_count
from
ctedata1 )
select
table1.id_pf_item,
table1.product_id,
table1.color_code,
table1.l1_code,
table1.local_title as product_name,
table1.id_region,
table1.gender,
case
when table1.created_at is null then '1970/01/01 00:00:00'
else table1.created_at
end as created_at,
(
select
count(distinct id_outfit)
from
table3
left join table4 on
table3.id_item = table4.id_item
and table4.deleted_at is null
where
table3.deleted_at is null
and table3.id_pf_item = table1.id_pf_item) as outfit_count,
count(*) over() as total_matched,
case
when table1.v8_im_name = '' then table1.im_name
else table1.v8_im_name
end as im_name,
case
when table1.id_region != 1 then null
else
case
when table1.sales_start_at is null then '1970/01/01 00:00:00'
else table1.sales_start_at
end
end as sales_start_date,
table1.category_ids,
array_to_string(table1.intermediate_category_ids, ','),
table1.image_url,
table1.brand,
table1.pdp_url,
coalesce(ctedata2.total_visit_count, 0) as total_visit_count,
coalesce(ctedata2.sh_visit_count, 0) as sh_visit_count,
coalesce(ctedata2.ec_visit_count, 0) as ec_visit_count,
coalesce(ctedata2.total_like_count, 0) as total_like_count,
coalesce(ctedata2.sh_like_count, 0) as sh_like_count,
coalesce(ctedata2.ec_like_count, 0) as ec_like_count,
coalesce(ctedata2.total_order_count, 0) as total_order_count,
coalesce(ctedata2.sh_order_count, 0) as sh_order_count,
coalesce(ctedata2.ec_order_count, 0) as ec_order_count,
coalesce(ctedata2.total_sales_amount, 0) as total_sales_amount,
coalesce(ctedata2.sh_sales_amount, 0) as sh_sales_amount,
coalesce(ctedata2.ec_sales_amount, 0) as ec_sales_amount,
coalesce(ctedata2.ec_order_online_count, 0) as ec_order_online_count,
coalesce(ctedata2.ec_sales_online_amount, 0) as ec_sales_online_amount,
coalesce(ctedata2.ec_order_in_store_count, 0) as ec_order_in_store_count,
coalesce(ctedata2.ec_sales_in_store_amount, 0) as ec_sales_in_store_amount,
ctedata2.rank,
ctedata2.total_count,
table1.department,
table1.seasons
from
table1
left join ctedata2 on
table1.im_name = ctedata2.im_name
and table1.brand = ctedata2.kpibrand
where
table1.deleted_at is null
and table1.id_region = any('{1}')
and lower(table1.brand) = any('{"brand1","brand2"}')
and 'season1' = any(lower(seasons::text)::text[])
and table1.department = 'Department1'
order by
total_sales_amount desc offset 0
limit 100
The explain output for above query is
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=172326.55..173435.38 rows=1 width=952) (actual time=85664.201..85665.970 rows=100 loops=1)
CTE ctedata1
-> GroupAggregate (cost=0.42..80478.71 rows=43468 width=530) (actual time=0.063..708.069 rows=73121 loops=1)
Group Key: table2.im_name, table2.id_region, table2.brand
-> Index Scan using udx_table2_im_name_id_region_brand_target_date_key on table2 (cost=0.42..59699.18 rows=391708 width=146) (actual time=0.029..308.582 rows=391779 loops=1)
Filter: ((deleted_at IS NULL) AND (id_region = ANY ('{1}'::integer[])))
Rows Removed by Filter: 20415
CTE ctedata2
-> WindowAgg (cost=16104.06..17842.78 rows=43468 width=628) (actual time=1012.994..1082.057 rows=73121 loops=1)
-> WindowAgg (cost=16104.06..17082.09 rows=43468 width=620) (actual time=945.755..1014.656 rows=73121 loops=1)
-> Sort (cost=16104.06..16212.73 rows=43468 width=612) (actual time=945.747..963.254 rows=73121 loops=1)
Sort Key: ctedata1.kpiregion, ctedata1.kpibrand, (COALESCE(ctedata1.total_sales_amount, '0'::numeric)) DESC
Sort Method: external merge Disk: 6536kB
-> CTE Scan on ctedata1 (cost=0.00..869.36 rows=43468 width=612) (actual time=0.069..824.841 rows=73121 loops=1)
-> Result (cost=74005.05..75113.88 rows=1 width=952) (actual time=85664.199..85665.950 rows=100 loops=1)
-> Sort (cost=74005.05..74005.05 rows=1 width=944) (actual time=85664.072..85664.089 rows=100 loops=1)
Sort Key: (COALESCE(ctedata2.total_sales_amount, '0'::numeric)) DESC
Sort Method: top-N heapsort Memory: 76kB
-> WindowAgg (cost=10960.95..74005.04 rows=1 width=944) (actual time=85658.049..85661.393 rows=3151 loops=1)
-> Nested Loop Left Join (cost=10960.95..74005.02 rows=1 width=927) (actual time=1075.219..85643.595 rows=3151 loops=1)
Join Filter: (((table1.im_name)::text = ctedata2.im_name) AND ((table1.brand)::text = ctedata2.kpibrand))
Rows Removed by Join Filter: 230402986
-> Bitmap Heap Scan on table1 (cost=10960.95..72483.64 rows=1 width=399) (actual time=45.466..278.376 rows=3151 loops=1)
Recheck Cond: (id_region = ANY ('{1}'::integer[]))
Filter: ((deleted_at IS NULL) AND (department = 'Department1'::text) AND (lower((brand)::text) = ANY ('{brand1, brand2}'::text[])) AND ('season1'::text = ANY ((lower((seasons)::text))::text[])))
Rows Removed by Filter: 106335
Heap Blocks: exact=42899
-> Bitmap Index Scan on table1_im_name_id_region_key (cost=0.00..10960.94 rows=110619 width=0) (actual time=38.307..38.307 rows=109486 loops=1)
Index Cond: (id_region = ANY ('{1}'::integer[]))
-> CTE Scan on ctedata2 (cost=0.00..869.36 rows=43468 width=592) (actual time=0.325..21.721 rows=73121 loops=3151)
SubPlan 3
-> Aggregate (cost=1108.80..1108.81 rows=1 width=8) (actual time=0.018..0.018 rows=1 loops=100)
-> Nested Loop Left Join (cost=5.57..1108.57 rows=93 width=4) (actual time=0.007..0.016 rows=3 loops=100)
-> Bitmap Heap Scan on table3 (cost=5.15..350.95 rows=93 width=4) (actual time=0.005..0.008 rows=3 loops=100)
Recheck Cond: (id_pf_item = table1.id_pf_item)
Filter: (deleted_at IS NULL)
Heap Blocks: exact=107
-> Bitmap Index Scan on idx_id_pf_item (cost=0.00..5.12 rows=93 width=0) (actual time=0.003..0.003 rows=3 loops=100)
Index Cond: (id_pf_item = table1.id_pf_item)
-> Index Scan using index_table4_id_item on table4 (cost=0.42..8.14 rows=1 width=8) (actual time=0.002..0.002 rows=0 loops=303)
Index Cond: (table3.id_item = id_item)
Filter: (deleted_at IS NULL)
Rows Removed by Filter: 0
Planning time: 1.023 ms
Execution time: 85669.512 ms
I changed
and lower(table1.brand) = any('{"brand1","brand2"}')
in the query to
and table1.brand = any('{"Brand1","Brand2"}')
and the plan changed to
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=173137.44..188661.06 rows=14 width=952) (actual time=1444.123..1445.653 rows=100 loops=1)
CTE ctedata1
-> GroupAggregate (cost=0.42..80478.71 rows=43468 width=530) (actual time=0.040..769.982 rows=73121 loops=1)
Group Key: table2.im_name, table2.id_region, table2.brand
-> Index Scan using udx_table2_item_im_name_id_region_brand_target_date_key on table2 (cost=0.42..59699.18 rows=391708 width=146) (actual time=0.021..350.774 rows=391779 loops=1)
Filter: ((deleted_at IS NULL) AND (id_region = ANY ('{1}'::integer[])))
Rows Removed by Filter: 20415
CTE ctedata2
-> WindowAgg (cost=16104.06..17842.78 rows=43468 width=628) (actual time=1088.905..1153.749 rows=73121 loops=1)
-> WindowAgg (cost=16104.06..17082.09 rows=43468 width=620) (actual time=1020.017..1089.117 rows=73121 loops=1)
-> Sort (cost=16104.06..16212.73 rows=43468 width=612) (actual time=1020.011..1037.170 rows=73121 loops=1)
Sort Key: ctedata1.kpiregion, ctedata1.kpibrand, (COALESCE(ctedata1.total_sales_amount, '0'::numeric)) DESC
Sort Method: external merge Disk: 6536kB
-> CTE Scan on ctedata1 (cost=0.00..869.36 rows=43468 width=612) (actual time=0.044..891.653 rows=73121 loops=1)
-> Result (cost=74815.94..90339.56 rows=14 width=952) (actual time=1444.121..1445.635 rows=100 loops=1)
-> Sort (cost=74815.94..74815.98 rows=14 width=944) (actual time=1444.053..1444.065 rows=100 loops=1)
Sort Key: (COALESCE(ctedata2.total_sales_amount, '0'::numeric)) DESC
Sort Method: top-N heapsort Memory: 76kB
-> WindowAgg (cost=72207.31..74815.68 rows=14 width=944) (actual time=1439.128..1441.885 rows=3151 loops=1)
-> Hash Right Join (cost=72207.31..74815.40 rows=14 width=927) (actual time=1307.531..1437.246 rows=3151 loops=1)
Hash Cond: ((ctedata2.im_name = (table1.im_name)::text) AND (ctedata2.kpibrand = (table1.brand)::text))
-> CTE Scan on ctedata2 (cost=0.00..869.36 rows=43468 width=592) (actual time=1088.911..1209.646 rows=73121 loops=1)
-> Hash (cost=72207.10..72207.10 rows=14 width=399) (actual time=216.850..216.850 rows=3151 loops=1)
Buckets: 4096 (originally 1024) Batches: 1 (originally 1) Memory Usage: 1249kB
-> Bitmap Heap Scan on table1 (cost=10960.95..72207.10 rows=14 width=399) (actual time=46.434..214.246 rows=3151 loops=1)
Recheck Cond: (id_region = ANY ('{1}'::integer[]))
Filter: ((deleted_at IS NULL) AND (department = 'Department1'::text) AND ((brand)::text = ANY ('{Brand1, Brand2}'::text[])) AND ('season1'::text = ANY ((lower((seasons)::text))::text[])))
Rows Removed by Filter: 106335
Heap Blocks: exact=42899
-> Bitmap Index Scan on table1_im_name_id_region_key (cost=0.00..10960.94 rows=110619 width=0) (actual time=34.849..34.849 rows=109486 loops=1)
Index Cond: (id_region = ANY ('{1}'::integer[]))
SubPlan 3
-> Aggregate (cost=1108.80..1108.81 rows=1 width=8) (actual time=0.015..0.015 rows=1 loops=100)
-> Nested Loop Left Join (cost=5.57..1108.57 rows=93 width=4) (actual time=0.006..0.014 rows=3 loops=100)
-> Bitmap Heap Scan on table3 (cost=5.15..350.95 rows=93 width=4) (actual time=0.004..0.006 rows=3 loops=100)
Recheck Cond: (id_pf_item = table1.id_pf_item)
Filter: (deleted_at IS NULL)
Heap Blocks: exact=107
-> Bitmap Index Scan on idx_id_pf_item (cost=0.00..5.12 rows=93 width=0) (actual time=0.003..0.003 rows=3 loops=100)
Index Cond: (id_pf_item = table1.id_pf_item)
-> Index Scan using index_table4_id_item on table4 (cost=0.42..8.14 rows=1 width=8) (actual time=0.002..0.002 rows=0 loops=303)
Index Cond: (table3.id_item = id_item)
Filter: (deleted_at IS NULL)
Rows Removed by Filter: 0
Planning time: 0.760 ms
Execution time: 1448.848 ms
My Observation
The join strategy for table1 left join ctedata2 changes after the lower() function is avoided. The strategy changes from nested loop left join to hash right join.
The CTE Scan node on ctedata2 is executed only once in the better performing query.
Postgres Version
9.6
Please help me to understand this behaviour. I will supply additional info if required.
It is almost not worthwhile taking a deep dive into the inner workings of a nearly-obsolete version. That time and energy is probably better spent jollying along an upgrade.
But the problem is pretty plain. Your scan on table1 is estimated dreadfully, although 14 times less dreadful in the better plan.
-> Bitmap Heap Scan on table1 (cost=10960.95..72483.64 rows=1 width=399) (actual time=45.466..278.376 rows=3151 loops=1)
-> Bitmap Heap Scan on table1 (cost=10960.95..72207.10 rows=14 width=399) (actual time=46.434..214.246 rows=3151 loops=1)
Your use of lower(), apparently without reason, surely contributes to the poor estimation. And dynamically converting a string into an array certainly doesn't help either. If it were stored as a real array in the first place, the statistics system could get its hands on it and generate more reasonable estimates.
SELECT a1.object_id,
(SELECT COALESCE(json_agg(b1), '[]')
FROM (
SELECT c1.root_id,
(SELECT COALESCE(json_agg(d1), '[]')
FROM (
SELECT e1.root_id,
(SELECT COALESCE(json_agg(f1), '[]')
FROM (
SELECT g1.root_id,
(SELECT SUM(h2.amount)
FROM table_5 AS h1
LEFT OUTER JOIN table_6 AS h2 on h1.hash_id = h2.hash_id
WHERE g1.object_id = h1.root_id
AND h2.mode = 'Real'
AND h2.type = 'Reject'
) AS amount
FROM table_4 AS g1
LEFT OUTER JOIN table_5 AS g2
ON g1.general_id = g2.object_id
LEFT OUTER JOIN table_6 AS g3
ON g1.properties_hash_id = g3.hash_id
WHERE e1.object_id = g1.root_id
) AS f1) AS tickets
FROM table_3 AS e1
WHERE c1.object_id = e1.root_id
) AS d1) AS blocks
FROM table_2 AS c1
WHERE a1.object_id = c1.root_id
) AS b1) AS sources
FROM table_1 AS a1
With AND h2.type = 'Reject' line the query takes ~5 Seconds
...
SubPlan 1
-> Aggregate (cost=22.77..22.78 rows=1 width=32) (actual time=0.296..0.296 rows=1 loops=16502)
-> Nested Loop (cost=10.72..22.76 rows=1 width=6) (actual time=0.184..0.295 rows=1 loops=16502)
-> Index Scan using t041_type_idx on table_6 h2 (cost=0.15..8.17 rows=1 width=10) (actual time=0.001..0.007 rows=20 loops=16502)
Index Cond: (type = 'Reject'::valid_optimized_segment_types)
Filter: (mode = 'Real'::valid_optimized_segment_modes)
Rows Removed by Filter: 20
-> Bitmap Heap Scan on table_5 h1 (cost=10.57..14.58 rows=1 width=4) (actual time=0.012..0.012 rows=0 loops=330040)
Recheck Cond: ((g1.object_id = root_id) AND (hash_id = h2.hash_id))
Heap Blocks: exact=12379
-> BitmapAnd (cost=10.57..10.57 rows=1 width=0) (actual time=0.011..0.011 rows=0 loops=330040)
-> Bitmap Index Scan on t031_root_id_fkey (cost=0.00..4.32 rows=3 width=0) (actual time=0.001..0.001 rows=2 loops=330040)
Index Cond: (root_id = g1.object_id)
-> Bitmap Index Scan on t031_md5_id_idx (cost=0.00..6.00 rows=228 width=0) (actual time=0.010..0.010 rows=619 loops=330040)
Index Cond: (hash_id = h2.hash_id)
Planning Time: 0.894 ms
Execution Time: 4925.776 ms
Without AND h2.type = 'Reject' line the query takes ~770ms
SubPlan 1
-> Aggregate (cost=25.77..25.78 rows=1 width=32) (actual time=0.045..0.046 rows=1 loops=16502)
-> Hash Join (cost=15.32..25.77 rows=2 width=6) (actual time=0.019..0.041 rows=1 loops=16502)
Hash Cond: (h2.hash_id = h1.hash_id)
-> Seq Scan on table_6 h2 (cost=0.00..8.56 rows=298 width=10) (actual time=0.001..0.026 rows=285 loops=16502)
Filter: (mode = 'Real'::valid_optimized_segment_modes)
Rows Removed by Filter: 80
-> Hash (cost=15.28..15.28 rows=3 width=4) (actual time=0.002..0.002 rows=2 loops=16502)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Index Scan using t031_root_id_fkey on table_5 h1 (cost=0.29..15.28 rows=3 width=4) (actual time=0.001..0.002 rows=2 loops=16502)
Index Cond: (root_id = g1.object_id)
Planning Time: 0.889 ms
Execution Time: 787.264 ms
Why is this single line causing so much differences (h2.type is
BTREE indexed)?
How else can I achieve the same result with better efficiency?
-> Index Scan using t041_type_idx on table_6 h2 (cost=0.15..8.17 rows=1 width=10) (actual time=0.001..0.007 rows=20 loops=16502)
It thinks there will be 1 row, and there are 20. Meaning the next node is executed 20 times more than it thought it would be. If it knew here would be 20, it might have chosen a different plan.
You could try to create cross-column statistics so it can get a better estimate for that row count.
Or you could just speed up the next node, by adding the multi column index on table_5 (root_id,hash_id). It would still get executed far more times than the planner thinks it will, but each execution will be faster.
Or you might just force it into a different plan by making one of those indexes unusable:
...JOIN table_6 AS h2 on h1.hash_id+0 = h2.hash_id
I use a simple query with a WHERE on a fixed value. This query does a left join on a temporary view. For some reason this query is performing very badly. I guess that the view is being executed for every row and not only for the selected rows. When I replace the fixed value with a value from a temporary table, the query performs MUCH better (about 15-20 times faster). Why is this?
I use postgresql version 9.2.15.
I added a temporary table 'wrkovz91-selecties' with only 1 record to pass the selection-value of the WHERE instruction to the query.
The view 'view_wrk_012_000001' that is joined in the query is pretty 'heavy', because it contains a nested other view ('view_wrk_013_000006').
First create the temporary table and add one record:
CREATE TEMPORARY TABLE WRKOVZ91_SELECTIES
(
NR DECIMAL(006) NOT NULL,
KLANTNR DECIMAL(008),
CONSTRAINT WRKOVZ91_SELECTIES_KEY_001 PRIMARY KEY (NR)
);
INSERT INTO WRKOVZ91_SELECTIES
(NR, KLANTNR) VALUES (1, 1);
Then create the temporary view (with a nested view):
CREATE TEMPORARY VIEW view_wrk_013_000006 AS
SELECT vrr.klantnr AS klantnr,
UPPER(vrr.artnr) AS artnr,
vrr.partijnr AS partijnr,
SUM(vrr.kollo) AS colli_op_voorraad
FROM voorraad vrr
WHERE (vrr.status = 'A'::text)
GROUP BY 1,
2,
3;
CREATE TEMPORARY VIEW view_wrk_012_000001 AS
SELECT COALESCE(wrd.klantnr,0) AS klantnr,
COALESCE(wrd.wrkopdrnr,0) AS wrkopdrnr,
MIN(COALESCE(wrd.recept_benodigd_colli,0) *
COALESCE(wrk.te_produceren,0)) AS vrije_voorraad_ind,
MIN(COALESCE(v40.colli_op_voorraad,0)) AS hulpveld
FROM wrkopdr wrk
LEFT JOIN wrkopdrd wrd ON wrd.klantnr = wrk.klantnr
AND wrd.wrkopdrnr = wrk.wrkopdrnr
LEFT JOIN view_wrk_013_000006 v40 ON v40.klantnr =
COALESCE(wrd.klantnr_grondstof,0)
AND v40.artnr = COALESCE(wrd.artnr_grondstof,'')
AND v40.partijnr = COALESCE(wrd.partijnr_grondstof,0)
LEFT JOIN artikel art ON art.klantnr = COALESCE(wrd.klantnr_grondstof,0)
AND art.artnr = COALESCE(wrd.artnr_grondstof,'')
WHERE wrk.status = 'A'::text
AND art.voorraadhoudend_jn = 'J'::text
GROUP BY 1,
2;
The query that performs badly is simple:
SELECT WRK.KLANTNR,
WRK.WRKOPDRNR,
WRK.MIL_UITVOER_DATUM
FROM WRKOPDR WRK
LEFT JOIN VIEW_WRK_012_000001 V38 ON V38.KLANTNR = WRK.KLANTNR AND
V38.WRKOPDRNR = WRK.WRKOPDRNR
LEFT JOIN WRKOVZ91_SELECTIES S02 ON S02.NR = 1
WHERE WRK.KLANTNR = 1
LIMIT 9999;
The query that performs MUCH better is (note the small difference):
SELECT WRK.KLANTNR,
WRK.WRKOPDRNR,
WRK.MIL_UITVOER_DATUM
FROM WRKOPDR WRK
LEFT JOIN VIEW_WRK_012_000001 V38 ON V38.KLANTNR = WRK.KLANTNR AND
V38.WRKOPDRNR = WRK.WRKOPDRNR
LEFT JOIN WRKOVZ91_SELECTIES S02 ON S02.NR = 1
WHERE WRK.KLANTNR = S02.KLANTNR
LIMIT 9999;
I cannot understand why the slow query is performing so badly. It takes in my test-data about 219 secondes. The fast query is taking only 12 secondes. The only difference between the 2 queries is the selection-value in the WHERE.
Does anyone have an explanation for this behaviour?
In addition, the output of the explain analyse of the slow query is:
Limit (cost=19460.18..19972.73 rows=9999 width=18) (actual time=221573.343..221585.953 rows=9999 loops=1)
-> Hash Left Join (cost=19460.18..20913.07 rows=28344 width=18) (actual time=221573.341..221583.701 rows=9999 loops=1)
Hash Cond: ((wrk.klantnr = v38.klantnr) AND (wrk.wrkopdrnr = v38.wrkopdrnr))
-> Seq Scan on wrkopdr wrk (cost=0.00..1240.30 rows=28344 width=18) (actual time=0.055..5.490 rows=9999 loops=1)
Filter: (klantnr = 1::numeric)
-> Hash (cost=19460.17..19460.17 rows=1 width=64) (actual time=221573.254..221573.254 rows=2621 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 113kB
-> Subquery Scan on v38 (cost=19460.15..19460.17 rows=1 width=64) (actual time=221570.429..221572.158 rows=2621 loops=1)
-> HashAggregate (cost=19460.15..19460.16 rows=1 width=52) (actual time=221570.429..221571.499 rows=2621 loops=1)
-> Nested Loop Left Join (cost=14049.90..19460.14 rows=1 width=52) (actual time=225.848..221495.813 rows=6801 loops=1)
Join Filter: ((vrr.klantnr = COALESCE(wrd.klantnr_grondstof, 0::numeric)) AND ((upper(vrr.artnr)) = COALESCE(wrd.artnr_grondstof, ''::text)) AND (vrr.partijnr = COALESCE(wrd.partijnr_grondstof, 0::numeric)))
Rows Removed by Join Filter: 308209258
-> Nested Loop (cost=1076.77..6011.60 rows=1 width=43) (actual time=9.506..587.824 rows=6801 loops=1)
-> Hash Right Join (cost=1076.77..5828.70 rows=69 width=43) (actual time=9.428..204.601 rows=7861 loops=1)
Hash Cond: ((wrd.klantnr = wrk.klantnr) AND (wrd.wrkopdrnr = wrk.wrkopdrnr))
Filter: (COALESCE(wrd.klantnr, 0::numeric) = 1::numeric)
Rows Removed by Filter: 1
-> Seq Scan on wrkopdrd wrd (cost=0.00..3117.73 rows=116873 width=38) (actual time=0.013..65.472 rows=116873 loops=1)
-> Hash (cost=1026.34..1026.34 rows=3362 width=16) (actual time=9.324..9.324 rows=3362 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 161kB
-> Bitmap Heap Scan on wrkopdr wrk (cost=98.31..1026.34 rows=3362 width=16) (actual time=0.850..7.843 rows=3362 loops=1)
Recheck Cond: (status = 'A'::text)
-> Bitmap Index Scan on wrkopdr_key_002 (cost=0.00..97.47 rows=3362 width=0) (actual time=0.763..0.763 rows=3362 loops=1)
Index Cond: (status = 'A'::text)
-> Index Scan using artikel_key_001 on artikel art (cost=0.00..2.64 rows=1 width=17) (actual time=0.037..0.043 rows=1 loops=7861)
Index Cond: ((klantnr = COALESCE(wrd.klantnr_grondstof, 0::numeric)) AND (artnr = COALESCE(wrd.artnr_grondstof, ''::text)))
Filter: (voorraadhoudend_jn = 'J'::text)
Rows Removed by Filter: 0
-> HashAggregate (cost=12973.14..13121.70 rows=11885 width=25) (actual time=0.027..19.661 rows=45319 loops=6801)
-> Seq Scan on voorraad vrr (cost=0.00..12340.71 rows=63243 width=25) (actual time=0.456..122.855 rows=62655 loops=1)
Filter: (status = 'A'::text)
Rows Removed by Filter: 113953
Total runtime: 221587.386 ms
(33 rows)
The output of the explain analyse of the fast query is:
Limit (cost=56294.55..57997.06 rows=142 width=18) (actual time=445.371..12739.474 rows=9999 loops=1)
-> Nested Loop Left Join (cost=56294.55..57997.06 rows=142 width=18) (actual time=445.368..12736.035 rows=9999 loops=1)
Join Filter: ((v38.klantnr = wrk.klantnr) AND (v38.wrkopdrnr = wrk.wrkopdrnr))
Rows Removed by Join Filter: 26206807
-> Nested Loop (cost=0.00..1532.01 rows=142 width=18) (actual time=0.055..18.652 rows=9999 loops=1)
Join Filter: (wrk.klantnr = s02.klantnr)
-> Index Scan using wrkovz91_selecties_key_001 on wrkovz91_selecties s02 (cost=0.00..8.27 rows=1 width=14) (actual time=0.021..0.021 rows=1 loops=1)
Index Cond: (nr = 1::numeric)
-> Seq Scan on wrkopdr wrk (cost=0.00..1169.44 rows=28344 width=18) (actual time=0.026..11.905 rows=9999 loops=1)
-> Materialize (cost=56294.55..56296.25 rows=68 width=64) (actual time=0.044..0.380 rows=2621 loops=9999)
-> Subquery Scan on v38 (cost=56294.55..56295.91 rows=68 width=64) (actual time=441.797..443.503 rows=2621 loops=1)
-> HashAggregate (cost=56294.55..56295.23 rows=68 width=52) (actual time=441.795..442.848 rows=2621 loops=1)
-> Hash Left Join (cost=14525.30..56293.70 rows=68 width=52) (actual time=255.847..433.386 rows=6801 loops=1)
Hash Cond: ((COALESCE(wrd.klantnr_grondstof, 0::numeric) = v40.klantnr) AND (COALESCE(wrd.artnr_grondstof, ''::text) = v40.artnr) AND (COALESCE(wrd.partijnr_grondstof, 0::numeric) = v40.partijnr))
-> Nested Loop (cost=1076.77..42541.70 rows=68 width=43) (actual time=10.356..171.502 rows=6801 loops=1)
-> Hash Right Join (cost=1076.77..5794.04 rows=13863 width=43) (actual time=10.286..91.471 rows=7862 loops=1)
Hash Cond: ((wrd.klantnr = wrk.klantnr) AND (wrd.wrkopdrnr = wrk.wrkopdrnr))
-> Seq Scan on wrkopdrd wrd (cost=0.00..3117.73 rows=116873 width=38) (actual time=0.014..38.276 rows=116873 loops=1)
-> Hash (cost=1026.34..1026.34 rows=3362 width=16) (actual time=10.179..10.179 rows=3362 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 161kB
-> Bitmap Heap Scan on wrkopdr wrk (cost=98.31..1026.34 rows=3362 width=16) (actual time=0.835..8.667 rows=3362 loops=1)
Recheck Cond: (status = 'A'::text)
-> Bitmap Index Scan on wrkopdr_key_002 (cost=0.00..97.47 rows=3362 width=0) (actual time=0.748..0.748 rows=3362 loops=1)
Index Cond: (status = 'A'::text)
-> Index Scan using artikel_key_001 on artikel art (cost=0.00..2.64 rows=1 width=17) (actual time=0.009..0.009 rows=1 loops=7862)
Index Cond: ((klantnr = COALESCE(wrd.klantnr_grondstof, 0::numeric)) AND (artnr = COALESCE(wrd.artnr_grondstof, ''::text)))
Filter: (voorraadhoudend_jn = 'J'::text)
Rows Removed by Filter: 0
-> Hash (cost=13240.55..13240.55 rows=11885 width=74) (actual time=245.430..245.430 rows=45319 loops=1)
Buckets: 2048 Batches: 1 Memory Usage: 2645kB
-> Subquery Scan on v40 (cost=12973.14..13240.55 rows=11885 width=74) (actual time=186.156..222.042 rows=45319 loops=1)
-> HashAggregate (cost=12973.14..13121.70 rows=11885 width=25) (actual time=186.154..209.633 rows=45319 loops=1)
-> Seq Scan on voorraad vrr (cost=0.00..12340.71 rows=63243 width=25) (actual time=0.453..126.361 rows=62655 loops=1)
Filter: (status = 'A'::text)
Rows Removed by Filter: 113953
Total runtime: 12742.125 ms
(36 rows)
this is my query. I have more than 600000 rows of my table sale. I think the numbers of data is not too much, but it takes approximately 50s.
Here's the query.
I need faster.
SELECT
st.product_id,
prd.price_in_stock,
prd.product_name_eng,
prd.currency_id,
prd.product_name_kh,
(SELECT quantity
FROM daily_stock
WHERE stock_id = st.stock_id
AND DATE (stock_date) = DATE (now())
) AS pre_stock_quantity,
(SELECT SUM (trrc.quantity)
FROM transfer trs
INNER JOIN transfer_detail trd
ON trs.transfer_id = trd.transfer_id
INNER JOIN transfer_received trrc
ON trrc.transfer_detail_id = trd.transfer_detail_id
WHERE
trs.transfer_from = st.branch_id
AND trd.product_id = st.product_id
AND DATE (trrc.received_date) = DATE (NOW())
) AS trasfered_quantity,
(SELECT SUM (trrc.quantity)
FROM transfer trs
INNER JOIN transfer_detail trd
ON trs.transfer_id = trd.transfer_id
INNER JOIN transfer_received trrc
ON trrc.transfer_detail_id = trd.transfer_detail_id
WHERE
trs.transfer_to = st.branch_id
AND trd.product_id = st.product_id
AND DATE (trrc.received_date) = DATE (NOW())
) AS received_quantity,
(SELECT (SUM (smallest_devisor (sd.product_id)
* sd.quantity
/ getDevisor (sd.product_id, sd.unit_id)))
/ smallest_devisor (sd.product_id) AS sold_quantity
FROM
sale_detail sd
INNER JOIN sale sa ON sa.sale_id = sd.sale_id
WHERE
sa.branch_id = st.branch_id
AND sd.product_id = st.product_id
AND DATE (sa.sale_date) = DATE (NOW())
GROUP BY
sd.product_id
),
(SELECT (SUM(smallest_devisor (sd.product_id)
* rp.quantity
/ getDevisor (sd.product_id, sd.unit_id))
) / smallest_devisor (sd.product_id) AS returned_quantity
FROM
returned_product rp
INNER JOIN sale_detail sd ON sd.sale_detail_id = rp.sale_detail_id
WHERE
rp.branch_id = st.branch_id
AND sd.product_id = st.product_id
AND DATE (rp.returned_date) = DATE (NOW())
GROUP BY
sd.product_id
),
(
SELECT
SUM (quantity) AS imported_quantity
FROM
import
WHERE
branch_id = st.branch_id
AND product_id = st.product_id
AND DATE (import_date) = DATE (NOW())
),
st.quantity AS post_stock_quantity
FROM
stock st
INNER JOIN product prd ON prd.product_id = st.product_id
LEFT JOIN import imp ON imp.product_id = st.product_id
WHERE
st.branch_id = 'BR0000';
analyze is here, I've seen the nested loop has taken cost much, but I don't know what to do more. I don't understand well about them, pls explain me some.
Hash Left Join (cost=2.45..210929.10 rows=4 width=99) (actual time=845.040..68556.076 rows=22 loops=1)
Hash Cond: ((st.product_id)::text = (imp.product_id)::text)
Buffers: shared hit=2362098 read=499796, temp read=63910 written=63781
-> Hash Join (cost=1.11..11.36 rows=4 width=99) (actual time=0.130..0.160 rows=5 loops=1)
Hash Cond: ((st.product_id)::text = (prd.product_id)::text)
Buffers: shared hit=11
-> Seq Scan on stock st (cost=0.00..10.19 rows=4 width=23) (actual time=0.025..0.041 rows=5 loops=1)
Filter: ((branch_id)::text = 'BR0000'::text)
Rows Removed by Filter: 10
Buffers: shared hit=10
-> Hash (cost=1.05..1.05 rows=5 width=114) (actual time=0.058..0.058 rows=5 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
Buffers: shared hit=1
-> Seq Scan on product prd (cost=0.00..1.05 rows=5 width=114) (actual time=0.037..0.041 rows=5 loops=1)
Buffers: shared hit=1
-> Hash (cost=1.15..1.15 rows=15 width=38) (actual time=0.041..0.041 rows=22 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
Buffers: shared hit=1
-> Seq Scan on import imp (cost=0.00..1.15 rows=15 width=38) (actual time=0.009..0.019 rows=22 loops=1)
Buffers: shared hit=1
SubPlan 1
-> Seq Scan on daily_stock (cost=0.00..48.25 rows=1 width=8) (actual time=0.010..0.011 rows=1 loops=22)
Filter: ((stock_id = st.stock_id) AND (date(stock_date) = date(now())))
Rows Removed by Filter: 29
Buffers: shared hit=22
SubPlan 2
-> Aggregate (cost=52.41..52.42 rows=1 width=8) (actual time=0.010..0.010 rows=1 loops=22)
-> Nested Loop (cost=0.15..52.41 rows=1 width=8) (actual time=0.008..0.008 rows=0 loops=22)
-> Nested Loop (cost=0.00..45.56 rows=1 width=60) (actual time=0.001..0.001 rows=0 loops=22)
Join Filter: (trd.transfer_detail_id = trrc.transfer_detail_id)
-> Seq Scan on transfer_received trrc (cost=0.00..28.00 rows=4 width=12) (actual time=0.000..0.000 rows=0 loops=22)
Filter: (date(received_date) = date(now()))
-> Materialize (cost=0.00..17.39 rows=3 width=56) (never executed)
-> Seq Scan on transfer_detail trd (cost=0.00..17.38 rows=3 width=56) (never executed)
Filter: ((product_id)::text = (st.product_id)::text)
-> Index Scan using transfer_pkey on transfer trs (cost=0.15..6.83 rows=1 width=52) (never executed)
Index Cond: ((transfer_id)::text = (trd.transfer_id)::text)
Filter: ((transfer_from)::text = (st.branch_id)::text)
SubPlan 3
-> Aggregate (cost=52.41..52.42 rows=1 width=8) (actual time=0.007..0.007 rows=1 loops=22)
-> Nested Loop (cost=0.15..52.41 rows=1 width=8) (actual time=0.005..0.005 rows=0 loops=22)
-> Nested Loop (cost=0.00..45.56 rows=1 width=60) (actual time=0.000..0.000 rows=0 loops=22)
Join Filter: (trd_1.transfer_detail_id = trrc_1.transfer_detail_id)
-> Seq Scan on transfer_received trrc_1 (cost=0.00..28.00 rows=4 width=12) (actual time=0.000..0.000 rows=0 loops=22)
Filter: (date(received_date) = date(now()))
-> Materialize (cost=0.00..17.39 rows=3 width=56) (never executed)
-> Seq Scan on transfer_detail trd_1 (cost=0.00..17.38 rows=3 width=56) (never executed)
Filter: ((product_id)::text = (st.product_id)::text)
-> Index Scan using transfer_pkey on transfer trs_1 (cost=0.15..6.83 rows=1 width=52) (never executed)
Index Cond: ((transfer_id)::text = (trd_1.transfer_id)::text)
Filter: ((transfer_to)::text = (st.branch_id)::text)
SubPlan 4
-> GroupAggregate (cost=32059.02..52563.95 rows=1 width=18) (actual time=3116.048..3116.048 rows=0 loops=22)
Group Key: sd.product_id
Buffers: shared hit=2362020 read=499796, temp read=63910 written=63781
-> Hash Join (cost=32059.02..52045.08 rows=1007 width=18) (actual time=830.903..988.696 rows=53521 loops=22)
Hash Cond: ((sd.sale_id)::text = (sa.sale_id)::text)
Buffers: shared hit=7084 read=499796, temp read=63910 written=63781
-> Seq Scan on sale_detail sd (cost=0.00..19220.90 rows=201358 width=35) (actual time=0.079..193.761 rows=292880 loops=22)
Filter: ((product_id)::text = (st.product_id)::text)
Rows Removed by Filter: 512552
Buffers: shared hit=3520 read=197846
-> Hash (cost=32008.68..32008.68 rows=4027 width=17) (actual time=552.758..552.758 rows=147183 loops=22)
Buckets: 131072 (originally 4096) Batches: 4 (originally 1) Memory Usage: 3137kB
Buffers: shared hit=3564 read=301950, temp written=10934
-> Seq Scan on sale sa (cost=0.00..32008.68 rows=4027 width=17) (actual time=400.100..500.394 rows=147183 loops=22)
Filter: (((branch_id)::text = (st.branch_id)::text) AND (date(sale_date) = date(now())))
Rows Removed by Filter: 658225
Buffers: shared hit=3564 read=301950
SubPlan 5
-> GroupAggregate (cost=0.42..10.66 rows=1 width=18) (actual time=0.041..0.041 rows=0 loops=22)
Group Key: sd_1.product_id
Buffers: shared hit=22
-> Nested Loop (cost=0.42..9.88 rows=1 width=18) (actual time=0.040..0.040 rows=0 loops=22)
Buffers: shared hit=22
-> Seq Scan on returned_product rp (cost=0.00..1.43 rows=1 width=12) (actual time=0.026..0.026 rows=0 loops=22)
Filter: (((branch_id)::text = (st.branch_id)::text) AND (date(returned_date) = date(now())))
Rows Removed by Filter: 19
Buffers: shared hit=22
-> Index Scan using sale_detail_pkey on sale_detail sd_1 (cost=0.42..8.45 rows=1 width=18) (never executed)
Index Cond: (sale_detail_id = rp.sale_detail_id)
Filter: ((product_id)::text = (st.product_id)::text)
SubPlan 6
-> Aggregate (cost=1.38..1.39 rows=1 width=8) (actual time=0.027..0.027 rows=1 loops=22)
Buffers: shared hit=22
-> Seq Scan on import (cost=0.00..1.38 rows=1 width=8) (actual time=0.018..0.018 rows=0 loops=22)
Filter: (((branch_id)::text = (st.branch_id)::text) AND ((product_id)::text = (st.product_id)::text) AND (date(import_date) = date(now())))
Rows Removed by Filter: 22
Buffers: shared hit=22
Planning time: 4.046 ms
Execution time: 68557.361 ms
How can we optimize the following query:
select *
from program_infos pi
join endeavour_organizations seller_organization on seller_organization.id = pi.supplier_id
join endeavour_organizations obligor_organization on obligor_organization.id = pi.buyer_id
join invoices i on pi.program_id = i.program_id
join assets fa on fa.invoice_id = i.id and fa.owner_id=pi.fi_id
join assets sa on sa.invoice_id = i.id and sa.owner_id=pi.supplier_id;
The corresponding Explain Analyze is :
Nested Loop (cost=36.94..70919.65 rows=505 width=793) (actual time=0.263..1729.519 rows=267238 loops=1)
-> Nested Loop (cost=36.79..70806.58 rows=505 width=718) (actual time=0.261..1405.417 rows=267238 loops=1)
Join Filter: ((i.id = fa.invoice_id) AND (pi.fi_id = fa.owner_id))
Rows Removed by Join Filter: 400287
-> Hash Join (cost=36.37..69201.99 rows=2567 width=626) (actual time=0.255..772.895 rows=248735 loops=1)
Hash Cond: (pi.supplier_id = seller_organization.id)
-> Hash Join (cost=27.52..68973.45 rows=15977 width=551) (actual time=0.202..672.442 rows=248735 loops=1)
Hash Cond: ((sa.owner_id = pi.supplier_id) AND (i.program_id = pi.program_id))
-> Merge Join (cost=1.29..63781.02 rows=667525 width=288) (actual time=0.021..496.274 rows=667525 loops=1)
Merge Cond: (i.id = sa.invoice_id)
-> Index Scan using invoices_pkey on invoices i (cost=0.42..27363.52 rows=249447 width=196) (actual time=0.004..60.598 rows=249440 loops=1)
-> Index Scan using index_assets_invoice on assets sa (cost=0.42..27450.72 rows=667525 width=92) (actual time=0.014..147.276 rows=667525 loops=1)
-> Hash (cost=20.09..20.09 rows=409 width=263) (actual time=0.176..0.176 rows=409 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 133kB
-> Seq Scan on program_infos pi (cost=0.00..20.09 rows=409 width=263) (actual time=0.001..0.064 rows=409 loops=1)
-> Hash (cost=5.60..5.60 rows=260 width=75) (actual time=0.049..0.049 rows=260 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 27kB
-> Seq Scan on endeavour_organizations seller_organization (cost=0.00..5.60 rows=260 width=75) (actual time=0.006..0.019 rows=260 loops=1)
-> Index Scan using index_assets_owner_invoice on assets fa (cost=0.42..0.57 rows=4 width=92) (actual time=0.001..0.002 rows=3 loops=248735)
Index Cond: (invoice_id = sa.invoice_id)
-> Index Scan using endeavour_organizations_pkey on endeavour_organizations obligor_organization (cost=0.15..0.21 rows=1 width=75) (actual time=0.001..0.001 rows=1 loops=267238)
Index Cond: (id = pi.buyer_id)
Planning time: 3.194 ms
Execution time: 1740.875 ms
(24 rows)
Indexes are on pi.program_id, pi.fi_id, pi.supplier_id, asset.invoice_id, i.program_id
I am not able to understand why its doing nested loop. Please let me know if something else is needed.