# MA pbp section b5 partial hosp

`medicare_silver.pbp_section_b5_partial_hosp` · Medicare Advantage, Part D, Medicaid · status partial

Section B5: Partial hospitalization cost-share. Coverage: All years.

**Grain.** One row per Medicare Advantage plan-segment per plan-year per CMS quarterly PUF release. Minimal verified key = contract_id + plan_id + segment_id + observed_period + quarter (= 201,488 rows, exactly the table count, unique). bid_id and effective_period_start are redundant (bid_id is always contract_plan_segment; effective_period_start always equals observed_period), and the profiler's extra key columns pbp_a_plan_type / pbp_b5_maxplan_yesno_pos are not needed. Dropping quarter collapses to 114,175 distinct plan-years because 2018-2024 plan-years are republished in 2-4 quarterly PUFs.

**How to use it.** Benchmark partial-hospitalization member cost-share across MA plans for the latest year, de-duplicating the quarterly snapshots and handling the era-shifted copay column. Example: SELECT contract_id, plan_id, segment_id, pbp_b5_copay_yn, COALESCE(pbp_b5_copay_mcia_amt, pbp_b5_copay_mcia_amt_min, pbp_b5_copay_mciaamt) AS copay_usd, pbp_b5_coins_yn, COALESCE(pbp_b5_coins_pct, pbp_b5_coins_pct_min) AS coins_pct, pbp_b5_auth_yn FROM medicare_silver.pbp_section_b5_partial_hosp WHERE report_year = 2026 AND quarter = (SELECT MAX(quarter) FROM medicare_silver.pbp_section_b5_partial_hosp WHERE report_year = 2026). This answers 'for each 2026 MA plan, does partial hospitalization need prior auth and what copay/coinsurance does a member owe?' Join to pbp_section_a (or other pbp_section_* tables) on contract_id+plan_id+segment_id+report_year+quarter to assemble the full benefit package.

**Traps.**
- QUARTERLY-SNAPSHOT DUPLICATION (biggest trap): a plan-year is NOT one row. For 2018-2024, CMS republished the same plan-year in 2-4 quarterly PUFs, so each plan appears 2-4 near-identical times distinguished only by `quarter`. 2001-2017, 2021, 2025, 2026 are Q1-only (CMS dropped quarterly cadence CY2025+). There is NO is_current/latest flag. To get one row per plan-year you MUST pick a single quarter, e.g. the max(quarter) per (contract_id,plan_id,segment_id,report_year). Counting rows = counting plan-year-quarters, ~1.76x the plan-year count.
- ERA-DEPENDENT COST COLUMNS - the active copay column moves across years: pbp_b5_copay_mciaamt (2001 only) -> pbp_b5_copay_mcia_amt (2002-2015) -> pbp_b5_copay_mcia_amt_min/_max (2016+). Coinsurance similarly splits: pbp_b5_coins_pct (plain, some eras) vs pbp_b5_coins_pct_min/_max (2016+), and they are mutually exclusive. Always COALESCE across the family (e.g. COALESCE(copay_mcia_amt, copay_mcia_amt_min, copay_mciaamt)) or you silently get all-NULL for half the history.
- B5A/B5B SUB-BENEFIT COLUMNS ARE 2026-ONLY: all 18 pbp_b5a_* / pbp_b5b_* columns are populated solely in report_year 2026 (CMS split partial hospitalization into 5A/5B sub-services that year) and are 100% NULL for 2001-2025. Do not treat them as historical fields.
- YN GATES DRIVE AMOUNT PRESENCE - the cost columns are null unless the matching _yn flag says Yes: copay_yn='1' <-> copay amount present (verified 100%/0%); coins_yn='1' or '3' <-> coinsurance pct present, '2'=No=no pct. coins_yn has a third state '3' (range variant). A NULL amount usually means 'no charge / not applicable', not missing data - read it together with its _yn.
- MANY DECLARED COLUMNS ARE INERT - every `_pos` place-of-service variant (auth_pos, coins_pct_pos, maxpln_*_pos, maxplan_yesno_pos, etc.) plus maxenr_per_d are ~100% NULL (maxplan_yesno_pos has only 29 non-null rows). maxplan_yesno_pos appears in the profiler's candidate key but is effectively empty - do NOT include it in the grain. plan_id (656) and segment_id (27) are only unique WITHIN a contract, never globally.
- DESIGN DATA, NOT SPEND: these are plan-filed cost-share parameters (what a member would owe), not paid claims, allowed amounts, or utilization. Use for benefit-design benchmarking across MA plans/years; do not read as Vlada-priced or as actual dollars paid. Provenance lives in source_url / sha256_content_hash / asserted_at (bitemporal load time).

