Conversation
| def up | ||
| execute <<~SQL | ||
| CREATE TABLE IF NOT EXISTS simple_reporting.reporting_patient_prescriptions ( | ||
| patient_id uuid, |
There was a problem hiding this comment.
we don’t currently have indexes on:(patient_id, month_date) or month_date.Since reporting will likely filter by month and/or patient, adding these indexes will help avoid full scans and keep queries fast as data grows.
There was a problem hiding this comment.
We don't really need a composite index on patient_id and month date.
Since the table is partitioned on month_date, postgres will prune partitions correctly when month_date is used in the filter.
But an index on patient_id is missing which I will add.
| class CreateReportingPatientPrescriptions < ActiveRecord::Migration[6.1] | ||
| def up | ||
| execute <<~SQL | ||
| CREATE TABLE IF NOT EXISTS simple_reporting.reporting_patient_prescriptions ( |
There was a problem hiding this comment.
We should add: PRIMARY KEY (patient_id, month_date) .This will prevent duplicate rows per patient per month and keep the data safe long-term.
There was a problem hiding this comment.
This table pulls up data from reporting_patient_states which has unique patient_id and month_date record, because of distinct clause in it's definition.
Chances of getting a duplicate row per patient per month is really slim.
| SQL | ||
|
|
||
| execute <<~SQL | ||
| CREATE INDEX index_reporting_patient_prescriptions_on_age ON ONLY simple_reporting.reporting_patient_prescriptions USING btree (age); |
There was a problem hiding this comment.
In the migration we are creating indexes like:CREATE INDEX ... ON ONLY simple_reporting.reporting_patient_prescriptions .Since this is a partitioned table, ONLY creates the index just on the parent table (which does not store any data). The actual data lives in the monthly partitions, and those partitions won’t have these indexes. That could lead to full table scans and slower reports as data grows.It would be better to remove ONLY so the indexes apply properly to the partitions as well.
There was a problem hiding this comment.
Postgres will apply the index in the partitions even if the index is created on main table and not individual partitions
explain select * from simple_reporting.reporting_patient_prescriptions where month_date = '2025-09-01' and age > 40;;
QUERY PLAN
Bitmap Heap Scan on reporting_patient_prescriptions_20250901 reporting_patient_prescriptions (cost=12.60..235.24 rows=3 width=1348)
Recheck Cond: (age > 40)
Filter: (month_date = '2025-09-01'::date)
-> Bitmap Index Scan on reporting_patient_prescriptions_20250901_age_idx (cost=0.00..12.60 rows=576 width=0)
Index Cond: (age > 40)
| LEFT JOIN medicine_purposes mp ON mp.name = pd.name | ||
| WHERE pd.patient_id = rps.patient_id | ||
| AND pd.deleted_at IS NULL | ||
| AND to_char(timezone(current_setting('TIMEZONE'), timezone('UTC', pd.device_created_at)),'YYYY-MM') <= to_char(rps.month_date, 'YYYY-MM') |
There was a problem hiding this comment.
Optional: we could replace to_char() with date_trunc() and compare dates directly for cleaner and potentially more efficient date comparison.
There was a problem hiding this comment.
I tried using date_trunc() method, but it doesn't behave efficiently over the timezone border conditions.
Although this comparison is not efficient, it is inline with the time comparison query for other reporting tables/mat views
Story card: sc-37
Because
Titration reports are running very slow on metabase. We need to add a table which would have monthly summary of patient's prescriptions precalculated, which can be used over metabase for faster processing.
This addresses
Adding a partitioned reporting_patient_prescriptions table which contains prescription summary for each patient every month.
Refreshing past 4 months partitions for this table, 1st of every month to account for older data syncing.
Test instructions
Connect to db console and run
CALL simple_reporting.add_shard_to_table('2026-02-01', 'reporting_patient_prescriptions');
This would populate the data for Feb-2026.
We can verify the aggregated data against prescription drugs table using sql select.