mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
114 lines
4.1 KiB
SQL
114 lines
4.1 KiB
SQL
-- Pulse: a day is the mean of its hours' means, a coarser bucket the mean of
|
|
-- its days.
|
|
--
|
|
-- A watch samples pulse about every 5 to 10 minutes at rest and about every
|
|
-- 5 seconds in a workout, so the plain mean over a day's readings let one
|
|
-- workout hour outweigh the other twenty-three. The rollup writer now stores a
|
|
-- PULSE DAY bucket's `mean` as the mean of its per-hour means, and a WEEK /
|
|
-- MONTH / YEAR bucket's as the mean of its days' values (see
|
|
-- `src/lib/measurements/day-statistic.ts`). Nothing rewrites a stored bucket
|
|
-- on its own: the boot backfill and the freshness check fill only missing
|
|
-- days, so rows written before this release would keep the reading mean
|
|
-- until a write happened to touch their day. This migration recomputes them.
|
|
--
|
|
-- Scope, exactly:
|
|
-- - only rows of type PULSE, in every granularity;
|
|
-- - only the `mean` column. `count`, `min_value`, `max_value`, `sum_value`,
|
|
-- `sd`, `slope`, `r2` and the regression sums stay over every reading, as
|
|
-- the writer keeps them;
|
|
-- - hours, days, weeks, months and years are `date_trunc` on `measured_at`
|
|
-- in the session zone, exactly as the writer groups them, per user / source
|
|
-- over non-deleted readings.
|
|
--
|
|
-- Recomputing from `measurements` makes the migration idempotent: a rerun
|
|
-- derives the same means whatever the column held before. A stored row whose
|
|
-- readings are all gone is left as it is; the writer prunes such rows on the
|
|
-- next fold of their range.
|
|
--
|
|
-- The hour means are built once into a temporary table, so the readings are
|
|
-- scanned once for all four granularities.
|
|
--
|
|
-- Reversibility (down): recompute `mean` as `AVG(value)` over the same groups,
|
|
-- or refold the PULSE rollups on a release before this one. No column changes
|
|
-- shape.
|
|
|
|
CREATE TEMPORARY TABLE pulse_hour_means AS
|
|
SELECT
|
|
m."user_id" AS user_id,
|
|
m."source" AS source,
|
|
date_trunc('hour', m."measured_at") AS hr,
|
|
AVG(m."value") AS hour_mean
|
|
FROM measurements m
|
|
WHERE m."type" = 'PULSE'
|
|
AND m."deleted_at" IS NULL
|
|
GROUP BY m."user_id", m."source", date_trunc('hour', m."measured_at");
|
|
|
|
CREATE TEMPORARY TABLE pulse_day_means AS
|
|
SELECT
|
|
h.user_id,
|
|
h.source,
|
|
date_trunc('day', h.hr) AS day,
|
|
AVG(h.hour_mean) AS day_mean
|
|
FROM pulse_hour_means h
|
|
GROUP BY h.user_id, h.source, date_trunc('day', h.hr);
|
|
|
|
CREATE INDEX pulse_day_means_key ON pulse_day_means (user_id, source, day);
|
|
|
|
-- DAY buckets: the mean of the day's hour means.
|
|
UPDATE "measurement_rollups" r
|
|
SET "mean" = d.day_mean
|
|
FROM pulse_day_means d
|
|
WHERE r."granularity" = 'DAY'
|
|
AND r."type" = 'PULSE'
|
|
AND r."user_id" = d.user_id
|
|
AND r."source" = d.source
|
|
AND r."bucket_start" = d.day;
|
|
|
|
-- WEEK buckets: the mean of the week's day values.
|
|
UPDATE "measurement_rollups" r
|
|
SET "mean" = s.mean
|
|
FROM (
|
|
SELECT d.user_id, d.source, date_trunc('week', d.day) AS bucket_start,
|
|
AVG(d.day_mean) AS mean
|
|
FROM pulse_day_means d
|
|
GROUP BY d.user_id, d.source, date_trunc('week', d.day)
|
|
) s
|
|
WHERE r."granularity" = 'WEEK'
|
|
AND r."type" = 'PULSE'
|
|
AND r."user_id" = s.user_id
|
|
AND r."source" = s.source
|
|
AND r."bucket_start" = s.bucket_start;
|
|
|
|
-- MONTH buckets.
|
|
UPDATE "measurement_rollups" r
|
|
SET "mean" = s.mean
|
|
FROM (
|
|
SELECT d.user_id, d.source, date_trunc('month', d.day) AS bucket_start,
|
|
AVG(d.day_mean) AS mean
|
|
FROM pulse_day_means d
|
|
GROUP BY d.user_id, d.source, date_trunc('month', d.day)
|
|
) s
|
|
WHERE r."granularity" = 'MONTH'
|
|
AND r."type" = 'PULSE'
|
|
AND r."user_id" = s.user_id
|
|
AND r."source" = s.source
|
|
AND r."bucket_start" = s.bucket_start;
|
|
|
|
-- YEAR buckets.
|
|
UPDATE "measurement_rollups" r
|
|
SET "mean" = s.mean
|
|
FROM (
|
|
SELECT d.user_id, d.source, date_trunc('year', d.day) AS bucket_start,
|
|
AVG(d.day_mean) AS mean
|
|
FROM pulse_day_means d
|
|
GROUP BY d.user_id, d.source, date_trunc('year', d.day)
|
|
) s
|
|
WHERE r."granularity" = 'YEAR'
|
|
AND r."type" = 'PULSE'
|
|
AND r."user_id" = s.user_id
|
|
AND r."source" = s.source
|
|
AND r."bucket_start" = s.bucket_start;
|
|
|
|
DROP TABLE pulse_day_means;
|
|
DROP TABLE pulse_hour_means;
|