| column | type | meaning |
|---|---|---|
| contract_id | string | CMS Medicare Advantage contract number (e.g. 'H0028'). 1,972 distinct. Part of the grain key. Joins to contract/enrollment tables. |
| plan_id | string | Plan number within the contract, zero-padded 3-char string (e.g. '001','008'). 656 distinct values but only unique WITHIN a contract. Part of grain key. |
| segment_id | string | Plan segment within the plan (service-area split), string (e.g. '0','1','2'). 27 distinct. Part of grain key. Most plans are segment '0'. |
| version | double | CMS PBP bid version/iteration number for that plan-year (double, e.g. 2.0,3.0,4.0,5.0). The plan's internal submission revision; does NOT make rows duplicate (each plan-year-quarter has one version). |
| report_year | double | Contract/benefit calendar year as a double (2001-2026). Equals YEAR(observed_period). This is the plan year the benefits apply to. |
| quarter | double | CMS PUF release quarter (1-4) the row was sourced from. CRITICAL: part of the grain. A single plan-year is re-published in multiple quarterly PUFs for 2018-2024, so the SAME plan appears 2-4 times (once per quarter). 2001-2017, 2021, 2025, 2026 are Q1-only (CMS dropped quarterly cadence CY2025+). |
| bid_id | string | Composite key string, ALWAYS exactly contract_id//'_'//plan_id//'_'//segment_id (0 mismatches in 201,488 rows). Fully redundant with the three id columns; convenient join handle, NOT an independent dimension. |
| orgtype | string | Organization type code (string '01'-'14'). '01' dominates (177,737 = local CCP/HMO-PPO). '08','04','11','06','05' etc are other org types (PFFS, regional PPO, cost, etc). |
| pbp_a_ben_cov | string | Section-A benefit-coverage indicator carried into this section ('1' = ~196k rows, '2' = 4,373, null = 342). NOT a clean covered/not-covered flag — both '1' and '2' rows carry populated B5 cost-share detail. Inherited context field. |
| pbp_a_plan_type | string | Section-A plan-type code (string). '01'=HMO (101k), '04'=local PPO (56k), '02'=HMOPOS (20k), '20','09','31','18','48' etc. Describes the MA plan product type. |
| pbp_b5_auth | string | Free/numeric auth detail field. Almost entirely null in modern eras (61,700 nulls); superseded by pbp_b5_auth_yn. Legacy. |
| pbp_b5_auth_pos | string | Place-of-service ('_pos') variant of the auth field. Effectively always null (only 29 non-null, all in 2001). Inert column. |
| pbp_b5_auth_yn | string | Does partial-hospitalization require prior authorization? Coded '1'=Yes (122,635), '2'=No (17,153), null=61,700 (older eras / not-collected). The primary auth flag. |
| pbp_b5_coins_cov | string | Coinsurance coverage-scope descriptor. Almost entirely null. Legacy/rarely populated. |
| pbp_b5_coins_cov_pos | string | Place-of-service variant of coins_cov. Effectively all null. Inert. |
| pbp_b5_coins_pct | double | Coinsurance percent (plain, single value). Populated 12,926 rows and ONLY in eras that use this column (mutually exclusive with coins_pct_min/max — plain_only=12,926). Era-dependent: this is the active coinsurance column in some years, the min/max pair in others. |
| pbp_b5_coins_pct_max | double | Upper bound of coinsurance percent when the plan reports a range. Real values 0-45%. Populated in the min/max-era rows. Equals min when the rate is a single value. |
| pbp_b5_coins_pct_min | double | Lower bound (or single value) of coinsurance percent. Median 20%, max 45%. 46,175 populated. The active coinsurance column for 2016+ era rows. |
| pbp_b5_coins_pct_pos | double | Place-of-service variant of coinsurance percent. Effectively all null (18 rows, 2001 only). Inert. |
| pbp_b5_coins_yesno | string | Legacy yes/no coinsurance descriptor (string). Almost entirely null; superseded by pbp_b5_coins_yn. |
| pbp_b5_coins_yesno_pos | string | Place-of-service variant of coins_yesno. All null. Inert. |
| pbp_b5_coins_yn | string | Does the plan charge coinsurance for partial hospitalization? '2'=No (132,248, no pct present), '1'=Yes (57,878, pct present), '3'=Yes-range variant (1,182, all have pct), null=10,180. GATE for whether the coins_pct* columns are populated. |
| pbp_b5_copay_amtd_pos | double | Place-of-service copay-amount-per-day variant (double). Effectively all null. Inert. |
| pbp_b5_copay_mcia_amt | double | Copay dollar amount (Medicare Covered If Applicable). THE active copay column for plan years 2002-2015 (0 rows outside that era). Real dollars, median ~$36, max $800. Era-specific — null in 2001 and 2016+. |
| pbp_b5_copay_mcia_amt_max | double | Upper bound of copay dollar amount when a range is reported. Active 2016+ era. Real dollars, max $750. Equals min for single values. |
| pbp_b5_copay_mcia_amt_min | double | Lower bound (or single value) copay dollar amount. THE active copay column for 2016+ plan years. Median $55, max $750. Null before 2016. |
| pbp_b5_copay_mciaamt | double | Oldest-form copay dollar column (note: no underscore before 'amt'). Populated ONLY in report_year 2001 (453 rows). Replaced by mcia_amt then mcia_amt_min/max. Era-specific copay value. |
| pbp_b5_copay_yesno | string | Legacy yes/no copay descriptor (string). Almost entirely null; superseded by pbp_b5_copay_yn. |
| pbp_b5_copay_yesno_pos | string | Place-of-service variant of copay_yesno. All null. Inert. |
| pbp_b5_copay_yn | string | Does the plan charge a copay for partial hospitalization? '1'=Yes (112,671, 100% have a copay amount), '2'=No (78,635, 0% have amount), '3'=rare (2), null=10,180. GATE for the copay amount columns. Verified: yn drives amount presence exactly. |
| pbp_b5_ded_amt | double | Deductible dollar amount specific to this benefit. Rarely used: only 100 rows populated, 0-$952. Partial-hosp almost never has its own deductible. |
| pbp_b5_ded_amt_pos | double | Place-of-service variant of the deductible amount. All null. Inert. |
| pbp_b5_ded_yesno | string | Legacy yes/no deductible descriptor. Almost entirely null. Legacy. |
| pbp_b5_ded_yesno_pos | string | Place-of-service variant of ded_yesno. All null. Inert. |
| pbp_b5_ded_yn | string | Does this benefit have its own deductible? Overwhelmingly '2'=No (191,184); '1'=Yes only 100; null 10,204. Gate for ded_amt. |
| pbp_b5_maxenr_amt | double | Per-enrollee maximum out-of-pocket dollar amount for this benefit. Rarely populated (maxenr_yn is almost always No). Dollar cap per member. |
| pbp_b5_maxenr_amt_pos | double | Place-of-service variant of maxenr_amt. All null. Inert. |
| pbp_b5_maxenr_per | string | Per-enrollee-maximum periodicity number (string). Almost all null (201,157); '3' (284) and '6' (47) when present. Composes with per_d unit. Rarely used. |
| pbp_b5_maxenr_per_d | string | Per-enrollee-maximum period denominator/unit (D=day, M=month, etc per CMS dict). 100% NULL in this table — declared but never populated for B5. |
| pbp_b5_maxenr_yesno | string | Legacy yes/no per-enrollee-max descriptor. Almost all null. Legacy. |
| pbp_b5_maxenr_yn | string | Does the plan apply a per-enrollee maximum to this benefit? Overwhelmingly '2'=No (190,956); '1'=Yes only 328; null 10,204. Gate for the maxenr_* fields. |
| pbp_b5_maxenr_yn_pos | string | Place-of-service variant of maxenr_yn. All null. Inert. |
| pbp_b5_maxplan_yesno_pos | string | Plan-level-maximum yes/no, place-of-service variant. 99.99% NULL (only 29 non-null, value '2'). Appears in the profiler's candidate key but is inert; the true minimal grain does NOT need it. |
| pbp_b5_maxpln_amt_pos | double | Plan-level maximum dollar amount, place-of-service variant. All null. Inert. |
| pbp_b5_maxpln_cov_pos | string | Plan-level maximum coverage scope, place-of-service variant. All null. Inert. |
| pbp_b5_maxpln_per_pos | string | Plan-level maximum periodicity, place-of-service variant. 100% null. Inert. |
| pbp_b5_refer_yn | string | Is a referral required for partial hospitalization? '2'=No (144,584), '1'=Yes (44,369), null=12,535. |
| pbp_b5a_coins_pct | double | Coinsurance percent for sub-benefit B5A. NEW CY2026 only (populated solely in report_year 2026, ~994 rows). Part of the 2026 split of partial-hosp into 5A/5B sub-services. Null for all prior years. |
| pbp_b5a_copay_amt | double | Copay dollar amount for sub-benefit B5A. NEW CY2026 only (4,444 rows in 2026, null elsewhere). |
| pbp_b5b_auth_yn | string | Prior-auth flag for sub-benefit B5B. NEW CY2026 only (7,002 rows). Null for all years before 2026. |
| pbp_b5b_coins_pct | double | Coinsurance percent for sub-benefit B5B. CY2026 only (903 rows). |
| pbp_b5b_coins_pct_max | double | Upper bound of B5B coinsurance percent. CY2026 only. |
| pbp_b5b_coins_pct_min | double | Lower bound/single B5B coinsurance percent. CY2026 only. |
| pbp_b5b_coins_yn | string | Does B5B sub-benefit charge coinsurance? CY2026 only. Gate for b5b_coins_pct*. |
| pbp_b5b_copay_amt | double | Copay dollar amount for B5B. CY2026 only (4,495 rows). |
| pbp_b5b_copay_amt_max | double | Upper bound of B5B copay dollar amount (range). CY2026 only. |
| pbp_b5b_copay_amt_min | double | Lower bound/single B5B copay dollar amount. CY2026 only. |
| pbp_b5b_copay_yn | string | Does B5B charge a copay? CY2026 only. Gate for b5b_copay_amt*. |
| pbp_b5b_ded_amt | double | Deductible dollar amount for B5B. CY2026 only. |
| pbp_b5b_ded_yn | string | Does B5B have its own deductible? CY2026 only. |
| pbp_b5b_maxenr_amt | double | Per-enrollee maximum dollar amount for B5B. CY2026 only. |
| pbp_b5b_maxenr_per | string | Per-enrollee-max periodicity number for B5B. CY2026 only (only 7 rows populated). |
| pbp_b5b_maxenr_per_d | string | Per-enrollee-max period unit for B5B. 100% null even in 2026. Inert. |
| pbp_b5b_maxenr_yn | string | Does B5B apply a per-enrollee maximum? CY2026 only. |
| pbp_b5b_refer_yn | string | Is a referral required for B5B? CY2026 only. |
| observed_period | date | Vlada-added DATE = first day of the plan year (e.g. 2018-01-01). Equals effective_period_start (0 mismatches) and YEAR equals report_year. The benefit-year anchor; part of grain. Annual cadence. |
| asserted_at | date | Vlada provenance DATE: when this row was loaded/asserted into the silver layer (e.g. 2026-05-20). Bitemporal 'system time' column; same for a load batch. Not a benefit attribute. |
| effective_period_start | date | DATE the benefit takes effect = Jan 1 of report_year. Identical to observed_period (verified 0 diffs). Redundant with observed_period. |
| effective_period_end | date | DATE the benefit period ends = Dec 31 of report_year (e.g. 2018-12-31). Annual coverage window close. |
| cms_vintage_sequence | int | Vlada integer ordering CMS source vintages (e.g. 2). Sequence of the CMS PUF vintage this row came from; provenance/ordering aid. |
| parse_revision | int | Vlada integer parser revision (e.g. 2). Which version of the ingestion parser produced the row. Provenance, not benefit data. |
| parser_artifact_hash | string | SHA-256 of the parser artifact/code that produced the row. Provenance/reproducibility. |
| source_url | string | CMS download URL of the source PUF zip (e.g. .../pbp-benefits-2018-q2.zip). Encodes the year+quarter of the source release; ground-truth provenance. |
| sha256_content_hash | string | SHA-256 of the source file content. Integrity/dedup of the underlying CMS file. |

Tools that read it: get_pbp_benefit

Query it: `query_substrate` with `SELECT ... FROM medicare_silver.pbp_section_b5_partial_hosp WHERE ... LIMIT 100`; read `describe_data_source("medicare_silver.pbp_section_b5_partial_hosp")` first.
