perf(properties): stop reading property_targets and funding_package

Both were per-property LATERAL lookups against tables with no index on the
join column (property_targets.property_id, funding_package.plan_id), so each
seq-scanned 150k-180k rows once per property — ~3.4s of the load for a
291-property portfolio came from property_targets alone.

Neither value is actually used by the portfolio table: the "Expected EPC" column
derives from currentSapPoints + recommendation SAP, and fundingScheme is fetched
separately by the proposals table. Drop both reads (kept as NULL to preserve the
PropertyWithRelations shape) and trim the plan LATERAL to the columns used.
291-property portfolio: ~3.5s -> ~85ms.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-23 08:21:16 +00:00
parent fec714a406
commit 04d9b45f0a

View file

@ -718,9 +718,14 @@ export async function getProperties(
p.creation_status AS "creationStatus",
p.current_epc_rating AS "currentEpcRating",
p.current_sap_points AS "currentSapPoints",
t.epc AS "targetEpc",
-- property_targets is no longer read (the "Expected EPC" column derives
-- from currentSapPoints + recommendation SAP). Kept as NULL to preserve
-- the PropertyWithRelations shape.
NULL AS "targetEpc",
pl.id AS "planId",
fp.scheme AS "fundingScheme",
-- funding_package is no longer read here (the proposals table fetches it
-- separately). Kept as NULL to preserve the PropertyWithRelations shape.
NULL AS "fundingScheme",
COALESCE(rec.sap_points, 0) AS "totalRecommendationSapPoints",
COALESCE(rec.cost, 0) AS "totalRecommendationCost",
p.landlord_property_id AS "landlordPropertyId",
@ -736,27 +741,16 @@ export async function getProperties(
${mainfuelSql(sql`epc`)} AS mainfuel,
p.lexiscore AS lexiscore
FROM property p
-- LATERAL one-row lookups keep this query at "12 properties × index probe"
-- LATERAL one-row lookups keep this query at "N properties × index probe"
-- instead of hash-joining the multi-million-row plan/recommendation tables.
LEFT JOIN LATERAL (
SELECT epc FROM property_targets
WHERE property_id = p.id
ORDER BY created_at DESC
LIMIT 1
) t ON true
LEFT JOIN LATERAL (
SELECT * FROM plan
SELECT id, post_sap_points FROM plan
WHERE property_id = p.id
AND portfolio_id = p.portfolio_id
AND is_default = true
ORDER BY created_at DESC
LIMIT 1
) pl ON true
LEFT JOIN LATERAL (
SELECT scheme FROM funding_package
WHERE plan_id = pl.id
LIMIT 1
) fp ON true
-- Active (default, not-yet-installed) recommendations for the property,
-- read denormalised. We aggregate by recommendation.property_id the only
-- indexed access path (there is NO index on recommendation.plan_id, and the