mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-07-27 22:45:03 +00:00
Merge pull request #374 from Hestia-Homes/perf/measures-plan-default-index
perf(db): covering partial index for the reporting measures aggregate
This commit is contained in:
commit
153c64195b
4 changed files with 12202 additions and 0 deletions
21
src/app/db/migrations/0266_hesitant_bromley.sql
Normal file
21
src/app/db/migrations/0266_hesitant_bromley.sql
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
-- Covering partial index for the reporting "where the money goes" aggregate.
|
||||
-- The query joins the latest plan per property to its recommendations ON
|
||||
-- plan_id, filters to default = true AND already_installed = false, and sums
|
||||
-- estimated_cost / counts property_id per measure_type. The plain
|
||||
-- idx_recommendation_plan_id only locates the rows; the aggregate then
|
||||
-- heap-fetches every recommendation to apply the filter and read these
|
||||
-- columns, which times out on large portfolios (e.g. 434). Carrying
|
||||
-- measure_type, property_id and estimated_cost as key columns makes the whole
|
||||
-- aggregate index-only.
|
||||
--
|
||||
-- OPS: `recommendation` is large, so build this CONCURRENTLY out-of-band on
|
||||
-- prod BEFORE this migration runs, so the migration no-ops there (matches the
|
||||
-- 0258 idx_plan_recommendations_recommendation_id pattern). A plain
|
||||
-- CREATE INDEX here would lock writes on the table. Fresh environments build
|
||||
-- it inline via IF NOT EXISTS.
|
||||
--
|
||||
-- CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_recommendation_plan_default"
|
||||
-- ON "recommendation" USING btree ("plan_id","measure_type","property_id","estimated_cost")
|
||||
-- WHERE "default" = true AND "already_installed" = false;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "idx_recommendation_plan_default" ON "recommendation" USING btree ("plan_id","measure_type","property_id","estimated_cost") WHERE "recommendation"."default" = true AND "recommendation"."already_installed" = false;
|
||||
12154
src/app/db/migrations/meta/0266_snapshot.json
Normal file
12154
src/app/db/migrations/meta/0266_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1856,6 +1856,13 @@
|
|||
"when": 1783446767021,
|
||||
"tag": "0265_uprn_corrections",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 266,
|
||||
"version": "7",
|
||||
"when": 1783511384352,
|
||||
"tag": "0266_hesitant_bromley",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -118,6 +118,26 @@ export const recommendation = pgTable(
|
|||
sql`${table.default} = true AND ${table.alreadyInstalled} = false`,
|
||||
),
|
||||
|
||||
// Covering partial index for the reporting "where the money goes"
|
||||
// aggregate, which joins the latest plan per property to its
|
||||
// recommendations ON plan_id, filters to the default active set, and sums
|
||||
// estimated_cost / counts property_id per measure_type. The plain
|
||||
// idx_recommendation_plan_id only locates the rows — the aggregate then
|
||||
// heap-fetches every recommendation to apply the filter and read these
|
||||
// columns, which times out on large portfolios. Carrying measure_type,
|
||||
// property_id and estimated_cost as key columns (drizzle-orm has no
|
||||
// INCLUDE) makes the whole aggregate index-only.
|
||||
index("idx_recommendation_plan_default")
|
||||
.on(
|
||||
table.planId,
|
||||
table.measureType,
|
||||
table.propertyId,
|
||||
table.estimatedCost,
|
||||
)
|
||||
.where(
|
||||
sql`${table.default} = true AND ${table.alreadyInstalled} = false`,
|
||||
),
|
||||
|
||||
index("idx_recommendation_material_id").on(table.materialId),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue