skip to content

What is a bridge table in a dimensional model, and what problem does it solve?

level: middleimportance: must knowfreq 62%

answer

  1. some relationships are genuinely many-to-many
  2. adding the key to the fact would change what?
  3. a key for the set, not the member
  4. three columns: group, member, share
  5. the fact grain must survive untouched

basics

~20 s

A bridge table resolves a many-to-many between a fact and a dimension: one row per group member, holding a group key, a dimension key and often a weighting factor. The fact keeps its original grain and still reaches every member.

solid answer

~50 s

Some relationships are genuinely many-to-many: a hospital visit has several diagnoses, an account has several owners, a deal has several sales reps. Putting the dimension key directly on the fact row would force one row per pair and change the grain, so the measure repeats. A **bridge table** avoids that. The fact row carries a **group key** — an identifier for the *set* of members — and the bridge holds one row per (group key, dimension key) pair, optionally with a **weighting factor**. Queries that need the members join fact → bridge → dimension; queries that only want totals ignore the bridge entirely and stay correct. The same pattern also attaches to a dimension rather than a fact: a customer row can point at a group of segments through a bridge. The price is a fan-out on any bridged join, which is what the weighting factor is there to correct.

code

sql · 15 lines
sql
-- fact keeps one row per visit; the bridge holds the set members
CREATE TABLE fact_visit (
  visit_key           BIGINT PRIMARY KEY,
  date_key            INTEGER NOT NULL,
  patient_key         BIGINT  NOT NULL,
  diagnosis_group_key BIGINT  NOT NULL,
  charge_amount       DECIMAL(12,2) NOT NULL
);

CREATE TABLE bridge_diagnosis_group (
  diagnosis_group_key BIGINT NOT NULL,
  diagnosis_key       BIGINT NOT NULL,
  weighting_factor    DECIMAL(9,6) NOT NULL,
  PRIMARY KEY (diagnosis_group_key, diagnosis_key)
);

go deeper

for a junior

Be ready to recognise a many-to-many between an event and a dimension — several diagnoses per visit, several owners per account — and to say that the extra key does not simply go on the fact row.

for a middle

Explain the three-column bridge, the group key indirection, and exactly what would go wrong to the grain and to SUM without it. Show a query that walks fact to bridge to dimension.

for a senior

Demonstrate judgment about group-key reuse, about membership changing over time, and about warning consumers that any bridged join fans out and needs weighting before it is summed.

for a principal

Own where bridged many-to-many relationships are permitted in a published model at all, since every consumer tool must handle them, and decide when the complexity is better absorbed in a downstream mart than exposed enterprise-wide.

