Skip to content

Commit 7744bbb

Browse files
chase-jonesMike Krahulec
authored andcommitted
932 if encounters between claims and clinical datasets overlap ie adt both encounter get thrown out in readmissions (#933)
* Make updates to readmissions log to pick one out of the bunch of overlapping instances. Choose the medical claim over the encounter. Prefer longer duration * Rename columns to match * Pulling through encounter source type * adding column name * Encounter type is not needed * Adding distinct to fix logic on mapping * database compatibility
1 parent 6e25122 commit 7744bbb

6 files changed

Lines changed: 111 additions & 58 deletions

models/readmissions/intermediate/readmissions__encounter.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ select
1818
, cast(drg_code as {{ dbt.type_string() }}) as drg_code
1919
, cast(paid_amount as {{ dbt.type_numeric() }}) as paid_amount
2020
, cast(primary_diagnosis_code as {{ dbt.type_string() }}) as primary_diagnosis_code
21+
, cast(encounter_source_type as {{ dbt.type_string() }}) as encounter_source_type
2122
, '{{ var('tuva_last_run') }}' as tuva_last_run
2223
from {{ ref('readmissions__stg_core__encounter') }}

models/readmissions/intermediate/readmissions__encounter_data_quality.sql

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,9 @@ select
5454
else 0
5555
end as no_diagnosis_ccs_flag
5656
, aa.ccs_diagnosis_category as diagnosis_ccs
57-
, case
58-
when aa.encounter_id in (select distinct encounter_id_a
59-
from {{ ref('readmissions__encounter_overlap') }})
60-
or
61-
aa.encounter_id in (select distinct encounter_id_b
62-
from {{ ref('readmissions__encounter_overlap') }})
57+
, case /* WHEN ENCOUNTER IS NOT THE BEST, THEN FLAG THIS ERROR. */
58+
when exists (select 1 from {{ ref('readmissions__encounter_overlap') }} as overlap
59+
where aa.encounter_id = overlap.encounter_id and overlap.is_best_encounter = 0)
6360
then 1
6461
else 0
6562
end as overlaps_with_another_encounter_flag

models/readmissions/intermediate/readmissions__encounter_overlap.sql

Lines changed: 99 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,113 @@
33
)
44
}}
55

6-
-- Here we give a list of all pairs of encounters
7-
-- that have some date overlap.
86

7+
with encounter_enhanced as (
8+
select
9+
*
10+
, COALESCE(
11+
case
12+
when {{ dbt.datediff("discharge_date", "admit_date","day") }} >= 1
13+
then {{ dbt.datediff("discharge_date", "admit_date","day") }}
14+
end
15+
, 1
16+
) as actual_length_of_stay
917

10-
with encounters_with_row_num as (
11-
select
12-
encounter_id
13-
, person_id
14-
, admit_date
15-
, discharge_date
16-
, row_number() over (
17-
partition by person_id
18-
order by encounter_id
19-
) as row_num
20-
from {{ ref('readmissions__encounter') }}
21-
)
18+
-- Source type priority (lower number = higher priority)
19+
, case
20+
when UPPER(TRIM(COALESCE(encounter_source_type, ''))) = 'CLAIM' then 1
21+
else 2
22+
end as source_type_priority
2223

24+
-- Data completeness score (count of complete fields, higher = better)
25+
, (case when discharge_disposition_code is not null
26+
and TRIM(discharge_disposition_code) != ''
27+
and TRIM(discharge_disposition_code) != '00' then 1 else 0 end) +
28+
(case when drg_code_type is not null
29+
and TRIM(drg_code_type) != '' then 1 else 0 end) +
30+
(case when drg_code is not null
31+
and TRIM(drg_code) != ''
32+
and TRIM(drg_code) not in ('998', '999') then 1 else 0 end) +
33+
(case when paid_amount is not null then 1 else 0 end) +
34+
(case when primary_diagnosis_code is not null
35+
and TRIM(primary_diagnosis_code) != '' then 1 else 0 end) as completeness_score
36+
from {{ ref('readmissions__encounter') }}
37+
)
2338

