22-- Reusable SQL functions for exclusion blocks shared across multiple measures.
33-- Eliminates ~100 lines of copy-paste per measure.
44--
5+ -- Each function accepts an optional p_subject parameter (DEFAULT NULL).
6+ -- p_subject IS NULL → population mode (scan whole cohort, used by population reports)
7+ -- p_subject = '<id>' → subject mode (filter early via ix_*_subject indexes; ~100× faster)
8+ --
9+ -- Existing 2-arg callers (shared_hospice(mp.mp_start, mp.mp_end)) continue to work — the
10+ -- subject parameter defaults to NULL and the filter condition becomes a no-op.
11+ --
512-- Usage in measure SQL (LATERAL pattern):
613-- hospice AS (SELECT h.* FROM mp, LATERAL shared_hospice(mp.mp_start, mp.mp_end) h),
714-- palliative AS (SELECT h.* FROM mp, LATERAL shared_palliative(mp.mp_start, mp.mp_end) h),
8- -- ...
15+ --
16+ -- With push-down for a specific patient:
17+ -- hospice AS (SELECT h.* FROM mp, LATERAL shared_hospice(mp.mp_start, mp.mp_end, $subject) h),
918--
1019-- Prerequisites: shared/sql/01-views.sql (flat views must exist)
1120
21+ -- ============================================================
22+ -- Drop existing functions in reverse-dependency order.
23+ -- shared_advanced_illness_frailty depends on the other three (frailty/illness/dementia),
24+ -- so it must be dropped first.
25+ -- ============================================================
26+ DROP FUNCTION IF EXISTS shared_advanced_illness_frailty(timestamptz , timestamptz );
27+ DROP FUNCTION IF EXISTS shared_hospice(timestamptz , timestamptz );
28+ DROP FUNCTION IF EXISTS shared_palliative(timestamptz , timestamptz );
29+ DROP FUNCTION IF EXISTS shared_has_frailty(timestamptz , timestamptz );
30+ DROP FUNCTION IF EXISTS shared_advanced_illness(timestamptz , timestamptz );
31+ DROP FUNCTION IF EXISTS shared_dementia_meds(timestamptz , timestamptz );
32+ DROP FUNCTION IF EXISTS shared_nursing_home(timestamptz , timestamptz );
33+
1234-- ============================================================
1335-- HOSPICE (6 sub-checks) — used by 9 measures
1436-- ============================================================
15- CREATE OR REPLACE FUNCTION shared_hospice (p_mp_start timestamptz , p_mp_end timestamptz )
37+ CREATE OR REPLACE FUNCTION shared_hospice (
38+ p_mp_start timestamptz ,
39+ p_mp_end timestamptz ,
40+ p_subject text DEFAULT NULL
41+ )
1642RETURNS TABLE(patient_id text ) AS $$
1743 SELECT DISTINCT patient_id FROM (
1844 -- Inpatient discharge to hospice
@@ -25,6 +51,7 @@ RETURNS TABLE(patient_id text) AS $$
2551 AND e .period_end <= p_mp_end AND e .period_end >= p_mp_start
2652 AND (r .resource - > ' hospitalization' - > ' dischargeDisposition' - > ' coding' - > 0 - >> ' code'
2753 IN (' 428361000124107' , ' 428371000124100' ))
54+ AND (p_subject IS NULL OR e .patient_id = p_subject)
2855 UNION ALL
2956 -- Hospice encounter
3057 SELECT e .patient_id
@@ -34,6 +61,7 @@ RETURNS TABLE(patient_id text) AS $$
3461 WHERE e .status = ' finished'
3562 AND e .period_start <= p_mp_end
3663 AND (e .period_end IS NULL OR e .period_end >= p_mp_start)
64+ AND (p_subject IS NULL OR e .patient_id = p_subject)
3765 UNION ALL
3866 -- Hospice observation (LOINC 45755-6 = yes)
3967 SELECT o .patient_id
@@ -43,6 +71,7 @@ RETURNS TABLE(patient_id text) AS $$
4371 AND o .status IN (' final' , ' amended' , ' corrected' )
4472 AND o .effective_start <= p_mp_end
4573 AND (o .effective_end IS NULL OR o .effective_end >= p_mp_start)
74+ AND (p_subject IS NULL OR o .patient_id = p_subject)
4675 UNION ALL
4776 -- Hospice order (ServiceRequest)
4877 SELECT sr .patient_id
@@ -52,6 +81,7 @@ RETURNS TABLE(patient_id text) AS $$
5281 WHERE sr .status IN (' active' , ' completed' )
5382 AND sr .intent IN (' order' , ' original-order' , ' reflex-order' , ' filler-order' , ' instance-order' )
5483 AND sr .authored_on >= p_mp_start AND sr .authored_on <= p_mp_end
84+ AND (p_subject IS NULL OR sr .patient_id = p_subject)
5585 UNION ALL
5686 -- Hospice procedure
5787 SELECT pr .patient_id
@@ -61,6 +91,7 @@ RETURNS TABLE(patient_id text) AS $$
6191 WHERE pr .status = ' completed'
6292 AND pr .performed_start <= p_mp_end
6393 AND (pr .performed_end IS NULL OR pr .performed_end >= p_mp_start)
94+ AND (p_subject IS NULL OR pr .patient_id = p_subject)
6495 UNION ALL
6596 -- Hospice diagnosis
6697 SELECT c .patient_id
@@ -71,13 +102,18 @@ RETURNS TABLE(patient_id text) AS $$
71102 OR c .verification_status IN (' confirmed' , ' unconfirmed' , ' provisional' , ' differential' ))
72103 AND c .onset_date <= p_mp_end
73104 AND (c .abatement_date IS NULL OR c .abatement_date >= p_mp_start)
105+ AND (p_subject IS NULL OR c .patient_id = p_subject)
74106 ) sub
75107$$ LANGUAGE sql STABLE;
76108
77109-- ============================================================
78110-- PALLIATIVE CARE (4 sub-checks) — used by 5 measures
79111-- ============================================================
80- CREATE OR REPLACE FUNCTION shared_palliative (p_mp_start timestamptz , p_mp_end timestamptz )
112+ CREATE OR REPLACE FUNCTION shared_palliative (
113+ p_mp_start timestamptz ,
114+ p_mp_end timestamptz ,
115+ p_subject text DEFAULT NULL
116+ )
81117RETURNS TABLE(patient_id text ) AS $$
82118 SELECT DISTINCT patient_id FROM (
83119 -- Palliative observation (LOINC 71007-9)
@@ -87,6 +123,7 @@ RETURNS TABLE(patient_id text) AS $$
87123 AND o .status IN (' final' , ' amended' , ' corrected' )
88124 AND o .effective_start <= p_mp_end
89125 AND (o .effective_end IS NULL OR o .effective_end >= p_mp_start)
126+ AND (p_subject IS NULL OR o .patient_id = p_subject)
90127 UNION ALL
91128 -- Palliative diagnosis
92129 SELECT c .patient_id
@@ -97,6 +134,7 @@ RETURNS TABLE(patient_id text) AS $$
97134 OR c .verification_status IN (' confirmed' , ' unconfirmed' , ' provisional' , ' differential' ))
98135 AND c .onset_date <= p_mp_end
99136 AND (c .abatement_date IS NULL OR c .abatement_date >= p_mp_start)
137+ AND (p_subject IS NULL OR c .patient_id = p_subject)
100138 UNION ALL
101139 -- Palliative encounter
102140 SELECT e .patient_id
@@ -106,6 +144,7 @@ RETURNS TABLE(patient_id text) AS $$
106144 WHERE e .status = ' finished'
107145 AND e .period_start <= p_mp_end
108146 AND (e .period_end IS NULL OR e .period_end >= p_mp_start)
147+ AND (p_subject IS NULL OR e .patient_id = p_subject)
109148 UNION ALL
110149 -- Palliative procedure
111150 SELECT pr .patient_id
@@ -115,13 +154,18 @@ RETURNS TABLE(patient_id text) AS $$
115154 WHERE pr .status = ' completed'
116155 AND pr .performed_start <= p_mp_end
117156 AND (pr .performed_end IS NULL OR pr .performed_end >= p_mp_start)
157+ AND (p_subject IS NULL OR pr .patient_id = p_subject)
118158 ) sub
119159$$ LANGUAGE sql STABLE;
120160
121161-- ============================================================
122162-- FRAILTY INDICATORS (5 sub-checks) — used by 4 measures
123163-- ============================================================
124- CREATE OR REPLACE FUNCTION shared_has_frailty (p_mp_start timestamptz , p_mp_end timestamptz )
164+ CREATE OR REPLACE FUNCTION shared_has_frailty (
165+ p_mp_start timestamptz ,
166+ p_mp_end timestamptz ,
167+ p_subject text DEFAULT NULL
168+ )
125169RETURNS TABLE(patient_id text ) AS $$
126170 SELECT DISTINCT patient_id FROM (
127171 -- Frailty Device (DeviceRequest)
@@ -131,6 +175,7 @@ RETURNS TABLE(patient_id text) AS $$
131175 AND vs .valueset_url = ' http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.118.12.1300' -- FrailtyDevice
132176 WHERE dr .status IN (' active' , ' completed' )
133177 AND dr .authored_on >= p_mp_start AND dr .authored_on <= p_mp_end
178+ AND (p_subject IS NULL OR dr .patient_id = p_subject)
134179 UNION ALL
135180 -- Frailty Diagnosis
136181 SELECT c .patient_id
@@ -141,6 +186,7 @@ RETURNS TABLE(patient_id text) AS $$
141186 OR c .verification_status IN (' confirmed' , ' unconfirmed' , ' provisional' , ' differential' ))
142187 AND c .onset_date <= p_mp_end
143188 AND (c .abatement_date IS NULL OR c .abatement_date >= p_mp_start)
189+ AND (p_subject IS NULL OR c .patient_id = p_subject)
144190 UNION ALL
145191 -- Frailty Encounter
146192 SELECT e .patient_id
@@ -150,6 +196,7 @@ RETURNS TABLE(patient_id text) AS $$
150196 WHERE e .status = ' finished'
151197 AND e .period_start <= p_mp_end
152198 AND (e .period_end IS NULL OR e .period_end >= p_mp_start)
199+ AND (p_subject IS NULL OR e .patient_id = p_subject)
153200 UNION ALL
154201 -- Frailty Symptom
155202 SELECT o .patient_id
@@ -159,6 +206,7 @@ RETURNS TABLE(patient_id text) AS $$
159206 WHERE o .status IN (' preliminary' , ' final' , ' amended' , ' corrected' )
160207 AND o .effective_start <= p_mp_end
161208 AND (o .effective_end IS NULL OR o .effective_end >= p_mp_start)
209+ AND (p_subject IS NULL OR o .patient_id = p_subject)
162210 UNION ALL
163211 -- Frailty via Medical Equipment (ObservationScreeningAssessment)
164212 SELECT o .patient_id
@@ -168,13 +216,18 @@ RETURNS TABLE(patient_id text) AS $$
168216 WHERE o .code = ' 98181-1' AND o .code_system = ' http://loinc.org'
169217 AND o .status IN (' final' , ' amended' , ' corrected' )
170218 AND o .effective_end >= p_mp_start AND o .effective_end <= p_mp_end
219+ AND (p_subject IS NULL OR o .patient_id = p_subject)
171220 ) sub
172221$$ LANGUAGE sql STABLE;
173222
174223-- ============================================================
175224-- ADVANCED ILLNESS (condition within MP-1yr to MP end) — used by 4 measures
176225-- ============================================================
177- CREATE OR REPLACE FUNCTION shared_advanced_illness (p_mp_start timestamptz , p_mp_end timestamptz )
226+ CREATE OR REPLACE FUNCTION shared_advanced_illness (
227+ p_mp_start timestamptz ,
228+ p_mp_end timestamptz ,
229+ p_subject text DEFAULT NULL
230+ )
178231RETURNS TABLE(patient_id text ) AS $$
179232 SELECT c .patient_id
180233 FROM condition_flat c
@@ -184,12 +237,17 @@ RETURNS TABLE(patient_id text) AS $$
184237 OR c .verification_status IN (' confirmed' , ' unconfirmed' , ' provisional' , ' differential' ))
185238 AND c .onset_date >= (p_mp_start - INTERVAL ' 1 year' )
186239 AND c .onset_date <= p_mp_end
240+ AND (p_subject IS NULL OR c .patient_id = p_subject)
187241$$ LANGUAGE sql STABLE;
188242
189243-- ============================================================
190244-- DEMENTIA MEDICATIONS (active, overlapping MP-1yr to MP end) — used by 4 measures
191245-- ============================================================
192- CREATE OR REPLACE FUNCTION shared_dementia_meds (p_mp_start timestamptz , p_mp_end timestamptz )
246+ CREATE OR REPLACE FUNCTION shared_dementia_meds (
247+ p_mp_start timestamptz ,
248+ p_mp_end timestamptz ,
249+ p_subject text DEFAULT NULL
250+ )
193251RETURNS TABLE(patient_id text ) AS $$
194252 SELECT mr .patient_id
195253 FROM medicationrequest_flat mr
@@ -199,27 +257,37 @@ RETURNS TABLE(patient_id text) AS $$
199257 AND mr .intent IN (' order' , ' original-order' , ' reflex-order' , ' filler-order' , ' instance-order' )
200258 AND COALESCE(mr .validity_start , mr .authored_on ) <= p_mp_end
201259 AND COALESCE(mr .validity_end , mr .authored_on ) >= (p_mp_start - INTERVAL ' 1 year' )
260+ AND (p_subject IS NULL OR mr .patient_id = p_subject)
202261$$ LANGUAGE sql STABLE;
203262
204263-- ============================================================
205264-- ADVANCED ILLNESS + FRAILTY combined (standard: age >= 66) — used by CMS125, 130, 131
206265-- CMS165 uses its own variant with age 66-80 / >=81 split
207266-- ============================================================
208- CREATE OR REPLACE FUNCTION shared_advanced_illness_frailty (p_mp_start timestamptz , p_mp_end timestamptz )
267+ CREATE OR REPLACE FUNCTION shared_advanced_illness_frailty (
268+ p_mp_start timestamptz ,
269+ p_mp_end timestamptz ,
270+ p_subject text DEFAULT NULL
271+ )
209272RETURNS TABLE(patient_id text ) AS $$
210273 SELECT p .id AS patient_id
211274 FROM patient_flat p
212- JOIN shared_has_frailty(p_mp_start, p_mp_end) f ON f .patient_id = p .id
213- LEFT JOIN shared_advanced_illness(p_mp_start, p_mp_end) ai ON ai .patient_id = p .id
214- LEFT JOIN shared_dementia_meds(p_mp_start, p_mp_end) dm ON dm .patient_id = p .id
275+ JOIN shared_has_frailty(p_mp_start, p_mp_end, p_subject ) f ON f .patient_id = p .id
276+ LEFT JOIN shared_advanced_illness(p_mp_start, p_mp_end, p_subject ) ai ON ai .patient_id = p .id
277+ LEFT JOIN shared_dementia_meds(p_mp_start, p_mp_end, p_subject ) dm ON dm .patient_id = p .id
215278 WHERE EXTRACT(YEAR FROM AGE(p_mp_end, p .birth_date ::date )) >= 66
216279 AND (ai .patient_id IS NOT NULL OR dm .patient_id IS NOT NULL )
280+ AND (p_subject IS NULL OR p .id = p_subject)
217281$$ LANGUAGE sql STABLE;
218282
219283-- ============================================================
220284-- NURSING HOME (age >= 66, housing status = lives in nursing home) — used by 4 measures
221285-- ============================================================
222- CREATE OR REPLACE FUNCTION shared_nursing_home (p_mp_start timestamptz , p_mp_end timestamptz )
286+ CREATE OR REPLACE FUNCTION shared_nursing_home (
287+ p_mp_start timestamptz ,
288+ p_mp_end timestamptz ,
289+ p_subject text DEFAULT NULL
290+ )
223291RETURNS TABLE(patient_id text ) AS $$
224292 SELECT o .patient_id
225293 FROM observation_flat o
@@ -229,4 +297,5 @@ RETURNS TABLE(patient_id text) AS $$
229297 AND o .value_code = ' 160734000'
230298 AND o .status IN (' final' , ' amended' , ' corrected' )
231299 AND o .effective_end <= p_mp_end
300+ AND (p_subject IS NULL OR o .patient_id = p_subject)
232301$$ LANGUAGE sql STABLE;
0 commit comments