Identical query 1000x times slower in production than development - postgresql

I have a query. In production env it takes around 15seconds, while in development (using a almost up to date snapshot of the production database), it takes no longer than 100ms.
The database is not really under heavy load, less than 50 operations/second (probably less)
Production:
Nested Loop Anti Join (cost=999.94..2116.89 rows=1 width=2171) (actual time=16922.305..16922.305 rows=0 loops=1)
Join Filter: (auto_message_events.auto_message_id = a0.id)
Rows Removed by Join Filter: 1683
-> Nested Loop Left Join (cost=225.57..1311.15 rows=1 width=2235) (actual time=37.732..130.746 rows=5049 loops=1)
7.600..114.552 rows=51 loops=1)=202.12..329.72 rows=1 width=2235) (actual time=3--More--
-> Nested Loop (cost=0.28..86.34 rows=3 width=2194) (actual time=0.905..1.088 rows=3 loops=1)
-> Seq Scan on auto_messages a0 (cost=0.00..61.42 rows=3 width=2171) (actual time=0.880..1.009 rows=3 loops=1)
Filter: (((status)::text = 'live'::text) AND ((channel)::text = 'email'::text))
Rows Removed by Filter: 425
-> Index Only Scan using apps_pkey on apps a1 (cost=0.28..8.29 rows=1 width=23) (actual time=0.014..0.017 rows=1 loops=3)
Index Cond: (id = (a0.app_id)::text)
Heap Fetches: 0
-> Hash Join (cost=201.84..215.68 rows=1 width=313) (actual time=37.603..37.783 rows=17 loops=3)
Hash Cond: ((find_matching_visitors.id)::text = (v2.id)::text)
Join Filter: CASE WHEN a0.window_enabled THEN ((date_part('dow'::text, timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), now())) = ANY ((a0.window_days_of_week)::double precision[])) AND (timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) >= (a0.window_start)::time with time zone) AND (timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) <= (a0.window_end)::time with time zone)) ELSE true END
Rows Removed by Join Filter: 12
-> Function Scan on find_matching_visitors (cost=0.25..10.25 rows=1000 width=32) (actual time=29.358..29.393 rows=31 loops=3)
-> Hash (cost=201.54..201.54 rows=4 width=313) (actual time=8.037..8.037 rows=881 loops=3)
Buckets: 1024 Batches: 1 Memory Usage: 299kB
-> Index Scan using visitors_app_id_signed_up_index on visitors v2 (cost=0.55..201.54 rows=4 width=313) (actual time=0.036..7.259 rows=881 loops=3)
Index Cond: ((app_id)::text = (a1.id)::text)
Filter: ((NOT merged) AND (email IS NOT NULL) AND (NOT unsubscribed) AND (((type)::text = 'user'::text) OR ((type)::text = 'lead'::text)))
Rows Removed by Filter: 1558
-> Bitmap Heap Scan on auto_message_events a3 (cost=23.46..977.51 rows=392 width=4) (actual time=0.035..0.198 rows=99 loops=51)
Recheck Cond: (auto_message_id = a0.id)
Heap Blocks: exact=2499
-> Bitmap Index Scan on auto_message_events_auto_message_id_visitor_id_event_index (cost=0.00..23.36 rows=392 width=0) (actual time=0.023..0.023 rows=99 loops=51)
Index Cond: (auto_message_id = a0.id)
h=73) (actual time=3.320..3.320 rows=1 loops=5049)ost=774.36..790.02 rows=6 widt--More--
Recheck Cond: ((((visitor_id)::text = (v2.id)::text) AND ((event)::text = 'sent'::text)) OR (((event)::text = 'sent'::text) AND ((visitor_user_id)::text = (v2.user_id)::text)) OR (((event)::text = 'sent'::text) AND ((visitor_email)::text = (v2.email)::text)))
Heap Blocks: exact=5940
-> BitmapOr (cost=774.36..774.36 rows=6 width=0) (actual time=3.316..3.316 rows=0 loops=5049)
-> Bitmap Index Scan on auto_message_events_auto_message_id_visitor_id_event_index (cost=0.00..746.48 rows=1 width=0) (actual time=3.088..3.088 rows=2 loops=5049)
Index Cond: (((visitor_id)::text = (v2.id)::text) AND ((event)::text = 'sent'::text))
-> Bitmap Index Scan on auto_message_id_event_visitor_user_id_idx (cost=0.00..13.42 rows=3 width=0) (actual time=0.113..0.113 rows=2 loops=5049)
Index Cond: (((event)::text = 'sent'::text) AND ((visitor_user_id)::text = (v2.user_id)::text))
-> Bitmap Index Scan on auto_message_id_event_visitor_email_idx (cost=0.00..14.46 rows=3 width=0) (actual time=0.110..0.110 rows=2 loops=5049)
Index Cond: (((event)::text = 'sent'::text) AND ((visitor_email)::text = (v2.email)::text))
Planning time: 4.171 ms
Execution time: 16922.671 ms
Development
QUERY PLAN
Nested Loop Anti Join (cost=32.20..1602.94 rows=1 width=2175) (actual time=57.971..57.971 rows=0 loops=1)
Join Filter: (((auto_message_events.visitor_id)::text = (v2.id)::text) OR ((auto_message_events.visitor_user_id)::text = (v2.user_id)::text) OR ((auto_message_events.visitor_email)::text = (v2.email)::text))
Rows Removed by Join Filter: 4095
-> Nested Loop Left Join (cost=16.29..595.98 rows=1 width=2736) (actual time=37.186..52.374 rows=91 loops=1)
-> Nested Loop (cost=1.22..40.02 rows=1 width=2736) (actual time=37.134..52.245 rows=1 loops=1)
Join Filter: ((v2.id)::text = (find_matching_visitors.id)::text)
Rows Removed by Join Filter: 152
-> Nested Loop (cost=0.97..17.27 rows=1 width=2736) (actual time=0.258..0.621 rows=3 loops=1)
Join Filter: CASE WHEN a0.window_enabled THEN ((date_part('dow'::text, timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), now())) = ANY ((a0.window_days_of_week)::double precision[])) AND (timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) >= (a0.window_start)::time with time zone) AND (timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) <= (a0.window_end)::time with time zone)) ELSE true END
Rows Removed by Join Filter: 6
-> Nested Loop (cost=0.70..16.75 rows=1 width=834) (actual time=0.105..0.196 rows=3 loops=1)
-> Index Scan using email_idx on visitors v2 (cost=0.42..8.45 rows=1 width=810) (actual time=0.084..0.127 rows=3 loops=1)
Index Cond: (email IS NOT NULL)
Filter: ((NOT merged) AND (NOT unsubscribed) AND (((type)::text = 'user'::text) OR ((type)::text = 'lead'::text)))
-> Index Only Scan using apps_pkey on apps a1 (cost=0.28..8.29 rows=1 width=24) (actual time=0.019..0.020 rows=1 loops=3)
Index Cond: (id = (v2.app_id)::text)
Heap Fetches: 3
-> Index Scan using auto_messages_app_id_user_id_title_index on auto_messages a0 (cost=0.27..0.44 rows=1 width=2175) (actual time=0.057..0.100 rows=3 loops=3)
Index Cond: ((app_id)::text = (a1.id)::text)
Filter: (((status)::text = 'live'::text) AND ((channel)::text = 'email'::text))
Rows Removed by Filter: 49
-> Function Scan on find_matching_visitors (cost=0.25..10.25 rows=1000 width=32) (actual time=17.191..17.195 rows=51 loops=3)
-> Bitmap Heap Scan on auto_message_events a3 (cost=15.07..552.54 rows=342 width=4) (actual time=0.043..0.080 rows=91 loops=1)
Recheck Cond: (auto_message_id = a0.id)
Heap Blocks: exact=26
-> Bitmap Index Scan on auto_message_events_auto_message_id_visitor_id_event_index (cost=0.00..14.98 rows=342 width=0) (actual time=0.031..0.031 rows=91 loops=1)
Index Cond: (auto_message_id = a0.id)
-> Bitmap Heap Scan on auto_message_events (cost=15.91..508.98 rows=281 width=73) (actual time=0.022..0.040 rows=46 loops=91)
Recheck Cond: ((auto_message_id = a0.id) AND ((event)::text = 'sent'::text))
Heap Blocks: exact=1820
-> Bitmap Index Scan on auto_message_events_auto_message_id_visitor_id_event_index (cost=0.00..15.84 rows=281 width=0) (actual time=0.020..0.020 rows=52 loops=91)
Index Cond: ((auto_message_id = a0.id) AND ((event)::text = 'sent'::text))
Planning time: 4.882 ms
Execution time: 58.174 ms
UPDATE
Dynamic query function used in join:
CREATE OR REPLACE FUNCTION find_matching_visitors(app_id text, default_filters text[], custom_filters text[])
RETURNS TABLE (
id varchar
) AS
$body$
DECLARE
default_filterstring text;
custom_filterstring text;
default_filter_length integer;
custom_filter_length integer;
sql VARCHAR;
BEGIN
default_filter_length := COALESCE(array_length(default_filters, 1), 0);
custom_filter_length := COALESCE(array_length(custom_filters, 1), 0);
default_filterstring := array_to_string(default_filters, ' AND ');
custom_filterstring := array_to_string(custom_filters, ' AND ');
IF custom_filterstring = '' or custom_filterstring is null THEN
custom_filterstring := '1=1';
END IF;
IF default_filterstring = '' or default_filterstring is null THEN
default_filterstring := '1=1';
END IF;
sql := format('
SELECT v.id FROM visitors v
LEFT JOIN trackings t on v.id = t.visitor_id
WHERE v.app_id = ''%s''
group by v.id
having (%s) AND (%s)
', app_id, custom_filterstring, default_filterstring);
RETURN QUERY EXECUTE sql;
END;
$body$
LANGUAGE 'plpgsql';
Any ideas?

Related

How to optimize this PostgreSQL query

I have a question regarding PostgreSQL query speed optimizations.
I tried with Indexes and I speed up some queries, but for this one I don't know why it's still slow.
With this explain (https://explain.dalibo.com/plan/8ace3g496e6112f5), I see that the issues are on the Index Scan for User and Location, but I have indexes and it's still very slow.
I don't know why my tables or Indexes can't be fully in memory.
Query Text: SELECT "mygreatapp"."User"."id", "mygreatapp"."User"."createdAt", "mygreatapp"."User"."updatedAt", "mygreatapp"."User"."lastLogin", "mygreatapp"."User"."signupCompleted", "mygreatapp"."User"."visible", "mygreatapp"."User"."deleted", "mygreatapp"."User"."pushNotificationsToken", "mygreatapp"."User"."email", "mygreatapp"."User"."password", "mygreatapp"."User"."facebookUserId", "mygreatapp"."User"."name", "mygreatapp"."User"."birthday", "mygreatapp"."User"."birthdayString", "mygreatapp"."User"."bmi", "mygreatapp"."User"."height", "mygreatapp"."User"."weight", "mygreatapp"."User"."children", "mygreatapp"."User"."locale", "mygreatapp"."User"."timeZone", "mygreatapp"."User"."newMatchNotification", "mygreatapp"."User"."messageNotification", "mygreatapp"."User"."suggestionNotification", "mygreatapp"."User"."likeScore", "mygreatapp"."User"."likeNotificationFrequency", "mygreatapp"."User"."matchNotificationFrequency", "mygreatapp"."User"."messageNotificationFrequency", "mygreatapp"."User"."moderated", "mygreatapp"."User"."moderationDate", "mygreatapp"."User"."moderatedBy", "mygreatapp"."User"."isGrazer", "mygreatapp"."User"."blockedUntil", "mygreatapp"."User"."blockedBy", "mygreatapp"."User"."gender", "mygreatapp"."User"."emailStatus", "mygreatapp"."User"."emailOptIn", "mygreatapp"."User"."authProvider", "mygreatapp"."User"."role", "mygreatapp"."User"."drinking", "mygreatapp"."User"."eyesColor", "mygreatapp"."User"."hairColor", "mygreatapp"."User"."studyLevel", "mygreatapp"."User"."smoking", "mygreatapp"."User"."ethnia", "mygreatapp"."User"."religion", "mygreatapp"."User"."maritalSituation", "mygreatapp"."User"."conjugalSituation", "mygreatapp"."User"."blocked", "mygreatapp"."User"."lastLocationId", "mygreatapp"."User"."hiddenUniverses", "mygreatapp"."User"."jwtVersion", "mygreatapp"."User"."ipAddress", "mygreatapp"."User"."ipAddressCountry", "mygreatapp"."User"."ipAddressInEurope", "mygreatapp"."User"."ipAddressScore", "mygreatapp"."User"."phoneOperator" FROM "mygreatapp"."User" WHERE ("mygreatapp"."User"."signupCompleted" = $1 AND "mygreatapp"."User"."visible" = $2 AND "mygreatapp"."User"."blocked" IS NULL AND "mygreatapp"."User"."deleted" = $3 AND "mygreatapp"."User"."id" <> $4 AND ("mygreatapp"."User"."id") NOT IN (SELECT "t0"."id" FROM "mygreatapp"."User" AS "t0" INNER JOIN "mygreatapp"."Like" AS "j0" ON ("j0"."userId") = ("t0"."id") WHERE ("j0"."likedUserId" = $5 AND "j0"."status" = $6 AND "t0"."id" IS NOT NULL)) AND ("mygreatapp"."User"."id") NOT IN (SELECT "t0"."id" FROM "mygreatapp"."User" AS "t0" INNER JOIN "mygreatapp"."Like" AS "j0" ON ("j0"."likedUserId") = ("t0"."id") WHERE ((("j0"."userId" = $7 AND "j0"."status" = $8) OR ("j0"."universe" = $9 AND "j0"."userId" = $10)) AND "t0"."id" IS NOT NULL)) AND ("mygreatapp"."User"."id") IN (SELECT "t0"."id" FROM "mygreatapp"."User" AS "t0" INNER JOIN "mygreatapp"."UniverseOnUser" AS "j0" ON ("j0"."userId") = ("t0"."id") WHERE (("j0"."userId","j0"."universeId") IN (SELECT "t1"."userId", "t1"."universeId" FROM "mygreatapp"."UniverseOnUser" AS "t1" INNER JOIN "mygreatapp"."Universe" AS "j1" ON ("j1"."id") = ("t1"."universeId") WHERE ("j1"."id" = $11 AND "t1"."userId" IS NOT NULL AND "t1"."universeId" IS NOT NULL)) AND "j0"."relation" = $12 AND "t0"."id" IS NOT NULL)) AND "mygreatapp"."User"."gender" IN ($13,$14) AND "mygreatapp"."User"."religion" IN ($15,$16,$17,$18,$19,$20,$21,$22,$23) AND ("mygreatapp"."User"."id") NOT IN (SELECT "t0"."id" FROM "mygreatapp"."User" AS "t0" INNER JOIN "mygreatapp"."LocationOnUser" AS "j0" ON ("j0"."userId") = ("t0"."id") WHERE ("j0"."exclude" = $24 AND ("j0"."id") IN (SELECT "t1"."id" FROM "mygreatapp"."LocationOnUser" AS "t1" INNER JOIN "mygreatapp"."Bound" AS "j1" ON ("j1"."id") = ("t1"."boundId") WHERE ("j1"."maxLng" >= $25 AND "j1"."minLng" <= $26 AND "j1"."maxLat" >= $27 AND "j1"."minLat" <= $28 AND "t1"."id" IS NOT NULL)) AND "t0"."id" IS NOT NULL)) AND ("mygreatapp"."User"."id") IN (SELECT "t0"."id" FROM "mygreatapp"."User" AS "t0" INNER JOIN "mygreatapp"."Location" AS "j0" ON ("j0"."id") = ("t0"."lastLocationId") WHERE (((("j0"."latitude" <= $29 AND "j0"."latitude" >= $30) AND ("j0"."longitude" <= $31 AND "j0"."longitude" >= $32)) OR (("j0"."latitude" <= $33 AND "j0"."latitude" >= $34) AND ("j0"."longitude" <= $35 AND "j0"."longitude" >= $36)) OR (("j0"."latitude" <= $37 AND "j0"."latitude" >= $38) AND ("j0"."longitude" <= $39 AND "j0"."longitude" >= $40))) AND "t0"."id" IS NOT NULL))) ORDER BY "mygreatapp"."User"."id" ASC LIMIT $41 OFFSET $42
Limit (cost=6109.21..6109.24 rows=13 width=652) (actual time=482.978..482.993 rows=18 loops=1)
-> Sort (cost=6109.21..6109.24 rows=13 width=652) (actual time=482.976..482.990 rows=18 loops=1)
Sort Key: "User".id
Sort Method: quicksort Memory: 36kB
-> Nested Loop Semi Join (cost=1947.03..6108.97 rows=13 width=652) (actual time=80.857..482.921 rows=18 loops=1)
Join Filter: ("User".id = j0."userId")
-> Nested Loop Semi Join (cost=1946.16..4945.48 rows=231 width=679) (actual time=1.442..386.430 rows=1704 loops=1)
-> Bitmap Heap Scan on "User" (cost=1945.46..2700.02 rows=476 width=652) (actual time=1.353..7.950 rows=4281 loops=1)
Recheck Cond: ((blocked IS NULL) AND (gender = ANY ('{MALE,FEMALE}'::"Gender"[])) AND (religion = ANY ('{BUDDHIST,CATHOLIC,HINDU,JEW,MUSLIM,NONE,PROTESTANT,ORTHODOX,ORTHODOX}'::"Religion"[])))
Filter: ("signupCompleted" AND visible AND (NOT deleted) AND (id <> 'ckki8m4y81jew0792sw40q81w'::text) AND (NOT (hashed SubPlan 1)) AND (NOT (hashed SubPlan 2)) AND (NOT (hashed SubPlan 3)))
Rows Removed by Filter: 56
Heap Blocks: exact=621
-> Bitmap Index Scan on "User.signupCompleted_visible_blocked_deleted_gender_religion_in" (cost=0.00..129.45 rows=3809 width=0) (actual time=0.227..0.227 rows=4655 loops=1)
Index Cond: (("signupCompleted" = true) AND (visible = true) AND (blocked IS NULL) AND (deleted = false) AND (gender = ANY ('{MALE,FEMALE}'::"Gender"[])) AND (religion = ANY ('{BUDDHIST,CATHOLIC,HINDU,JEW,MUSLIM,NONE,PROTESTANT,ORTHODOX,ORTHODOX}'::"Religion"[])))
SubPlan 1
-> Nested Loop (cost=25.28..37.31 rows=1 width=27) (actual time=0.178..0.610 rows=59 loops=1)
-> Bitmap Heap Scan on "Like" j0_2 (cost=24.99..29.01 rows=1 width=26) (actual time=0.163..0.239 rows=59 loops=1)
Recheck Cond: (("likedUserId" = 'ckki8m4y81jew0792sw40q81w'::text) AND (status = 'LIKED'::"LikeStatus"))
Heap Blocks: exact=58
-> BitmapAnd (cost=24.99..24.99 rows=1 width=0) (actual time=0.154..0.155 rows=0 loops=1)
-> Bitmap Index Scan on "Like.likedUserId_index" (cost=0.00..4.71 rows=38 width=0) (actual time=0.014..0.014 rows=141 loops=1)
Index Cond: ("likedUserId" = 'ckki8m4y81jew0792sw40q81w'::text)
-> Bitmap Index Scan on "Like.status_universe_index" (cost=0.00..20.04 rows=1549 width=0) (actual time=0.132..0.132 rows=1559 loops=1)
Index Cond: (status = 'LIKED'::"LikeStatus")
-> Index Only Scan using "User.id_lastLogin_blocked_isGrazer_index" on "User" t0_2 (cost=0.29..8.30 rows=1 width=27) (actual time=0.005..0.005 rows=1 loops=59)
Index Cond: ((id = j0_2."userId") AND (id IS NOT NULL))
Heap Fetches: 59
SubPlan 2
-> Nested Loop (cost=102.16..1611.05 rows=168 width=27) (actual time=0.054..0.284 rows=33 loops=1)
-> Bitmap Heap Scan on "Like" j0_3 (cost=101.87..667.81 rows=168 width=26) (actual time=0.042..0.083 rows=33 loops=1)
Recheck Cond: ((("userId" = 'ckki8m4y81jew0792sw40q81w'::text) AND (status = 'LIKED'::"LikeStatus")) OR (("userId" = 'ckki8m4y81jew0792sw40q81w'::text) AND (universe = 'vegan'::text)))
Heap Blocks: exact=30
-> BitmapOr (cost=101.87..101.87 rows=169 width=0) (actual time=0.035..0.036 rows=0 loops=1)
-> Bitmap Index Scan on "Like.userId_status_universe_index" (cost=0.00..4.84 rows=42 width=0) (actual time=0.011..0.011 rows=24 loops=1)
Index Cond: (("userId" = 'ckki8m4y81jew0792sw40q81w'::text) AND (status = 'LIKED'::"LikeStatus"))
-> Bitmap Index Scan on "Like.userId_status_universe_index" (cost=0.00..96.95 rows=127 width=0) (actual time=0.023..0.024 rows=9 loops=1)
Index Cond: (("userId" = 'ckki8m4y81jew0792sw40q81w'::text) AND (universe = 'vegan'::text))
-> Index Only Scan using "User_pkey" on "User" t0_3 (cost=0.29..5.61 rows=1 width=27) (actual time=0.005..0.005 rows=1 loops=33)
Index Cond: ((id = j0_3."likedUserId") AND (id IS NOT NULL))
Heap Fetches: 33
SubPlan 3
-> Nested Loop (cost=74.27..167.09 rows=7 width=27) (actual time=0.103..0.106 rows=0 loops=1)
-> Nested Loop (cost=73.99..143.13 rows=7 width=27) (actual time=0.103..0.105 rows=0 loops=1)
-> HashAggregate (cost=73.70..75.32 rows=162 width=26) (actual time=0.102..0.105 rows=0 loops=1)
Group Key: t1_1.id
Batches: 1 Memory Usage: 40kB
-> Merge Join (cost=28.97..73.30 rows=162 width=26) (actual time=0.100..0.101 rows=0 loops=1)
Merge Cond: (t1_1."boundId" = j1_1.id)
-> Index Scan using "LocationOnUser.boundId_unique" on "LocationOnUser" t1_1 (cost=0.29..778.03 rows=15178 width=53) (actual time=0.025..0.057 rows=46 loops=1)
Filter: (id IS NOT NULL)
-> Sort (cost=28.68..28.71 rows=13 width=27) (actual time=0.027..0.028 rows=2 loops=1)
Sort Key: j1_1.id
Sort Method: quicksort Memory: 25kB
-> Bitmap Heap Scan on "Bound" j1_1 (cost=11.19..28.44 rows=13 width=27) (actual time=0.020..0.021 rows=2 loops=1)
Recheck Cond: (("maxLng" >= '5.531'::double precision) AND ("minLng" <= '5.531'::double precision) AND ("maxLat" >= '49.15'::double precision) AND ("minLat" <= '49.15'::double precision))
Heap Blocks: exact=2
-> Bitmap Index Scan on "Bound.maxLng_minLng_maxLat_minLat_index" (cost=0.00..11.19 rows=13 width=0) (actual time=0.017..0.017 rows=2 loops=1)
Index Cond: (("maxLng" >= '5.531'::double precision) AND ("minLng" <= '5.531'::double precision) AND ("maxLat" >= '49.15'::double precision) AND ("minLat" <= '49.15'::double precision))
-> Index Scan using "LocationOnUser_pkey" on "LocationOnUser" j0_4 (cost=0.29..0.42 rows=1 width=53) (never executed)
Index Cond: (id = t1_1.id)
Filter: exclude
-> Index Only Scan using "User_pkey" on "User" t0_4 (cost=0.29..3.42 rows=1 width=27) (never executed)
Index Cond: ((id = j0_4."userId") AND (id IS NOT NULL))
Heap Fetches: 0
-> Nested Loop (cost=0.70..4.71 rows=1 width=27) (actual time=0.088..0.088 rows=0 loops=4281)
-> Index Scan using "User_pkey" on "User" t0_1 (cost=0.29..3.83 rows=1 width=53) (actual time=0.018..0.036 rows=1 loops=4281)
Index Cond: ((id = "User".id) AND (id IS NOT NULL))
-> Index Scan using "Location_pkey" on "Location" j0_1 (cost=0.41..0.88 rows=1 width=26) (actual time=0.043..0.043 rows=0 loops=4281)
Index Cond: (id = t0_1."lastLocationId")
Filter: (((latitude <= '48.91064321183747'::double precision) AND (latitude >= '45.31335678816254'::double precision) AND (longitude <= '3.257852869245691'::double precision) AND (longitude >= '-2.025852869245691'::double precision)) OR ((latitude <= '50.94464321183747'::double precision) AND (latitude >= '47.34735678816255'::double precision) AND (longitude <= '8.155450105467445'::double precision) AND (longitude >= '2.658549894532557'::double precision)) OR ((latitude <= '50.94864321183746'::double precision) AND (latitude >= '47.35135678816253'::double precision) AND (longitude <= '8.279671659522354'::double precision) AND (longitude >= '2.782328340477646'::double precision)))
Rows Removed by Filter: 1
-> Nested Loop (cost=0.86..5.02 rows=1 width=79) (actual time=0.056..0.056 rows=0 loops=1704)
Join Filter: (j0."userId" = t0.id)
-> Nested Loop Semi Join (cost=0.58..4.47 rows=1 width=52) (actual time=0.056..0.056 rows=0 loops=1704)
Join Filter: (j0."userId" = t1."userId")
-> Index Scan using "UniverseOnUser.userId_index" on "UniverseOnUser" j0 (cost=0.29..1.59 rows=1 width=52) (actual time=0.050..0.050 rows=0 loops=1704)
Index Cond: ("userId" = t0_1.id)
Filter: ((relation = 'FRIENDSHIP'::"Relation") AND ("universeId" = 'cjzx66jhlh8gx0974trtqhkb4'::text))
Rows Removed by Filter: 7
-> Nested Loop (cost=0.29..2.87 rows=1 width=52) (actual time=0.018..0.018 rows=1 loops=18)
-> Index Scan using "UniverseOnUser.userId_index" on "UniverseOnUser" t1 (cost=0.29..1.59 rows=1 width=52) (actual time=0.010..0.010 rows=1 loops=18)
Index Cond: (("userId" = t0_1.id) AND ("userId" IS NOT NULL))
Filter: (("universeId" IS NOT NULL) AND ("universeId" = 'cjzx66jhlh8gx0974trtqhkb4'::text))
Rows Removed by Filter: 4
-> Seq Scan on "Universe" j1 (cost=0.00..1.26 rows=1 width=26) (actual time=0.004..0.004 rows=1 loops=18)
Filter: (id = 'cjzx66jhlh8gx0974trtqhkb4'::text)
Rows Removed by Filter: 6
-> Index Only Scan using "User_pkey" on "User" t0 (cost=0.29..0.54 rows=1 width=27) (actual time=0.006..0.006 rows=1 loops=18)
Index Cond: ((id = t0_1.id) AND (id IS NOT NULL))
Heap Fetches: 18
Thanks a lot.
Regards
Your request is very large, it will take time to review it in detail. I can just give you some recommendations.
It is recommended to use CTE (with as) to make your query readable and simple.
If there are duplicates between your subqueries, write them as (with as materialized).
If you have OR conditions in queries, use UNION instead of OR.
If you have queries that return large results in your subqueries after the IN command, then use INNER JOIN instead of IN. Apply the same logic to NOT IN if you can.
And lastly, of course, pay attention to the correct indexing of the fields you use in the conditions.

How does a string operation on a column in a filter condition of a Postgresql query have on the plan it chooses

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.

index only scan taking longer to run

In the below execution plan, the index scan on five_lima (table has 900m records) is where it's spending most of its time. I want to bring down the runtime to few seconds, how do I optimize it? Tried forcing seq scan and ran vacuum/analyze but it is not helping.
As per explain analysis from depesz, the index scan on five_lima is spending 86% of time.
five_lima 2 43,600.875 ms 86.6 %
Index Only Scan Backward 1 21,936.780 ms 50.3 %
Index Scan 1 21,664.095 ms 49.7 %
https://explain.depesz.com/s/7lXg
GroupAggregate (cost=5236122.79..5238409.75 rows=19058 width=392) (actual time=50337.968..50338.284 rows=76 loops=1)
Group Key: ((((((three.papa)::text || 'sierra_tango'::text) || (quebec_three.mike_india)::text) || bravo_five((quebec_three.sierra_uniform)::text, 3, 'november_golf'::text)) || 'lima_charlie'::text)), quebec_three.mike_india, quebec_three.sierra_uniform
-> Sort (cost=5236122.79..5236170.44 rows=19058 width=120) (actual time=50337.880..50337.903 rows=773 loops=1)
Sort Key: ((((((three.papa)::text || 'sierra_tango'::text) || (quebec_three.mike_india)::text) || bravo_five((quebec_three.sierra_uniform)::text, 3, 'november_golf'::text)) || 'lima_charlie'::text)), quebec_three.mike_india, quebec_three.sierra_uniform
Sort Method: quicksort Memory: 142kB
-> Hash Left Join (cost=5221327.29..5234767.95 rows=19058 width=120) (actual time=49423.721..50337.319 rows=773 loops=1)
Hash Cond: (((quebec_three.mike_india)::bpchar = three.mike_india) AND (quebec_three.sierra_uniform = three.sierra_uniform))
-> GroupAggregate (cost=5221204.51..5233639.85 rows=19058 width=292) (actual time=49422.982..50336.121 rows=773 loops=1)
Group Key: quebec_three.mike_india, quebec_three.sierra_uniform, quebec_three.whiskey, quebec_three.tango, quebec_three.juliet_charlie, quebec_three.victor_papa, quebec_three.yankee, quebec_three.india_papa, quebec_three.victor_charlie, quebec_three.november_hotel, quebec_three.hotel_november
-> Sort (cost=5221204.51..5221680.96 rows=190580 width=228) (actual time=49408.728..49416.532 rows=250551 loops=1)
Sort Key: quebec_three.mike_india, quebec_three.sierra_uniform, quebec_three.whiskey, quebec_three.tango, quebec_three.juliet_charlie, quebec_three.victor_papa, quebec_three.yankee, quebec_three.india_papa, quebec_three.victor_charlie, quebec_three.november_hotel, quebec_three.hotel_november
Sort Method: quicksort Memory: 27472kB
-> Subquery Scan on quebec_three (cost=5191626.46..5204490.61 rows=190580 width=228) (actual time=49045.224..49167.610 rows=250551 loops=1)
-> Unique (cost=5191626.46..5198773.21 rows=190580 width=286) (actual time=49045.204..49136.969 rows=250551 loops=1)
-> Sort (cost=5191626.46..5192102.91 rows=190580 width=286) (actual time=49045.190..49071.536 rows=252496 loops=1)
Sort Key: mike_november1.sierra_uniform, mike_november1.charlie_six, mike_november1.foxtrot_india, (xray(mike_november1.delta_xray, 'zulu'::text)), mike_november1.whiskey, quebec_sierra.tango, quebec_sierra.juliet_charlie, golf.oscar_lima, (CASE WHEN ((("six_four"((five_hotel.tango)::text, 2))::integer = 5) AND (five_hotel.juliet_charlie <> november_november ('charlie_tango'::bpchar[]))) THEN 'oscar_romeo'::text ELSE NULL::text END), (CASE WHEN ((("six_four"((five_hotel.tango)::text, 2))::integer = 5) AND (five_hotel.juliet_charlie = ANY ('charlie_tango'::bpchar[]))) THEN 'romeo'::text ELSE NULL::text END), (CASE WHEN ((("six_four"((five_hotel.tango)::text, 2))::integer = 6) AND (five_hotel.juliet_charlie <> november_november ('charlie_tango'::bpchar[]))) THEN 'oscar_romeo'::text ELSE NULL::text END), (CASE WHEN ((("six_four"((five_hotel.tango)::text, 2))::integer = 6) AND (five_hotel.juliet_charlie = ANY ('charlie_tango'::bpchar[]))) THEN 'romeo'::text ELSE NULL::text END), (CASE WHEN (golf.oscar_lima five_romeo NOT NULL) THEN 'delta_foxtrot'::text ELSE 'oscar_romeo'::text END), (CASE WHEN (("six_four"((five_hotel.tango)::text, 2))::integer = 15) THEN 'oscar_romeo'::text ELSE NULL::text END)
Sort Method: quicksort Memory: 41652kB
-> Nested Loop Left Join (cost=661986.99..5174912.56 rows=190580 width=286) (actual time=1737.304..47625.922 rows=252496 loops=1)
-> Gather (cost=661986.29..3041816.11 rows=190580 width=79) (actual time=1733.755..1827.448 rows=252383 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Hash Left Join (cost=660986.29..3021758.11 rows=79408 width=79) (actual time=1723.881..8094.375 rows=84128 loops=3)
Hash Cond: (((mike_november1.mike_india)::text = (seven_quebec.mike_india)::text) AND ((mike_november1.foxtrot_india)::text = (seven_quebec.foxtrot_india)::text) AND (mike_november1.sierra_uniform = seven_quebec.sierra_uniform))
-> Nested Loop Left Join (cost=420698.30..2780844.78 rows=79408 width=74) (actual time=1263.213..7579.180 rows=84128 loops=3)
-> Nested Loop Left Join (cost=420697.72..2359299.65 rows=79408 width=69) (actual time=1262.995..6114.930 rows=84128 loops=3)
-> Nested Loop Left Join (cost=420697.15..1943062.02 rows=79408 width=53) (actual time=1262.617..4846.387 rows=83834 loops=3)
-> Parallel Hash Left Join (cost=420691.40..1289489.61 rows=79408 width=53) (actual time=1262.432..3244.712 rows=83191 loops=3)
Hash Cond: (((mike_november1.mike_india)::text = (five_hotel.mike_india)::text) AND ((mike_november1.foxtrot_india)::text = (five_hotel.foxtrot_india)::text) AND (mike_november1.sierra_uniform = five_hotel.sierra_uniform))
-> Parallel Index Scan using lima_papa on six_echo juliet_xray_xray (cost=0.57..867904.42 rows=79408 width=45) (actual time=1.304..1922.996 rows=83190 loops=3)
Index Cond: ((delta_xray >= 'four_kilo'::timestamp without time zone) AND (delta_xray <= 'uniform'::timestamp without time zone) AND ((mike_india)::text = 'five_papa'::text))
Filter: (oscar_quebec = 'quebec_golf'::numeric)
Rows Removed by Filter: 115955
-> Parallel Hash (cost=404875.54..404875.54 rows=421741 width=32) (actual time=1259.567..1259.567 rows=93164 loops=3)
Buckets: 1048576 Batches: 1 Memory Usage: 27936kB
-> Parallel Bitmap Heap Scan on delta_echo five_hotel (cost=87668.95..404875.54 rows=421741 width=32) (actual time=947.771..1217.127 rows=93164 loops=3)
Recheck Cond: ((mike_india)::text = 'five_papa'::text)
Heap Blocks: exact=24664
-> Bitmap Index Scan on india_three (cost=0.00..87415.90 rows=1012179 width=0) (actual time=935.805..935.805 rows=466562 loops=1)
Index Cond: ((mike_india)::text = 'five_papa'::text)
-> Bitmap Heap Scan on two_bravo juliet_xray_delta (cost=5.76..8.20 rows=1 width=32) (actual time=0.018..0.018 rows=1 loops=249572)
Recheck Cond: (((charlie_six)::text = (mike_november1.charlie_six)::text) AND ((foxtrot_india)::text = (mike_november1.foxtrot_india)::text))
Filter: (((mike_india)::text = 'five_papa'::text) AND ((mike_india)::text = (mike_november1.mike_india)::text) AND (sierra_uniform = mike_november1.sierra_uniform))
Heap Blocks: exact=16
-> BitmapAnd (cost=5.76..5.76 rows=1 width=0) (actual time=0.016..0.016 rows=0 loops=249572)
-> Bitmap Index Scan on victor_three (cost=0.00..2.74 rows=5 width=0) (actual time=0.010..0.010 rows=1 loops=249572)
Index Cond: ((charlie_six)::text = (mike_november1.charlie_six)::text)
-> Bitmap Index Scan on two_delta (cost=0.00..2.77 rows=16 width=0) (actual time=0.010..0.010 rows=1 loops=128367)
Index Cond: ((foxtrot_india)::text = (mike_november1.foxtrot_india)::text)
-> Index Scan using hotel_oscar on charlie_yankee golf (cost=0.57..5.21 rows=1 width=40) (actual time=0.015..0.015 rows=1 loops=251501)
Index Cond: ((foxtrot_india)::text = (mike_november1.foxtrot_india)::text)
Filter: (((mike_india)::text = 'five_papa'::text) AND ((mike_india)::text = (mike_november1.mike_india)::text) AND (sierra_uniform = mike_november1.sierra_uniform))
Rows Removed by Filter: 0
-> Index Scan using seven_victor on five_charlie bravo_oscar (cost=0.57..5.28 rows=1 width=23) (actual time=0.017..0.017 rows=1 loops=252383)
Index Cond: ((charlie_six)::text = (mike_november1.charlie_six)::text)
Filter: (((mike_india)::text = 'five_papa'::text) AND ((mike_india)::text = (mike_november1.mike_india)::text) AND (sierra_uniform = mike_november1.sierra_uniform))
Rows Removed by Filter: 0
-> Parallel Hash (cost=232855.56..232855.56 rows=198198 width=29) (actual time=459.697..459.697 rows=66317 loops=3)
Buckets: 524288 Batches: 1 Memory Usage: 16608kB
-> Parallel Bitmap Heap Scan on victor_four seven_quebec (cost=20722.12..232855.56 rows=198198 width=29) (actual time=111.386..434.496 rows=66317 loops=3)
Recheck Cond: ((mike_india)::text = 'five_papa'::text)
Heap Blocks: exact=27484
-> Bitmap Index Scan on four_charlie (cost=0.00..20603.20 rows=475676 width=0) (actual time=107.013..107.013 rows=227154 loops=1)
Index Cond: ((mike_india)::text = 'five_papa'::text)
-> Index Scan using hotel_whiskey on five_lima quebec_sierra (cost=0.70..11.08 rows=1 width=33) (actual time=0.179..0.180 rows=1 loops=252383)
Index Cond: (((foxtrot_india)::text = (mike_november1.foxtrot_india)::text) AND ((mike_india)::text = (mike_november1.mike_india)::text) AND ((mike_india)::text = 'five_papa'::text) AND (sierra_uniform = mike_november1.sierra_uniform))
Filter: (bravo_lima = (delta_four 2))
Rows Removed by Filter: 6
SubPlan
-> Result (cost=5.55..5.58 rows=1 width=8) (actual time=0.013..0.013 rows=1 loops=1828065)
InitPlan
-> Limit (cost=0.70..5.55 rows=1 width=8) (actual time=0.012..0.012 rows=1 loops=1828065)
-> Index Only Scan Backward using hotel_whiskey on five_lima foxtrot_four (cost=0.70..5.55 rows=1 width=8) (actual time=0.012..0.012 rows=1 loops=1828065)
Index Cond: ((foxtrot_india = (quebec_sierra.foxtrot_india)::text) AND (mike_india = (quebec_sierra.mike_india)::text) AND (sierra_uniform = quebec_sierra.sierra_uniform) AND (oscar_quebec = quebec_sierra.oscar_quebec) AND (bravo_lima five_romeo NOT NULL))
Heap Fetches: 18062
-> Hash (cost=70.67..70.67 rows=1489 width=20) (actual time=0.644..0.644 rows=1489 loops=1)
Buckets: 2048 Batches: 1 Memory Usage: 95kB
-> Seq Scan on bravo_zulu three (cost=0.00..70.67 rows=1489 width=20) (actual time=0.048..0.416 rows=1489 loops=1)
Planning time: 24.541 ms
Execution time: 50356.651 ms
Here is the query -
explain analyze select quebec_three.mike_india,quebec_three.sierra_uniform,papa ||'('||quebec_three.mike_india||bravo_five(quebec_three.sierra_uniform::text,3,'0')||')' as papa,
sum( dms_appl_pending + dms_appl_done + no_of_fee_pending + veri_appl_pending )no_of_appl_done,
sum(veri_appl_done)veri_appl_done,sum(veri_appl_rejected)veri_appl_rejected,
sum(veri_appl_pending)veri_appl_pending,sum(app_appl_done)appr_appl_done,sum(app_appl_rejected)appr_appl_rejected,
sum(app_appl_pending)appr_appl_pending
,sum(no_of_fee_pending)no_of_fee_pending,sum(no_of_fee_done)no_of_fee_done
,sum(dms_appl_pending)dms_appl_pending,sum(dms_appl_done)dms_appl_done
from(
select quebec_three.mike_india,quebec_three.sierra_uniform,
case when right(quebec_three.tango::text,2)::int=05 and quebec_three.juliet_charlie ='C' then count(distinct quebec_three.foxtrot_india) else 0 end veri_appl_done,
case when (victor_papa='R' ) then count(quebec_three.foxtrot_india) else 0 end as veri_appl_rejected,
case when (yankee='P' ) then count(distinct quebec_three.foxtrot_india) else 0 end as veri_appl_pending,
case when whiskey='A' and (right(quebec_three.tango::text,2)::int=06 and quebec_three.juliet_charlie ='C') then count(distinct quebec_three.foxtrot_india) else 0 end app_appl_done,
case when (india_papa='R') then count(distinct quebec_three.foxtrot_india) else 0 end as app_appl_rejected,
case when (victor_charlie='P') then count(distinct quebec_three.foxtrot_india) else 0 end as app_appl_pending,
case when november_hotel='P' then count(distinct quebec_three.foxtrot_india) else 0 end as no_of_fee_pending,
case when november_hotel='A' then count(distinct quebec_three.foxtrot_india) else 0 end as no_of_fee_done,
case when (hotel_november='P' ) then count(distinct quebec_three.foxtrot_india) else 0 end as dms_appl_pending,
case when right(quebec_three.tango::text,2)::int=15 and quebec_three.juliet_charlie ='C' then count(distinct quebec_three.foxtrot_india) else 0 end dms_appl_done
from(
select distinct quebec_three.mike_india,quebec_three.sierra_uniform ,quebec_three.charlie_six,quebec_three.foxtrot_india,xray(quebec_three.delta_xray,'dd-Mon-yyyy')delta_xray,quebec_three.whiskey,quebec_sierra.tango,quebec_sierra.juliet_charlie,golf.oscar_lima,
case when right(five_hotel.tango::text,2)::int=05 and five_hotel.juliet_charlie not in ('M','I') then oscar_romeo else null end yankee,
case when right(five_hotel.tango::text,2)::int=05 and five_hotel.juliet_charlie in ('M','I') then romeo else null end victor_papa,
case when right(five_hotel.tango::text,2)::int=06 and five_hotel.juliet_charlie not in ('M','I') then oscar_romeo else null end victor_charlie,
case when right(five_hotel.tango::text,2)::int=06 and five_hotel.juliet_charlie in ('M','I') then romeo else null end india_papa,
case when golf.oscar_lima is not null then delta_foxtrot else oscar_romeo end november_hotel,
case when right(five_hotel.tango::text,2)::int=15 then oscar_romeo else null end hotel_november
from six_echo quebec_three
left join delta_echo five_hotel on five_hotel.foxtrot_india=quebec_three.foxtrot_india and five_hotel.mike_india=quebec_three.mike_india and five_hotel.sierra_uniform=quebec_three.sierra_uniform
left join five_lima quebec_sierra on quebec_sierra.foxtrot_india=quebec_three.foxtrot_india and quebec_sierra.mike_india=quebec_three.mike_india and quebec_sierra.sierra_uniform=quebec_three.sierra_uniform
and quebec_sierra.bravo_lima =(select max(bravo_lima) from five_lima foxtrot_four where foxtrot_four.foxtrot_india=quebec_sierra.foxtrot_india
and foxtrot_four.mike_india=quebec_sierra.mike_india and foxtrot_four.sierra_uniform=quebec_sierra.sierra_uniform and foxtrot_four.oscar_quebec=quebec_sierra.oscar_quebec)
left join hsrp.vt_hsrp h on h.foxtrot_india=quebec_three.foxtrot_india and h.charlie_six=quebec_three.charlie_six and h.mike_india=quebec_three.mike_india and h.sierra_uniform=quebec_three.sierra_uniform
left join two_bravo juliet_xray_delta on juliet_xray_delta.foxtrot_india=quebec_three.foxtrot_india and juliet_xray_delta.charlie_six=quebec_three.charlie_six and juliet_xray_delta.mike_india=quebec_three.mike_india and juliet_xray_delta.sierra_uniform=quebec_three.sierra_uniform
left join charlie_yankee golf on golf.foxtrot_india=quebec_three.foxtrot_india and golf.mike_india=quebec_three.mike_india and golf.sierra_uniform=quebec_three.sierra_uniform
left join five_charlie bravo_oscar on bravo_oscar.charlie_six=quebec_three.charlie_six and bravo_oscar.mike_india=quebec_three.mike_india and bravo_oscar.sierra_uniform=quebec_three.sierra_uniform
left join victor_four seven_quebec on seven_quebec.foxtrot_india=quebec_three.foxtrot_india and seven_quebec.mike_india=quebec_three.mike_india and seven_quebec.sierra_uniform=quebec_three.sierra_uniform
left join vm_vh_class vh on vh.vh_class=COALESCE(bravo_oscar.vh_class,seven_quebec.vh_class)
where quebec_three.mike_india='UP' and case when 0=0 then true else quebec_three.sierra_uniform=0 end and quebec_three.delta_xray between '2021-03-01 00:00:00.000000 +05:30' and ('2021-04-02 23:59:59.999000 +05:30'::date + interval '1 day' - interval '1 sec')
and quebec_three.oscar_quebec in (123)
)quebec_three
group by 1,2,whiskey,quebec_three.tango,quebec_three.juliet_charlie,victor_papa,yankee,india_papa,victor_charlie,november_hotel,hotel_november
)quebec_three
left join bravo_zulu three on three.mike_india=quebec_three.mike_india and three.sierra_uniform=quebec_three.sierra_uniform
group by 1,2,3 order by 3;
Adding orignal partial query/indexes/plan -
Partial query:
.....
vow4(# left join vha_status c on c.appl_no=a.appl_no and c.state_cd=a.state_cd and c.off_cd=a.off_cd
vow4(# and c.moved_on =(select max(moved_on) from vha_status c1 where c1.appl_no=c.appl_no
vow4(# and c1.state_cd=c.state_cd and c1.off_cd=c.off_cd and c1.pur_cd=c.pur_cd)
.....
vow4(# where a.state_cd='UP' and case when 0=0 then true else a.off_cd=0 end and a.appl_dt between '2021-03-01 00:00:00.000000 +05:30' and ('2021-04-02 23:59:59.999000 +05:30'::date + interval '1 day' - interval '1 sec')
vow4(# and a.pur_cd in (123)
Indexes:
"vha_status_pkey" PRIMARY KEY, btree (appl_no, pur_cd, file_movement_slno)
"idx_state_cd_vha_status" btree (state_cd)
"va_status_moved_on_indx" btree (moved_on)
"vha_status_appl_no_state_cd_off_cd_pur_cd_moved_on_idx" btree (appl_no, state_cd, off_cd, pur_cd, moved_on)
"vha_status_movedon_state_cd_off_cd_idx" btree (moved_on, state_cd, off_cd)
Partial Plan:
-> Index Scan using vha_status_appl_no_state_cd_off_cd_pur_cd_moved_on_idx on vha_status c (cost=0.70..11.08 rows=1 width=33) (actual time=0.179..0.180 rows=1 loops=252383)
Index Cond: (((appl_no)::text = (a_1.appl_no)::text) AND ((state_cd)::text = (a_1.state_cd)::text) AND ((state_cd)::text = 'UP'::text) AND (off_cd = a_1.off_cd))
Filter: (moved_on = (SubPlan 2))
Rows Removed by Filter: 6
SubPlan 2
-> Result (cost=5.55..5.58 rows=1 width=8) (actual time=0.013..0.013 rows=1 loops=1828065)
InitPlan 1 (returns $4)
-> Limit (cost=0.70..5.55 rows=1 width=8) (actual time=0.012..0.012 rows=1 loops=1828065)
-> Index Only Scan Backward using vha_status_appl_no_state_cd_off_cd_pur_cd_moved_on_idx on vha_status c1 (cost=0.70..5.55 rows=1 width=8) (actual time=0.012..0.012 rows=1 loops=1828065)
Index Cond: ((appl_no = (c.appl_no)::text) AND (state_cd = (c.state_cd)::text) AND (off_cd = c.off_cd) AND (pur_cd = c.pur_cd) AND (moved_on IS NOT NULL))
Heap Fetches: 18062
This looks like primarily that there is a nested loop on five_lima (900 million) rows are not the problem , no. of times your are querying is looks like.
Most probable solution for this would be either don't do that much query if possible, can discuss on exact solution , or else try to first limit the record by filtering and then query
In general querying that many times is not preferable.

postgresql optimize query analyze explain

Hi all postgresql experts. I am maintaining a web application I did neither conceive or develop, and there is a query that takes 5 minutes (yes minutes) to complete. As I am not familiar at all with PostgreSQL I have ran the "explain analyze" and here is the query and the output of the explain analyze. I would be very grateful if someone could explain ( :-) ) to me how to improve the query or just give me some pointers to understand the output :)
here is the query :
explain analyse SELECT a.salon as salonid, a.salon_ratachement,
(CASE WHEN a.salon IS NOT NULL THEN (select debut FROM salon WHERE id = a.salon) ELSE
a.hors_salon_date_debut END) as debut,
(CASE WHEN a.salon IS NOT NULL THEN (select fin FROM salon WHERE id = a.salon) ELSE a
.hors_salon_date_fin END) as fin,
(CASE WHEN a.salon IS NOT NULL THEN (select nom_fra FROM salon WHERE id = a.salon) WHEN a.salon_ratachement IS NOT NULL THEN (select nom_fra FROM salon WHERE id = a.salon_ratachement)||' (hors salon)' ELSE 'Hors salon' END ) as nom_salon,
a.id, a.reference, a.numero, ad.numero as numero_additif, a.jour_installation_souhaite,
ad.id as additifid, ad.statut_logistique, ad.etat, e.id as espaceid, e.code, e.nom,
c.id as clientid, c.nom as nom_client, c.lang
FROM client c, espace e, additif ad , affaire a, salon s
WHERE true and a.salon = '1237' and s.id = 1237
AND (ad.etat='NON CONFIRME' OR ad.etat='CONFIRME') and a.est_archive = 'false' AND (
ad.statut_logistique='a_preparer' OR ad.statut_logistique='en_preparation' OR ad.statut_logistique='a_installer' OR ad.statut_logistique='installe_partiellement' OR ad.statut_logistique='installe' OR ad.statut_logistique='desinstalle_partiellement' OR ad.statut_logistique='desinstalle') AND e.affaire=a.id AND a.client=c.id AND ad.affaire=a.id AND ad.espace=e.id
AND ((ad.id in (select lc.additif from ligne_commande lc, prestation p where lc.additif=ad .id and lc.prestation=p.id )) OR (ad.statut_logistique_force='t')) ORDER BY (case when a.salon is not null then a.salon::varchar||'b' when a.salon_ratachement is not null then a.salon_ratachement::varchar||'a' else '0' end) desc, a.id desc, ad.id;
and here is the explain output :
QUERY PLAN
Sort (cost=16051791.56..16051791.57 rows=1 width=143) (actual time=281189.406..281189.406 rows=5 loops=1)
Sort Key: (CASE WHEN (a.salon IS NOT NULL) THEN (((a.salon)::character varying)::text || 'b'::text) WHEN (a.salon_ratachement IS NOT NULL) THEN (((a.salon_ratachement)::character varying)::text || 'a'::text) ELSE '0'::text END), a.id, ad.id
Sort Method: quicksort Memory: 26kB
-> Nested Loop (cost=889.24..16051791.55 rows=1 width=143) (actual time=56077.822..281189.319 rows=5 loops=1)
Join Filter: (a.id = e.affaire)
-> Nested Loop (cost=889.24..16051757.76 rows=2 width=112) (actual time=56077.716..281188.945 rows=5 loops=1)
Join Filter: (a.id = ad.affaire)
-> Nested Loop (cost=0.00..961.46 rows=1 width=77) (actual time=0.096..8.279 rows=5 loops=1)
-> Nested Loop (cost=0.00..953.19 rows=1 width=77) (actual time=0.084..8.225 rows=5 loops=1)
-> Index Scan Backward using affaire_pkey on affaire a (cost=0.00..944.91 rows=1 width=56) (actual time=0.065..8.121 rows=5 loops=1)
Filter: ((NOT est_archive) AND (salon = 1237))
-> Index Scan using client_pkey on client c (cost=0.00..8.27 rows=1 width=25) (actual time=0.015..0.016 rows=1 loops=5)
Index Cond: (c.id = a.client)
-> Index Scan using salon_pkey on salon s (cost=0.00..8.27 rows=1 width=0) (actual time=0.007..0.009 rows=1 loops=5)
Index Cond: (s.id = 1237)
-> Bitmap Heap Scan on additif ad (cost=889.24..16050626.95 rows=13548 width=35) (actual time=4.506..56229.985 rows=17173 loops=5)
Recheck Cond: ((((ad.statut_logistique)::text = 'a_preparer'::text) OR ((ad.statut_logistique)::text = 'en_preparation'::text) OR ((ad.statut_logistique)::text = 'a_installer'::text) OR ((ad.statut_logistique)::text = 'installe_partiellement'::text) OR ((ad.statut_logistique)::text = 'installe'::text) OR ((ad.statut_logistique)::text = 'desinstalle_partiellement'::text) OR ((ad.statut_logistique)::text = 'desinstalle'::text)) AND (((ad.etat)::text = 'NON CONFIRME'::text) OR ((ad.etat)::text = 'CONFIRME'::text)))
Filter: ((SubPlan 5) OR ad.statut_logistique_force)
-> BitmapAnd (cost=889.24..889.24 rows=17129 width=0) (actual time=4.173..4.173 rows=0 loops=5)
-> BitmapOr (cost=443.27..443.27 rows=17304 width=0) (actual time=2.013..2.013 rows=0 loops=5)
-> Bitmap Index Scan on statut_logistique_key (cost=0.00..4.46 rows=27 width=0) (actual time=0.032..0.032 rows=51 loops=5)
Index Cond: ((ad.statut_logistique)::text = 'a_preparer'::text)
-> Bitmap Index Scan on statut_logistique_key (cost=0.00..4.46 rows=27 width=0) (actual time=0.006..0.006 rows=4 loops=5)
Index Cond: ((ad.statut_logistique)::text = 'en_preparation'::text)
-> Bitmap Index Scan on statut_logistique_key (cost=0.00..46.17 rows=1856 width=0) (actual time=0.245..0.245 rows=1783 loops=5)
Index Cond: ((ad.statut_logistique)::text = 'a_installer'::text)
-> Bitmap Index Scan on statut_logistique_key (cost=0.00..4.46 rows=27 width=0) (actual time=0.004..0.004 rows=1 loops=5)
Index Cond: ((ad.statut_logistique)::text = 'installe_partiellement'::text)
-> Bitmap Index Scan on statut_logistique_key (cost=0.00..23.84 rows=1012 width=0) (actual time=0.118..0.118 rows=985 loops=5)
Index Cond: ((ad.statut_logistique)::text = 'installe'::text)
-> Bitmap Index Scan on statut_logistique_key (cost=0.00..4.46 rows=27 width=0) (actual time=0.012..0.012 rows=49 loops=5)
Index Cond: ((ad.statut_logistique)::text = 'desinstalle_partiellement'::text)
-> Bitmap Index Scan on statut_logistique_key (cost=0.00..331.73 rows=14330 width=0) (actual time=1.594..1.594 rows=14329 loops=5)
Index Cond: ((ad.statut_logistique)::text = 'desinstalle'::text)
-> BitmapOr (cost=445.71..445.71 rows=18458 width=0) (actual time=2.116..2.116 rows=0 loops=5)
-> Bitmap Index Scan on etat_key (cost=0.00..4.27 rows=2 width=0) (actual time=0.011..0.011 rows=2 loops=5)
Index Cond: ((ad.etat)::text = 'NON CONFIRME'::text)
-> Bitmap Index Scan on etat_key (cost=0.00..434.67 rows=18456 width=0) (actual time=2.105..2.105 rows=18409 loops=5)
Index Cond: ((ad.etat)::text = 'CONFIRME'::text)
SubPlan 5
-> Nested Loop (cost=0.00..1873.85 rows=3 width=4) (actual time=3.271..3.271 rows=1 loops=85885)
-> Seq Scan on ligne_commande lc (cost=0.00..1849.01 rows=3 width=8) (actual time=3.264..3.264 rows=1 loops=85885)
Filter: (additif = $4)
-> Index Scan using prestation_pkey on prestation p (cost=0.00..8.27 rows=1 width=4) (actual time=0.005..0.005 rows=1 loops=85790)
Index Cond: (p.id = lc.prestation)
-> Index Scan using espace_pkey on espace e (cost=0.00..0.34 rows=1 width=43) (actual time=0.025..0.026 rows=1 loops=5)
Index Cond: (e.id = ad.espace)
SubPlan 1
-> Index Scan using salon_pkey on salon (cost=0.00..8.27 rows=1 width=4) (actual time=0.007..0.009 rows=1 loops=5)
Index Cond: (id = $0)
SubPlan 2
-> Index Scan using salon_pkey on salon (cost=0.00..8.27 rows=1 width=4) (actual time=0.002..0.003 rows=1 loops=5)
Index Cond: (id = $1)
SubPlan 3
-> Index Scan using salon_pkey on salon (cost=0.00..8.27 rows=1 width=14) (actual time=0.002..0.003 rows=1 loops=5)
Index Cond: (id = $2)
SubPlan 4
-> Index Scan using salon_pkey on salon (cost=0.00..8.27 rows=1 width=14) (never executed)
Index Cond: (id = $3)
Total runtime: 281189.828 ms
Thank you very much for your help !

query with IN expression running slower than query with OR

I have two queries one is using OR expression and is running very fast. The other query is similar but is using IN expression instead of OR and is running very slow. I would appreciate if you could let me know how to make the query using IN as fast as the one using OR. The table has 15 million records
SELECT e.id
FROM events e,
resources r
WHERE e.resource_id = r.id
AND resource_type_id IN (19872817,
282)
ORDER BY occurrence_date DESC LIMIT 100
Limit (cost=0.85..228363.80 rows=100 width=12) (actual time=238.668..57470.017 rows=19 loops=1)
-> Nested Loop (cost=0.85..26211499.28 rows=11478 width=12) (actual time=238.667..57470.010 rows=19 loops=1)
Join Filter: (e.resource_id = r.id)
Rows Removed by Join Filter: 507548495
-> Index Scan using eventoccurrencedateindex on events e (cost=0.43..603333.83 rows=15380258 width=16) (actual time=0.023..2798.538 rows=15380258 loops=1)
-> Materialize (cost=0.42..36.16 rows=111 width=4) (actual time=0.000..0.001 rows=33 loops=15380258)
-> Index Scan using resources_type_fk_index on resources r (cost=0.42..35.60 rows=111 width=4) (actual time=0.014..0.107 rows=33 loops=1)
Index Cond: (resource_type_id = ANY ('{19872817,282}'::integer[]))
Total runtime: 57470.057 ms
SELECT e.id
FROM events e,
resources r
WHERE e.resource_id = r.id
AND (resource_type_id = '19872817' OR resource_type_id = '282')
ORDER BY occurrence_date DESC LIMIT 100
Limit (cost=10.17..14.22 rows=100 width=12) (actual time=0.060..0.181 rows=100 loops=1)
-> Nested Loop (cost=10.17..34747856.23 rows=858030913 width=12) (actual time=0.059..0.167 rows=100 loops=1)
Join Filter: (((e.resource_id = r.id) AND (r.resource_type_id = 19872817)) OR (r.resource_type_id = 282))
-> Index Scan using eventoccurrencedateindex on events e (cost=0.43..603333.83 rows=15380258 width=16) (actual time=0.018..0.019 rows=4 loops=1)
-> Materialize (cost=9.74..349.92 rows=111 width=8) (actual time=0.009..0.023 rows=25
loops=4)
-> Bitmap Heap Scan on resources r (cost=9.74..349.36 rows=111 width=8) (actual time=0.034..0.081 rows=33 loops=1)
Recheck Cond: ((resource_type_id = 19872817) OR (resource_type_id = 282))
-> BitmapOr (cost=9.74..9.74 rows=111 width=0) (actual time=0.023..0.023 rows=0 loops=1)
-> Bitmap Index Scan on resources_type_fk_index (cost=0.00..4.84 rows=56 width=0) (actual time=0.009..0.009 rows=0 loops=1)
Index Cond: (resource_type_id = 19872817)
-> Bitmap Index Scan on resources_type_fk_index (cost=0.00..4.84 rows=56 width=0) (actual time=0.014..0.014 rows=33 loops=1)
Index Cond: (resource_type_id = 282)" "Total runtime: 0.242 ms
This is strange in the or version:
Join Filter: (
((e.resource_id = r.id) AND (r.resource_type_id = 19872817))
OR
(r.resource_type_id = 282)
)
It does e.resource_id = r.id AND r.resource_type_id = 19872817 first and then OR r.resource_type_id = 282 which is wrong. Are you sure you issued the correct condition in that query? Notice that there must be parenthesis wrapping the OR:
e.resource_id = r.id
AND
(r.resource_type_id = 19872817 OR r.resource_type_id = 282)