An order arrived at 09:15. The price changed at 09:10 and again at 09:25. An equality join finds no price at 09:15. A plain “earlier than” join finds too many. We want the most recent price already in effect.

That is an as-of match: pair each event with the applicable earlier state of the same entity.

Explore the idea

Move an order along the price history

Each WREN price applies from its timestamp until the next update.

NULL

order 101 has no applicable price

Before the first known price

The 09:00 price is in the future. An ASOF LEFT JOIN preserves the order without inventing a price.

Two timelines that do not line up

A price applies from its effective timestamp, including that instant, until the next price for the same SKU. The fixture uses local, timezone-free timestamps under one shared clock. A production system must normalize its time semantics before comparing events.

prices4 rows

The price history

Prices apply from their effective timestamp, including that instant, until the next price for the same SKU.

idskueffective_atprice
1WREN2026-01-08 09:00:0010
2WREN2026-01-08 09:10:0012
3WREN2026-01-08 09:25:0011.5
4FIR2026-01-08 09:05:0020

orders5 rows

Orders placed along that timeline

Order 101 predates every WREN price. FIR has its own independent price history.

idskuordered_atqty
101WREN2026-01-08 08:55:002
102WREN2026-01-08 09:00:001
103WREN2026-01-08 09:15:003
104WREN2026-01-08 09:30:002
105FIR2026-01-08 09:10:001

WREN has three price updates. FIR has a separate history. Order 101 arrives before any WREN price, so we cannot legitimately assign it one from this dataset.

Why an ordinary inequality join returns too much

Look at order 104, placed at 09:30. A regular join on an earlier-or-equal price time keeps all three WREN prices:

A regular inequality join keeps every earlier price

SELECT o.id, p.effective_at::VARCHAR AS price_time, p.price
FROM orders o LEFT JOIN prices p
  ON o.sku = p.sku AND o.ordered_at >= p.effective_at
WHERE o.id = 104 ORDER BY p.effective_at;
Expected result · sample data3 rows
id price_time price
104 2026-01-08 09:00:00 10
104 2026-01-08 09:10:00 12
104 2026-01-08 09:25:00 11.5

Every row satisfies the condition. Only the last is the applicable price. If you multiplied the quantity by every matching price and summed, you would invent extra charges.

A regular equality join has the opposite problem: it only matches an order placed exactly when a price was published. An unchanged price remains applicable between updates.

Ask for the closest earlier match

ASOF JOIN combines the equality key with the time inequality. The left side contains events; the right side contains the state changes we want to look backward through.

Match the closest earlier price for each order

SELECT o.id, o.sku, p.effective_at::VARCHAR AS price_time, p.price
FROM orders o
ASOF LEFT JOIN prices p
  ON o.sku = p.sku AND o.ordered_at >= p.effective_at
ORDER BY o.id;
Expected result · sample data5 rows
id sku price_time price
101 WREN NULL NULL
102 WREN 2026-01-08 09:00:00 10
103 WREN 2026-01-08 09:10:00 12
104 WREN 2026-01-08 09:25:00 11.5
105 FIR 2026-01-08 09:05:00 20

Scroll for more columns →

The SKU condition keeps the two product histories separate. ordered_at >= effective_at chooses the latest right-hand timestamp that is not after the order. The equality part of that inequality makes the 09:00 price valid for the 09:00 order.

LEFT preserves order 101 with NULL price fields. An inner as-of join would remove an order with no match, which is inconvenient if the task is to audit every order.

The result has at most one matched right-hand row per left-hand row. Do not rely on an arbitrary winner if your price history contains duplicate (sku, effective_at) keys. Resolve those duplicates under a documented rule before joining; the latest-record lesson demonstrates an explicit tie-break.

Calculate with the matched state

Once the applicable price is attached, the amount is quantity times price:

Calculate amounts while preserving the unpriced order

SELECT o.id, o.qty, p.price, o.qty * p.price AS amount
FROM orders o
ASOF LEFT JOIN prices p
  ON o.sku = p.sku AND o.ordered_at >= p.effective_at
ORDER BY o.id;
Expected result · sample data5 rows
id qty price amount
101 2 NULL NULL
102 1 10 10
103 3 12 36
104 2 11.5 23
105 1 20 20

Scroll for more columns →

Order 103 uses 12.00, producing 36.00. Order 104 uses 11.50, producing 23.00. Order 101 stays unpriced. Replacing that missing price with zero would convert “we cannot price it” into “it was free,” which is a different business statement.

Your turn · check your answer

Find orders with no earlier price

Return id for orders with no applicable price at or before their timestamp. Keep the SKU equality condition.

See the expected result
Your target result1 row
id
101
Sample data is loaded for you.

Preparing the editor…

Need a hint?

An equality join mistakes an unchanged price for a missing one. Use ASOF LEFT JOIN and then test the matched price ID.

Show a solution
SELECT o.id
FROM orders o
ASOF LEFT JOIN prices p
  ON o.sku = p.sku AND o.ordered_at >= p.effective_at
WHERE p.id IS NULL ORDER BY o.id;

Put boundaries around “applicable”

This example assumes a price stays valid until the next update. If prices expire after a fixed interval, add a policy that checks the age of the matched state. An earlier match can be technically correct yet too stale for the business question.

If timestamps come from different time zones, decide whether they represent local wall-clock values or actual instants. Normalize them consistently; changing a display format is not a time-zone conversion.

The same pattern works for exchange rates, sensor readings, and account status at the time of an event. Each needs the same decisions: the equality key, the direction of time, whether an exact-time match counts, what happens before the first state, and how stale a match may be.