398 lines
15 KiB
MySQL
398 lines
15 KiB
MySQL
|
|
-- ------------------------------------------------------------------
|
||
|
|
-- Title: Hourly Sequential Organ Failure Assessment (SOFA)
|
||
|
|
--
|
||
|
|
-- Adapted from the MIMIC-IV upstream
|
||
|
|
-- concepts/score/sofa.sql
|
||
|
|
-- and ported to MIMIC-III v1.3 vanilla PostgreSQL.
|
||
|
|
--
|
||
|
|
-- Produces one row per (icustay_id, hr) for every hour of the ICU
|
||
|
|
-- stay, with both the per-component score AT that hour and the
|
||
|
|
-- 24-hour rolling MAX of each component (which is the value used by
|
||
|
|
-- Sepsis-3). Final column `sofa_24hours` is the sum of the six
|
||
|
|
-- 24-hour rolling maxes.
|
||
|
|
--
|
||
|
|
-- Differences vs. MIMIC-IV upstream (search this file for "PORT NOTE"):
|
||
|
|
-- 1. ID column is `icustay_id`, not `stay_id`.
|
||
|
|
-- 2. There is no `icustay_hourly` derived table in MIMIC-III; we
|
||
|
|
-- build the hourly grid inline with `generate_series`.
|
||
|
|
-- 3. There is no `mimic_derived.ventilation` with a fine-grained
|
||
|
|
-- `ventilation_status='InvasiveVent'` flag in MIMIC-III; we use
|
||
|
|
-- the lumped `ventilation_durations` table, so any active
|
||
|
|
-- ventilation row is treated as invasive ventilation for the
|
||
|
|
-- purpose of the PaO2:FiO2 vent/novent split.
|
||
|
|
-- 4. We replicate MIMIC-IV's `urine_output_rate` adjustment inline
|
||
|
|
-- in `sofa_uo`: we materialise both `uo_24hr` (sum) and
|
||
|
|
-- `uo_tm_24hr` (count of distinct hours that actually had a UO
|
||
|
|
-- observation in the past 24 h), and the renal CASE in (11)
|
||
|
|
-- uses
|
||
|
|
-- GREATEST(uo_24hr, 0) * 24.0 / uo_tm_24hr
|
||
|
|
-- only when uo_tm_24hr BETWEEN 22 AND 30, falling back to
|
||
|
|
-- creatinine alone otherwise. The GREATEST(_, 0) clip prevents
|
||
|
|
-- patients on continuous bladder irrigation (which the upstream
|
||
|
|
-- `urine_output.sql` subtracts as a negative volume) from being
|
||
|
|
-- mis-scored as oliguric.
|
||
|
|
-- 5. Vasopressor rates come from the upstream
|
||
|
|
-- durations/{epinephrine,norepinephrine,dopamine,dobutamine}_dose.sql
|
||
|
|
-- tables, which already merge CareVue + MetaVision and convert
|
||
|
|
-- to mcg/kg/min.
|
||
|
|
--
|
||
|
|
-- Implementation note: each measurement class is materialised into
|
||
|
|
-- its own narrow staging table. This avoids forcing the planner to
|
||
|
|
-- optimise a single ~10-way CTE join, lets each scan of the giant raw
|
||
|
|
-- tables (`chartevents`, `labevents`, `outputevents`,
|
||
|
|
-- `inputevents_*`) run independently, and lets you `EXPLAIN ANALYZE`
|
||
|
|
-- each step in isolation.
|
||
|
|
--
|
||
|
|
-- Dependencies:
|
||
|
|
-- postgres-functions.sql
|
||
|
|
-- durations/ventilation_durations.sql
|
||
|
|
-- durations/{dobutamine,dopamine,epinephrine,norepinephrine}_dose.sql
|
||
|
|
-- fluid_balance/urine_output.sql
|
||
|
|
-- sepsis/blood_gas_arterial.sql
|
||
|
|
-- sepsis/gcs_all.sql
|
||
|
|
-- ------------------------------------------------------------------
|
||
|
|
|
||
|
|
-- 1. Hourly grid: one row per (icustay_id, hr) for the entire stay.
|
||
|
|
DROP TABLE IF EXISTS sofa_grid;
|
||
|
|
CREATE TABLE sofa_grid AS
|
||
|
|
SELECT ie.subject_id, ie.hadm_id, ie.icustay_id
|
||
|
|
, gs.hr
|
||
|
|
, ie.intime + ((gs.hr - 1) * INTERVAL '1 hour') AS starttime
|
||
|
|
, ie.intime + (gs.hr * INTERVAL '1 hour') AS endtime
|
||
|
|
FROM icustays ie
|
||
|
|
, LATERAL generate_series(
|
||
|
|
1,
|
||
|
|
GREATEST(1,
|
||
|
|
CEIL(EXTRACT(EPOCH FROM (ie.outtime - ie.intime)) / 3600.0)::int
|
||
|
|
)
|
||
|
|
) AS gs(hr)
|
||
|
|
WHERE ie.outtime IS NOT NULL
|
||
|
|
AND ie.outtime > ie.intime;
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_grid_idx ON sofa_grid (icustay_id, hr);
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_grid_time_idx
|
||
|
|
ON sofa_grid (icustay_id, starttime, endtime);
|
||
|
|
ANALYZE sofa_grid;
|
||
|
|
|
||
|
|
-- 2. Mean arterial pressure: minimum within the hour.
|
||
|
|
DROP TABLE IF EXISTS sofa_vs;
|
||
|
|
CREATE TABLE sofa_vs AS
|
||
|
|
SELECT g.icustay_id, g.hr
|
||
|
|
, MIN(ce.valuenum) AS meanbp_min
|
||
|
|
FROM sofa_grid g
|
||
|
|
LEFT JOIN chartevents ce
|
||
|
|
ON ce.icustay_id = g.icustay_id
|
||
|
|
AND ce.charttime > g.starttime
|
||
|
|
AND ce.charttime <= g.endtime
|
||
|
|
AND ce.itemid IN (456, 52, 6702, 443, 220052, 220181, 225312)
|
||
|
|
AND ce.valuenum > 0 AND ce.valuenum < 300
|
||
|
|
AND COALESCE(ce.error, 0) = 0
|
||
|
|
GROUP BY g.icustay_id, g.hr;
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_vs_idx ON sofa_vs (icustay_id, hr);
|
||
|
|
ANALYZE sofa_vs;
|
||
|
|
|
||
|
|
-- 3. GCS: minimum within the hour (uses gcs_all carry-forward logic).
|
||
|
|
DROP TABLE IF EXISTS sofa_gcs;
|
||
|
|
CREATE TABLE sofa_gcs AS
|
||
|
|
SELECT g.icustay_id, g.hr
|
||
|
|
, MIN(gc.gcs) AS gcs_min
|
||
|
|
FROM sofa_grid g
|
||
|
|
LEFT JOIN gcs_all gc
|
||
|
|
ON gc.icustay_id = g.icustay_id
|
||
|
|
AND gc.charttime > g.starttime
|
||
|
|
AND gc.charttime <= g.endtime
|
||
|
|
GROUP BY g.icustay_id, g.hr;
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_gcs_idx ON sofa_gcs (icustay_id, hr);
|
||
|
|
ANALYZE sofa_gcs;
|
||
|
|
|
||
|
|
-- 4. Bilirubin: maximum within the hour.
|
||
|
|
DROP TABLE IF EXISTS sofa_bili;
|
||
|
|
CREATE TABLE sofa_bili AS
|
||
|
|
SELECT g.icustay_id, g.hr
|
||
|
|
, MAX(le.valuenum) AS bilirubin_max
|
||
|
|
FROM sofa_grid g
|
||
|
|
INNER JOIN icustays ie
|
||
|
|
ON ie.icustay_id = g.icustay_id
|
||
|
|
LEFT JOIN labevents le
|
||
|
|
ON le.subject_id = ie.subject_id
|
||
|
|
AND le.hadm_id = ie.hadm_id
|
||
|
|
AND le.charttime > g.starttime
|
||
|
|
AND le.charttime <= g.endtime
|
||
|
|
AND le.itemid = 50885
|
||
|
|
AND le.valuenum IS NOT NULL
|
||
|
|
AND le.valuenum > 0
|
||
|
|
GROUP BY g.icustay_id, g.hr;
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_bili_idx ON sofa_bili (icustay_id, hr);
|
||
|
|
ANALYZE sofa_bili;
|
||
|
|
|
||
|
|
-- 5. Creatinine: maximum within the hour.
|
||
|
|
DROP TABLE IF EXISTS sofa_cr;
|
||
|
|
CREATE TABLE sofa_cr AS
|
||
|
|
SELECT g.icustay_id, g.hr
|
||
|
|
, MAX(le.valuenum) AS creatinine_max
|
||
|
|
FROM sofa_grid g
|
||
|
|
INNER JOIN icustays ie
|
||
|
|
ON ie.icustay_id = g.icustay_id
|
||
|
|
LEFT JOIN labevents le
|
||
|
|
ON le.subject_id = ie.subject_id
|
||
|
|
AND le.hadm_id = ie.hadm_id
|
||
|
|
AND le.charttime > g.starttime
|
||
|
|
AND le.charttime <= g.endtime
|
||
|
|
AND le.itemid = 50912
|
||
|
|
AND le.valuenum IS NOT NULL
|
||
|
|
AND le.valuenum > 0
|
||
|
|
AND le.valuenum < 150 -- sanity (mg/dL)
|
||
|
|
GROUP BY g.icustay_id, g.hr;
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_cr_idx ON sofa_cr (icustay_id, hr);
|
||
|
|
ANALYZE sofa_cr;
|
||
|
|
|
||
|
|
-- 6. Platelets: minimum within the hour.
|
||
|
|
DROP TABLE IF EXISTS sofa_plt;
|
||
|
|
CREATE TABLE sofa_plt AS
|
||
|
|
SELECT g.icustay_id, g.hr
|
||
|
|
, MIN(le.valuenum) AS platelet_min
|
||
|
|
FROM sofa_grid g
|
||
|
|
INNER JOIN icustays ie
|
||
|
|
ON ie.icustay_id = g.icustay_id
|
||
|
|
LEFT JOIN labevents le
|
||
|
|
ON le.subject_id = ie.subject_id
|
||
|
|
AND le.hadm_id = ie.hadm_id
|
||
|
|
AND le.charttime > g.starttime
|
||
|
|
AND le.charttime <= g.endtime
|
||
|
|
AND le.itemid = 51265
|
||
|
|
AND le.valuenum IS NOT NULL
|
||
|
|
AND le.valuenum > 0
|
||
|
|
GROUP BY g.icustay_id, g.hr;
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_plt_idx ON sofa_plt (icustay_id, hr);
|
||
|
|
ANALYZE sofa_plt;
|
||
|
|
|
||
|
|
-- 7. PaO2/FiO2: split into vent / novent based on whether the patient
|
||
|
|
-- is on invasive ventilation at the time of the blood gas. We
|
||
|
|
-- take the worst (lowest) PaO2:FiO2 of each kind during the hour.
|
||
|
|
DROP TABLE IF EXISTS sofa_pf;
|
||
|
|
CREATE TABLE sofa_pf AS
|
||
|
|
WITH pafi AS (
|
||
|
|
SELECT bg.icustay_id, bg.charttime
|
||
|
|
, CASE WHEN vd.icustay_id IS NULL THEN bg.pao2fio2 END AS pao2fio2_novent
|
||
|
|
, CASE WHEN vd.icustay_id IS NOT NULL THEN bg.pao2fio2 END AS pao2fio2_vent
|
||
|
|
FROM blood_gas_arterial bg
|
||
|
|
LEFT JOIN ventilation_durations vd
|
||
|
|
ON bg.icustay_id = vd.icustay_id
|
||
|
|
AND bg.charttime >= vd.starttime
|
||
|
|
AND bg.charttime <= vd.endtime
|
||
|
|
WHERE bg.pao2fio2 IS NOT NULL
|
||
|
|
)
|
||
|
|
SELECT g.icustay_id, g.hr
|
||
|
|
, MIN(p.pao2fio2_novent) AS pao2fio2_novent
|
||
|
|
, MIN(p.pao2fio2_vent) AS pao2fio2_vent
|
||
|
|
FROM sofa_grid g
|
||
|
|
LEFT JOIN pafi p
|
||
|
|
ON p.icustay_id = g.icustay_id
|
||
|
|
AND p.charttime > g.starttime
|
||
|
|
AND p.charttime <= g.endtime
|
||
|
|
GROUP BY g.icustay_id, g.hr;
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_pf_idx ON sofa_pf (icustay_id, hr);
|
||
|
|
ANALYZE sofa_pf;
|
||
|
|
|
||
|
|
-- 8. Urine output: sum over the preceding 24 h plus a count of how many
|
||
|
|
-- distinct hours actually had a UO observation (`uo_tm_24hr`). This
|
||
|
|
-- matches the MIMIC-IV `urine_output_rate` table's two columns and
|
||
|
|
-- lets the renal CASE distinguish "no data" from "really oliguric".
|
||
|
|
-- The renal score in (11) requires uo_tm_24hr to be in a plausible
|
||
|
|
-- range before the UO branch fires; otherwise it falls back to
|
||
|
|
-- creatinine alone. See port note #3 in the file header.
|
||
|
|
DROP TABLE IF EXISTS sofa_uo;
|
||
|
|
CREATE TABLE sofa_uo AS
|
||
|
|
SELECT g.icustay_id, g.hr
|
||
|
|
, SUM(uo.value) AS uo_24hr
|
||
|
|
, COUNT(DISTINCT date_trunc('hour', uo.charttime)) AS uo_tm_24hr
|
||
|
|
FROM sofa_grid g
|
||
|
|
LEFT JOIN urine_output uo
|
||
|
|
ON uo.icustay_id = g.icustay_id
|
||
|
|
AND uo.charttime > DATETIME_SUB(g.endtime, INTERVAL '24' HOUR)
|
||
|
|
AND uo.charttime <= g.endtime
|
||
|
|
GROUP BY g.icustay_id, g.hr;
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_uo_idx ON sofa_uo (icustay_id, hr);
|
||
|
|
ANALYZE sofa_uo;
|
||
|
|
|
||
|
|
-- 9. Vasopressor rates: take the maximum rate of each pressor active at
|
||
|
|
-- `endtime`. Each upstream dose table is (icustay_id, starttime,
|
||
|
|
-- endtime, vaso_rate, vaso_amount).
|
||
|
|
DROP TABLE IF EXISTS sofa_vaso;
|
||
|
|
CREATE TABLE sofa_vaso AS
|
||
|
|
SELECT g.icustay_id, g.hr
|
||
|
|
, MAX(epi.vaso_rate) AS rate_epinephrine
|
||
|
|
, MAX(nor.vaso_rate) AS rate_norepinephrine
|
||
|
|
, MAX(dop.vaso_rate) AS rate_dopamine
|
||
|
|
, MAX(dob.vaso_rate) AS rate_dobutamine
|
||
|
|
FROM sofa_grid g
|
||
|
|
LEFT JOIN epinephrine_dose epi
|
||
|
|
ON epi.icustay_id = g.icustay_id
|
||
|
|
AND g.endtime > epi.starttime
|
||
|
|
AND g.endtime <= epi.endtime
|
||
|
|
LEFT JOIN norepinephrine_dose nor
|
||
|
|
ON nor.icustay_id = g.icustay_id
|
||
|
|
AND g.endtime > nor.starttime
|
||
|
|
AND g.endtime <= nor.endtime
|
||
|
|
LEFT JOIN dopamine_dose dop
|
||
|
|
ON dop.icustay_id = g.icustay_id
|
||
|
|
AND g.endtime > dop.starttime
|
||
|
|
AND g.endtime <= dop.endtime
|
||
|
|
LEFT JOIN dobutamine_dose dob
|
||
|
|
ON dob.icustay_id = g.icustay_id
|
||
|
|
AND g.endtime > dob.starttime
|
||
|
|
AND g.endtime <= dob.endtime
|
||
|
|
WHERE epi.icustay_id IS NOT NULL
|
||
|
|
OR nor.icustay_id IS NOT NULL
|
||
|
|
OR dop.icustay_id IS NOT NULL
|
||
|
|
OR dob.icustay_id IS NOT NULL
|
||
|
|
GROUP BY g.icustay_id, g.hr;
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_vaso_idx ON sofa_vaso (icustay_id, hr);
|
||
|
|
ANALYZE sofa_vaso;
|
||
|
|
|
||
|
|
-- 10. Wide assembly: left-join all of the above onto the grid.
|
||
|
|
DROP TABLE IF EXISTS sofa_wide;
|
||
|
|
CREATE TABLE sofa_wide AS
|
||
|
|
SELECT g.subject_id, g.hadm_id, g.icustay_id, g.hr
|
||
|
|
, g.starttime, g.endtime
|
||
|
|
, vs.meanbp_min
|
||
|
|
, gc.gcs_min
|
||
|
|
, bi.bilirubin_max
|
||
|
|
, cr.creatinine_max
|
||
|
|
, pl.platelet_min
|
||
|
|
, pf.pao2fio2_novent
|
||
|
|
, pf.pao2fio2_vent
|
||
|
|
, uo.uo_24hr
|
||
|
|
, uo.uo_tm_24hr
|
||
|
|
, va.rate_epinephrine
|
||
|
|
, va.rate_norepinephrine
|
||
|
|
, va.rate_dopamine
|
||
|
|
, va.rate_dobutamine
|
||
|
|
FROM sofa_grid g
|
||
|
|
LEFT JOIN sofa_vs vs ON vs.icustay_id = g.icustay_id AND vs.hr = g.hr
|
||
|
|
LEFT JOIN sofa_gcs gc ON gc.icustay_id = g.icustay_id AND gc.hr = g.hr
|
||
|
|
LEFT JOIN sofa_bili bi ON bi.icustay_id = g.icustay_id AND bi.hr = g.hr
|
||
|
|
LEFT JOIN sofa_cr cr ON cr.icustay_id = g.icustay_id AND cr.hr = g.hr
|
||
|
|
LEFT JOIN sofa_plt pl ON pl.icustay_id = g.icustay_id AND pl.hr = g.hr
|
||
|
|
LEFT JOIN sofa_pf pf ON pf.icustay_id = g.icustay_id AND pf.hr = g.hr
|
||
|
|
LEFT JOIN sofa_uo uo ON uo.icustay_id = g.icustay_id AND uo.hr = g.hr
|
||
|
|
LEFT JOIN sofa_vaso va ON va.icustay_id = g.icustay_id AND va.hr = g.hr;
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_wide_idx ON sofa_wide (icustay_id, hr);
|
||
|
|
ANALYZE sofa_wide;
|
||
|
|
|
||
|
|
-- 11. Per-hour component scores (no rolling window yet).
|
||
|
|
DROP TABLE IF EXISTS sofa_components;
|
||
|
|
CREATE TABLE sofa_components AS
|
||
|
|
SELECT w.*
|
||
|
|
-- Respiration
|
||
|
|
, CASE
|
||
|
|
WHEN pao2fio2_vent < 100 THEN 4
|
||
|
|
WHEN pao2fio2_vent < 200 THEN 3
|
||
|
|
WHEN pao2fio2_novent < 300 THEN 2
|
||
|
|
WHEN pao2fio2_vent < 300 THEN 2
|
||
|
|
WHEN pao2fio2_novent < 400 THEN 1
|
||
|
|
WHEN pao2fio2_vent < 400 THEN 1
|
||
|
|
WHEN COALESCE(pao2fio2_vent, pao2fio2_novent) IS NULL THEN NULL
|
||
|
|
ELSE 0
|
||
|
|
END AS respiration
|
||
|
|
|
||
|
|
-- Coagulation
|
||
|
|
, CASE
|
||
|
|
WHEN platelet_min < 20 THEN 4
|
||
|
|
WHEN platelet_min < 50 THEN 3
|
||
|
|
WHEN platelet_min < 100 THEN 2
|
||
|
|
WHEN platelet_min < 150 THEN 1
|
||
|
|
WHEN platelet_min IS NULL THEN NULL
|
||
|
|
ELSE 0
|
||
|
|
END AS coagulation
|
||
|
|
|
||
|
|
-- Liver (mg/dL)
|
||
|
|
, CASE
|
||
|
|
WHEN bilirubin_max >= 12.0 THEN 4
|
||
|
|
WHEN bilirubin_max >= 6.0 THEN 3
|
||
|
|
WHEN bilirubin_max >= 2.0 THEN 2
|
||
|
|
WHEN bilirubin_max >= 1.2 THEN 1
|
||
|
|
WHEN bilirubin_max IS NULL THEN NULL
|
||
|
|
ELSE 0
|
||
|
|
END AS liver
|
||
|
|
|
||
|
|
-- Cardiovascular
|
||
|
|
, CASE
|
||
|
|
WHEN rate_dopamine > 15 OR rate_epinephrine > 0.1 OR rate_norepinephrine > 0.1 THEN 4
|
||
|
|
WHEN rate_dopamine > 5 OR rate_epinephrine <= 0.1 OR rate_norepinephrine <= 0.1 THEN 3
|
||
|
|
WHEN rate_dopamine > 0 OR rate_dobutamine > 0 THEN 2
|
||
|
|
WHEN meanbp_min < 70 THEN 1
|
||
|
|
WHEN COALESCE(meanbp_min, rate_dopamine, rate_dobutamine,
|
||
|
|
rate_epinephrine, rate_norepinephrine) IS NULL THEN NULL
|
||
|
|
ELSE 0
|
||
|
|
END AS cardiovascular
|
||
|
|
|
||
|
|
-- CNS (GCS)
|
||
|
|
, CASE
|
||
|
|
WHEN gcs_min >= 13 AND gcs_min <= 14 THEN 1
|
||
|
|
WHEN gcs_min >= 10 AND gcs_min <= 12 THEN 2
|
||
|
|
WHEN gcs_min >= 6 AND gcs_min <= 9 THEN 3
|
||
|
|
WHEN gcs_min < 6 THEN 4
|
||
|
|
WHEN gcs_min IS NULL THEN NULL
|
||
|
|
ELSE 0
|
||
|
|
END AS cns
|
||
|
|
|
||
|
|
-- Renal
|
||
|
|
-- We scale uo_24hr to a 24h-equivalent only when the rolling
|
||
|
|
-- window has at least 22 distinct hours of observations
|
||
|
|
-- (matching MIMIC-IV's `urine_output_rate` upper-bound check).
|
||
|
|
-- If fewer than 22 valid hours exist, the UO branch is treated
|
||
|
|
-- as missing and the renal score falls back to creatinine alone.
|
||
|
|
-- We also clip GU-irrigant negative net values to zero so a
|
||
|
|
-- patient on continuous bladder irrigation isn't mis-scored as
|
||
|
|
-- oliguric.
|
||
|
|
, CASE
|
||
|
|
WHEN creatinine_max >= 5.0 THEN 4
|
||
|
|
WHEN uo_tm_24hr BETWEEN 22 AND 30
|
||
|
|
AND GREATEST(uo_24hr, 0) * 24.0 / uo_tm_24hr < 200 THEN 4
|
||
|
|
WHEN creatinine_max >= 3.5 AND creatinine_max < 5.0 THEN 3
|
||
|
|
WHEN uo_tm_24hr BETWEEN 22 AND 30
|
||
|
|
AND GREATEST(uo_24hr, 0) * 24.0 / uo_tm_24hr < 500 THEN 3
|
||
|
|
WHEN creatinine_max >= 2.0 AND creatinine_max < 3.5 THEN 2
|
||
|
|
WHEN creatinine_max >= 1.2 AND creatinine_max < 2.0 THEN 1
|
||
|
|
WHEN creatinine_max IS NULL
|
||
|
|
AND NOT (uo_tm_24hr BETWEEN 22 AND 30) THEN NULL
|
||
|
|
ELSE 0
|
||
|
|
END AS renal
|
||
|
|
FROM sofa_wide w;
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_components_idx
|
||
|
|
ON sofa_components (icustay_id, hr);
|
||
|
|
ANALYZE sofa_components;
|
||
|
|
|
||
|
|
-- 12. Final hourly SOFA: 24-hour rolling MAX per component, summed.
|
||
|
|
DROP TABLE IF EXISTS sofa_hourly;
|
||
|
|
CREATE TABLE sofa_hourly AS
|
||
|
|
SELECT s.subject_id, s.hadm_id, s.icustay_id, s.hr
|
||
|
|
, s.starttime, s.endtime
|
||
|
|
, s.respiration, s.coagulation, s.liver
|
||
|
|
, s.cardiovascular, s.cns, s.renal
|
||
|
|
, COALESCE(MAX(s.respiration) OVER w, 0) AS respiration_24hours
|
||
|
|
, COALESCE(MAX(s.coagulation) OVER w, 0) AS coagulation_24hours
|
||
|
|
, COALESCE(MAX(s.liver) OVER w, 0) AS liver_24hours
|
||
|
|
, COALESCE(MAX(s.cardiovascular) OVER w, 0) AS cardiovascular_24hours
|
||
|
|
, COALESCE(MAX(s.cns) OVER w, 0) AS cns_24hours
|
||
|
|
, COALESCE(MAX(s.renal) OVER w, 0) AS renal_24hours
|
||
|
|
, COALESCE(MAX(s.respiration) OVER w, 0)
|
||
|
|
+ COALESCE(MAX(s.coagulation) OVER w, 0)
|
||
|
|
+ COALESCE(MAX(s.liver) OVER w, 0)
|
||
|
|
+ COALESCE(MAX(s.cardiovascular) OVER w, 0)
|
||
|
|
+ COALESCE(MAX(s.cns) OVER w, 0)
|
||
|
|
+ COALESCE(MAX(s.renal) OVER w, 0)
|
||
|
|
AS sofa_24hours
|
||
|
|
FROM sofa_components s
|
||
|
|
WINDOW w AS (
|
||
|
|
PARTITION BY s.icustay_id
|
||
|
|
ORDER BY s.hr
|
||
|
|
ROWS BETWEEN 23 PRECEDING AND CURRENT ROW
|
||
|
|
);
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_hourly_idx
|
||
|
|
ON sofa_hourly (icustay_id, hr);
|
||
|
|
CREATE INDEX IF NOT EXISTS sofa_hourly_time_idx
|
||
|
|
ON sofa_hourly (icustay_id, endtime);
|
||
|
|
ANALYZE sofa_hourly;
|