A junk dimension collapses a handful of low-cardinality flags and indicators - yes/no booleans, short status codes, payment-method enums - into one small dimension, instead of letting them sprawl across the fact table as a dozen separate columns or hang off it as separate tiny dimensions. The name is Kimball's, and it's not pejorative: it's the place you put the leftover descriptive odds and ends that don't belong to any proper dimension but still need to be queryable.

The problem it solves

An order fact often arrives with a cluster of small attributes: is_gift, is_expedited, payment_type (cash/card/credit), order_channel (web/phone/store), return_flag. Each is two or three values wide. Left on the fact, they bloat its width and force every query that filters on them to scan a wide table. Modeled as five separate dimensions, they add five joins and five surrogate keys to the fact for no analytic payoff - none of them is interesting enough to warrant its own table. The junk dimension folds all of them into a single table with one surrogate key, replacing five fact columns (or five joins) with one.

dim_order_junk (
  order_junk_key   INT,        -- surrogate
  is_gift          CHAR(1),    -- Y / N
  is_expedited     CHAR(1),    -- Y / N
  payment_type     VARCHAR,    -- Cash / Card / Credit
  order_channel    VARCHAR,    -- Web / Phone / Store
  return_flag      CHAR(1)     -- Y / N
)
fact_order (order_id, date_key, customer_key, order_junk_key, amount, ...)

Cartesian product vs. as-encountered population

There are two ways to fill the table. The cartesian-product approach pre-generates every combination of every attribute. With the columns above - 2 × 2 × 3 × 3 × 2 - that's 72 rows, generated once at build time and never touched again. This works when the total combination count is small and bounded. The as-encountered approach inserts a new junk row only when the ETL meets a combination it hasn't seen, looking up the existing key otherwise. Use it when the cartesian product would be large or when the realistic combinations are a sparse subset of the theoretical space - say four attributes that are 10 values each would be 10,000 rows, but only a few hundred actually occur in the data. The line is roughly low hundreds of rows: below it, pre-generate; above it, populate on demand.

What stays out

A junk dimension is for low-cardinality descriptors. A free-text comment field doesn't belong - that's a degenerate dimension kept on the fact, or dropped. An attribute that belongs to a real entity (customer, product) goes in that entity's dimension. The junk dimension is the home for transaction-level flags that describe the event itself and have no other natural owner.

Frequently asked questions

Why is it called a junk dimension? It's where the leftover low-cardinality flags and indicators go - the descriptive odds and ends that don't fit any proper dimension but still need to be filterable. The name is Kimball's standard term, not a quality judgment.

Cartesian product or as-encountered? Pre-generate the full cartesian product when the total combination count is small and bounded (a few dozen to low hundreds of rows). Populate as-encountered when the theoretical space is large but the combinations that actually occur are a sparse subset.

How is this different from a degenerate dimension? A degenerate dimension is a single attribute (usually an operational identifier like an order number) left on the fact with no dimension table. A junk dimension is a small table that groups several low-cardinality attributes behind one key.

What shouldn't go in it? High-cardinality fields (free text, identifiers) and attributes that belong to a real entity. The junk dimension holds only low-cardinality flags describing the event itself.