\set ON_ERROR_STOP on \timing off -- Session-local alpha-sized fixture. No production row is inserted or changed. SET search_path = pg_temp, public; SET jit = off; SET work_mem = '16MB'; CREATE TEMP TABLE fish (LIKE public.fish INCLUDING ALL); CREATE TEMP TABLE waterbody (LIKE public.waterbody INCLUDING ALL); CREATE TEMP TABLE bait (LIKE public.bait INCLUDING ALL); CREATE TEMP TABLE spot (LIKE public.spot INCLUDING ALL); CREATE TEMP TABLE catch_report (LIKE public.catch_report INCLUDING ALL); INSERT INTO fish (id, slug, name_ru, trophy_weight_g) SELECT md5('fish-' || g)::uuid, 'fish-' || g, 'Рыба ' || g, 5000 + g * 20 FROM generate_series(1, 252) AS g; INSERT INTO waterbody (id, slug, name_ru, unlock_level) SELECT md5('water-' || g)::uuid, 'water-' || g, 'Водоём ' || g, g FROM generate_series(1, 19) AS g; INSERT INTO bait (id, name, normalized_name, kind) SELECT md5('bait-' || g)::uuid, 'Приманка ' || g, 'приманка-' || g, CASE WHEN g % 3 = 0 THEN 'bait'::baitkind ELSE 'lure'::baitkind END FROM generate_series(1, 500) AS g; INSERT INTO spot (id, waterbody_id, x, y, description) SELECT md5('spot-' || g)::uuid, md5('water-' || ((g - 1) % 19 + 1))::uuid, (g * 37) % 1000, (g * 61) % 1000, NULL FROM generate_series(1, 5000) AS g; INSERT INTO catch_report ( id, fish_id, spot_id, waterbody_id, bait_id, weight_g, fishing_method, caught_at, reported_at, player_name, source_type, source_url, source_external_id, source_confidence, moderation_status, deleted_at, raw_payload ) SELECT md5('report-' || g)::uuid, md5('fish-' || ((g - 1) % 252 + 1))::uuid, md5('spot-' || ((g - 1) % 5000 + 1))::uuid, md5('water-' || (((g - 1) % 5000) % 19 + 1))::uuid, md5('bait-' || ((g - 1) % 500 + 1))::uuid, 100 + (g * 97) % 50000, (ARRAY['float', 'bottom', 'spinning'])[(g - 1) % 3 + 1], clock_timestamp() - (((g * 97) % 7776000) * interval '1 second'), clock_timestamp() - (((g * 97) % 7776000) * interval '1 second'), 'player-' || (g % 2000), CASE WHEN g % 5 = 0 THEN 'official_record'::sourcetype ELSE 'user'::sourcetype END, 'https://example.invalid/catches/' || g, 'query-plan-' || g, 50 + g % 51, CASE WHEN g % 10 = 1 THEN 'pending'::moderationstatus ELSE 'approved'::moderationstatus END, CASE WHEN g % 50 = 0 THEN clock_timestamp() ELSE NULL END, json_build_object('category', CASE WHEN g % 2 = 0 THEN 'weekly' ELSE 'absolute' END, 'region', 'RU') FROM generate_series(1, 100000) AS g; ANALYZE fish; ANALYZE waterbody; ANALYZE bait; ANALYZE spot; ANALYZE catch_report; \echo 'DATASET' SELECT count(*) AS reports, count(*) FILTER (WHERE moderation_status = 'approved') AS approved, count(*) FILTER (WHERE source_type = 'official_record') AS official, count(DISTINCT spot_id) AS spots FROM catch_report; \echo 'PLAN activity_72h' EXPLAIN (ANALYZE, BUFFERS, SETTINGS) SELECT cr.* FROM catch_report AS cr LEFT JOIN fish AS f ON f.id = cr.fish_id LEFT JOIN waterbody AS w ON w.id = cr.waterbody_id LEFT JOIN spot AS s ON s.id = cr.spot_id LEFT JOIN bait AS b ON b.id = cr.bait_id WHERE cr.moderation_status = 'approved' AND cr.deleted_at IS NULL AND cr.spot_id IS NOT NULL AND cr.reported_at >= now() - interval '72 hours'; \echo 'PLAN records_count' EXPLAIN (ANALYZE, BUFFERS, SETTINGS) SELECT count(cr.id) FROM catch_report AS cr WHERE cr.source_type = 'official_record'; \echo 'PLAN records_page' EXPLAIN (ANALYZE, BUFFERS, SETTINGS) SELECT cr.id, cr.fish_id, cr.waterbody_id, cr.bait_id, cr.weight_g, cr.caught_at FROM catch_report AS cr WHERE cr.source_type = 'official_record' ORDER BY cr.caught_at DESC, cr.weight_g DESC, cr.id DESC LIMIT 50; \echo 'PLAN spot_detail' EXPLAIN (ANALYZE, BUFFERS, SETTINGS) SELECT cr.* FROM catch_report AS cr LEFT JOIN bait AS b ON b.id = cr.bait_id WHERE cr.spot_id = md5('spot-2499')::uuid AND cr.moderation_status = 'approved' AND cr.deleted_at IS NULL; \echo 'PLAN public_spot_pages' EXPLAIN (ANALYZE, BUFFERS, SETTINGS) SELECT DISTINCT w.slug, s.x, s.y, f.slug FROM catch_report AS cr JOIN spot AS s ON cr.spot_id = s.id JOIN waterbody AS w ON s.waterbody_id = w.id JOIN fish AS f ON cr.fish_id = f.id WHERE cr.moderation_status = 'approved' AND cr.deleted_at IS NULL ORDER BY w.slug, s.x, s.y, f.slug LIMIT 500;