## The problem: a multi-valued dimension Dimensional modelling assumes each fact row points at exactly one member of each dimension — one product, one store, one date. Reality sometimes disagrees: - a hospital visit is coded with one to five **diagnoses**; - a bank account has one or more **owners**; - a closed deal is credited to several **sales reps**; - a book has several **authors**; - a customer belongs to several marketing **segments**. This is a **multi-valued dimension**. The two naive fixes are both bad. Adding `diagnosis_key` to the fact row silently redefines the grain from one row per visit to one row per visit-diagnosis; the visit's charge amount is now repeated on every row and `SUM(charge_amount)` overstates revenue. Picking a single "primary" value keeps the grain but throws away the other members, and secondary diagnoses are often exactly what the analysis is about. ## The bridge structure A bridge introduces one level of indirection — a key for the *set*: ```sql CREATE TABLE bridge_diagnosis_group ( diagnosis_group_key BIGINT NOT NULL, diagnosis_key BIGINT NOT NULL, weighting_factor DECIMAL(9,6) NOT NULL, PRIMARY KEY (diagnosis_group_key, diagnosis_key) ); ``` The fact table keeps its grain — one row per visit — and carries `diagnosis_group_key` instead of `diagnosis_key`. Three columns do the work: the group key names the set, the dimension key names one member of it, and the weighting factor says what share of a measure belongs to that member. A query that needs diagnosis detail walks the chain: ```sql SELECT d.diagnosis_name, SUM(f.charge_amount * b.weighting_factor) FROM fact_visit f JOIN bridge_diagnosis_group b ON b.diagnosis_group_key = f.diagnosis_group_key JOIN dim_diagnosis d ON d.diagnosis_key = b.diagnosis_key GROUP BY d.diagnosis_name; ``` A query that only wants total charges never touches the bridge and is exactly right — which is the property that makes the pattern safe. ## Two attachment points The bridge can hang off the **fact**, as above, when the many-to-many is between an event and a dimension. It can equally hang off a **dimension**: put `segment_group_key` on the customer row, bridge that to `dim_segment`, and one customer belongs to many segments while the customer dimension keeps one row per customer. Same three columns, different anchor. Choosing the anchor is a question of where the multiplicity genuinely lives — if the *set* varies per event (diagnoses of a visit) it belongs to the fact; if it is a property of the entity (a customer's segments) it belongs to the dimension. ## Managing group keys Groups repeat heavily — thousands of visits share the same two-diagnosis combination. Deduplicating them keeps the bridge small and makes group-level analysis possible: build the group key from a deterministic function of the **sorted list of member keys**, so identical sets collapse to one group. The alternative — a fresh group key per fact row — works and is simpler to load, but the bridge then grows with the fact table and you lose the ability to ask "how many visits had exactly this combination". ## Time variance Group membership changes: an account gains an owner, a customer leaves a segment. Two options. Mint a **new** group key when the set changes and repoint subsequent facts at it, so history is naturally preserved because old facts keep pointing at the old group. Or **effective-date** the bridge rows and constrain queries to a point in time — more flexible, and the dating mechanics are the slowly-changing-dimension topic's ground. The first option is the usual default for fact-attached bridges precisely because it needs no dating logic in the query. ## What it costs Every bridged join **fans out**: one fact row matches as many bridge rows as its group has members, so unweighted measures multiply. That is not a flaw to be patched later; it is the defining behaviour of the pattern, and the weighting factor is the standard correction. Bridges also complicate the model for consumers — many BI semantic layers handle many-to-many awkwardly, and some analysts will join the bridge for a report that did not need it. Both are reasons to label bridged measures explicitly rather than to avoid the pattern. ## Bridges you may already know by another name The same three-column shape covers the **multi-valued attribute** bridge (a customer's several segments) and, with ancestor/descendant instead of group/member, the **hierarchy** bridge that rolls facts up a variable-depth tree. All of them are the dimensional answer to the same question: how do I express many-to-many without letting it rewrite my fact grain? ## What interviewers listen for That you name the grain damage the bridge exists to prevent, that you can list the three columns unprompted, that you know the bridge can attach to either a fact or a dimension, and that you volunteer the fan-out rather than being caught by it.

  • How do you decide whether to reuse group keys across fact rows or mint a fresh one per row?
    Reuse when the same combinations recur and you want to analyse combinations — derive the group key deterministically from the sorted member key list so identical sets collapse. Mint per row when sets are near-unique or loading simplicity matters more; the bridge then grows with the fact table and combination-level analysis is lost.
  • What happens to existing facts when a group's membership changes?
    The usual default is to mint a new group key for the new membership and point subsequent facts at it, so old facts keep referring to the old group and history stays intact with no dating logic in queries. The alternative is effective-dating the bridge rows, which is more flexible but pushes a point-in-time predicate into every query.
  • Would you ever attach a bridge to a dimension instead of a fact?
    Yes. When the multiplicity is a property of the entity rather than the event — a customer's marketing segments, a product's several certifications — put the group key on the dimension row and bridge to the sub-dimension. The customer dimension keeps one row per customer and the fact join is unchanged.

A bridge is a guest list. The invitation records which list applies, not each guest, so the invitation stays one document; anyone who wants names opens the list and accepts that reading it produces one line per guest.

saying these in an interview costs you the question

  • Puts the multi-valued key straight on the fact row
  • Says a bridge is just a join table with no consequences
  • Thinks the fact grain is unaffected by exploding rows
  • Ignores the fan-out that a bridged join produces
  • Believes only a fact table can anchor a bridge

context