24-
, cartesian as (
25-
select
26-
aa.encounter_id as encounter_id_a
27-
, bb.encounter_id as encounter_id_b
28-
, aa.person_id
29-
, aa.admit_date as ai
30-
, aa.discharge_date as af
31-
, bb.admit_date as bi
32-
, bb.discharge_date as bf
33-
, case
34-
when (aa.admit_date between bb.admit_date and bb.discharge_date) or (aa.discharge_date between bb.admit_date and bb.discharge_date) or
35-
(bb.admit_date between aa.admit_date and aa.discharge_date) or (bb.discharge_date between aa.admit_date and aa.discharge_date)
36-
then 1
37-
else 0
38-
end as overlap
39-
from encounters_with_row_num as aa
40-
left outer join encounters_with_row_num as bb
41-
on aa.person_id = bb.person_id and aa.row_num < bb.row_num
39+
-- Identify all encounters that have overlapping dates with other encounters for the same person
40+
, overlapping_encounters as (
41+
select distinct
42+
e1.encounter_id
43+
, e1.person_id
44+
, e1.admit_date
45+
, e1.discharge_date
46+
from encounter_enhanced as e1
47+
where exists (
48+
select 1
49+
from encounter_enhanced as e2
50+
where e1.person_id = e2.person_id
51+
and e1.encounter_id != e2.encounter_id
52+
and e1.admit_date <= e2.discharge_date
53+
and e1.discharge_date >= e2.admit_date
54+
)
4255
)
4356

57+
-- Create overlap groups by assigning a group identifier
58+
, overlap_groups as (
59+
select distinct /* distinct here to prevent fan out for >2 overlapping encounters. */
60+
e1.encounter_id
61+
, e1.person_id
62+
, MIN(e2.encounter_id) over (
63+
partition by e1.person_id, e1.encounter_id
64+
) as overlap_group_id
65+
from overlapping_encounters as e1
66+
inner join overlapping_encounters as e2
67+
on e1.person_id = e2.person_id
68+
and e1.admit_date <= e2.discharge_date
69+
and e1.discharge_date >= e2.admit_date
70+
)
4471

45-
, overlapping_pairs as (
46-
select
47-
person_id
48-
, encounter_id_a
49-
, encounter_id_b
50-
from cartesian
51-
where overlap = 1
72+
-- Rank encounters within each overlap group
73+
, encounter_rankings as (
74+
select
75+
e.*
76+
, COALESCE(og.overlap_group_id, e.encounter_id) as overlap_group_id
77+
, case when og.encounter_id is not null then 1 else 0 end as has_overlaps
78+
, ROW_NUMBER() over (
79+
partition by e.person_id, COALESCE(og.overlap_group_id, e.encounter_id)
80+
order by
81+
e.source_type_priority asc -- prefer 'claim' source type
82+
, e.actual_length_of_stay desc -- prefer longer date spans
83+
, e.completeness_score desc -- prefer more complete data
84+
, e.encounter_id asc -- Consistent tie-breaker
85+
) as encounter_rank_in_group
86+
from encounter_enhanced as e
87+
left outer join overlap_groups as og
88+
on e.encounter_id = og.encounter_id
89+
and e.person_id = og.person_id
5290
)
5391

5492

93+
select
94+
encounter_id
95+
, person_id
96+
, admit_date
97+
, discharge_date
98+
, actual_length_of_stay
99+
, source_type_priority
100+
, completeness_score
101+
, overlap_group_id
102+
, has_overlaps
103+
, encounter_rank_in_group
104+
, case
105+
when encounter_rank_in_group = 1 then 1
106+
else 0
107+
end as is_best_encounter
108+
109+
, case
110+
when encounter_rank_in_group = 1 and has_overlaps = 1 then 'Selected as best among overlapping encounters'
111+
when encounter_rank_in_group = 1 and has_overlaps = 0 then 'No overlapping encounters'
112+
else 'Not selected - better encounter exists'
113+
end as selection_reason
55114

56-
select *, '{{ var('tuva_last_run') }}' as tuva_last_run
57-
from overlapping_pairs
115+
from encounter_rankings

models/readmissions/intermediate/readmissions__readmission_crude.sql

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,16 @@ select
1616
, enc.admit_date
1717
, enc.discharge_date
1818
from {{ ref('readmissions__encounter') }} as enc
19-
left outer join {{ ref('readmissions__encounter_overlap') }} as over_a
20-
on enc.encounter_id = over_a.encounter_id_a
21-
left outer join {{ ref('readmissions__encounter_overlap') }} as over_b
22-
on enc.encounter_id = over_b.encounter_id_b
2319
where
2420
admit_date is not null
2521
and
2622
discharge_date is not null
2723
and
2824
admit_date <= discharge_date
29-
and over_a.encounter_id_a is null and over_b.encounter_id_b is null
25+
and not exists (select 1 from {{ ref('readmissions__encounter_overlap') }} as overlap
26+
where overlap.encounter_id = enc.encounter_id and overlap.is_best_encounter = 0)
3027
)
3128

32-
3329
, encounter_sequence as (
3430
select
3531
encounter_id

models/readmissions/readmissions_models.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -265,14 +265,14 @@ models:
265265
alias: _int_encounter_overlap
266266
tags: readmissions
267267
materialized: view
268-
description: "This model lists all pairs of encounters that have some date overlap."
268+
description: "This model lists and labels encounters that overlap to identify the best one out of the group."
269269
columns:
270270
- name: person_id
271271
description: "The unique identifier for the patient"
272-
- name: encounter_id_A
273-
description: "Unique identifier for one of the overlapping encounters"
274-
- name: encounter_id_B
275-
description: "Unique identifier for the other overlapping encounter"
272+
- name: is_best_encounter
273+
description: "Flag that identifies the best encounter that has overlapping dates between encounters for the same patient."
274+
- name: selection_reason
275+
description: "Reason why or why not a particular encounter is selected as best for overlapping dates"
276276

277277
- name: readmissions__encounter_specialty_cohort
278278
config:

models/readmissions/staging/readmissions__stg_core__encounter.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ select
1414
, drg_code
1515
, paid_amount
1616
, primary_diagnosis_code
17+
, encounter_source_type
1718
, '{{ var('tuva_last_run') }}' as tuva_last_run
1819
from {{ ref('core__encounter') }}
1920
where encounter_type = 'acute inpatient'

0 commit comments

Comments
 (0)