SQL · medium · Reported at Airbnb

fact_orders has an order_ts. dim_customer is SCD Type 2 with valid_from/valid_to. Join each order to the customer attributes that were current when the order was placed.

Asked in Data Engineer interviews, in the SQL round.

Short answer

A range join, not an equality join: ON o.customer_id = d.customer_id AND o.order_ts >= d.valid_from AND o.order_ts < d.valid_to. The half-open interval is what stops an order matching two versions on a boundary. Joining on the customer key alone, or filtering d.is_current = true, silently reports today's attributes for historical orders, the classic SCD mistake, and the reason last year's revenue-by-segment number quietly changes.

Sample data

These tables are loaded into an in-browser engine on this page, so you can run your own answer against them. No account needed.

fact_orders

order_idcustomer_idorder_tsamount
1102026-01-15120
2102026-03-0280
3112026-02-10200
4102026-02-0160
5112026-03-2045

dim_customer

customer_idsegmentvalid_fromvalid_to
10retail2026-01-012026-02-01
10wholesale2026-02-012026-12-31
11retail2026-01-012026-12-31

How to answer it

Definition

A Type 2 dimension keeps one row per version of a customer, each valid for a window of time. Joining on the key alone would multiply every order by the number of versions, so the join has to include the time.

Syntax

FROM  fact_orders o
JOIN  dim_customer d
  ON  d.customer_id = o.customer_id
  AND o.order_ts >= d.valid_from
  AND o.order_ts <  d.valid_to        -- half open: >= start, < end

Example

SELECT o.order_id, o.order_ts, d.customer_id, d.segment, d.country
FROM   fact_orders o
JOIN   dim_customer d
  ON   d.customer_id = o.customer_id
 AND   o.order_ts >= d.valid_from
 AND   o.order_ts <  d.valid_to;

Why half open

version  valid_from   valid_to     segment
      1  2025-01-01   2025-03-01   Standard
      2  2025-03-01   9999-12-31   Premium

An order at exactly 2025-03-01 00:00:00 matches version 2 only. Had both ends been inclusive, that order would match both versions and be counted twice. Every boundary instant belongs to exactly one version, and that is the property which keeps the fact row count unchanged.

Key points

FAQ

The 30-second answer

It is a range join rather than an equality join. I match on the customer key and also require the order timestamp to fall inside the version's validity window: greater than or equal to valid_from, and strictly less than valid_to.
The half-open interval is the part I would emphasise. If both ends were inclusive, an order landing exactly on a version boundary would match two rows and be counted twice, which shows up as revenue that quietly does not tie out. I would also make sure the current version carries a far-future valid_to rather than NULL, and I would check the fact row count before and after, because it must not change.

In the interview

Practice this for real