Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Designing Dimension Tables with Keys, Attributes, and Hierarchies

Hello! This module moves from deciding what a fact row represents to designing the dimensions that make those facts useful for analysis. We’ll begin with an ordinary dimension table: how to give each row a warehouse-controlled key, retain its source identity, and expose descriptive attributes and hierarchy levels that people can filter and group by.

The goal is a design you could hand to an ETL developer and a report author without leaving either to guess what “property,” “region,” or a key value means. Estimated study time: about 40 minutes.


Start with what one dimension row describes

A dimension supplies context for a measurement. In the property-sale star schema below, PropertySale holds sale-related measurements, while surrounding tables describe the time, property, branch, buyer, and other circumstances of a sale.

A PropertySale fact table connected to descriptive dimensions. The PropertyForSale table provides property attributes that can be used to filter or group sale measurements.

The diagram identifies candidate columns, but it does not, by itself, settle every grain question. Suppose your fact table has one row per recorded property sale. Before designing PropertyForSale, decide whether its lowest-level member is a physical property, a listing of that property, or a version of a listing. Those are different things: a property might be listed more than once, and a listing’s description might change.

For our worked design, assume that the source assigns an identifier to each listing, and that a sale refers to exactly one listing. We will design one dimension row to describe one listing at one recorded version of its attributes. The version qualification leaves room to preserve historical descriptions when required; we are not choosing a change-handling policy yet.

This gives you an immediate test for every proposed column: Does this attribute have one well-defined value for this listing version? Property type and the listing’s city can pass that test. A list of every promotion used to market the property may not. The dimension’s grain is the contract that keeps its descriptive columns coherent.


Give the row two different kinds of identity

Watch this short demonstration before choosing keys. It shows why a source identifier that works for one dimension row can fail when that same business object has two historical rows.

Do You Still Need Surrogate Keys in Modern Data Warehouses?

Watch “Do You Still Need Surrogate Keys in Modern Data Warehouses?” from Guy in a Cube for a concrete illustration of natural and surrogate keys.

Watch the initial comparison, where both keys appear to give correct results. Then watch the changed member. Focus on why joining facts to a source identifier now matches multiple dimension rows, while joining to a surrogate key selects one particular version.

A useful dimension keeps these identities separate:

  • Surrogate key: A unique value assigned by the warehouse to this dimension row. property_key is the dimension’s primary key and appears as a foreign key in the sale fact table.
  • Retained source key: The identifier supplied by the operational source, such as source_property_id. It helps the load process find the corresponding listing and trace data back to its source. It is an attribute of the dimension, not its primary key.

If you integrate two feeds, both might contain a listing numbered 42. Retain source_system alongside source_property_id so that those values are distinguishable. Even the pair must not automatically be declared unique across all dimension rows: if you preserve two versions of one listing, both rows retain the same source identity but have different surrogate keys.

For example:

property_key source_system source_property_id listing description
701 feed_a 42 Original description
745 feed_a 42 Revised description
702 feed_b 42 A different listing

Here, 701 and 745 identify different rows describing the same source listing. 702 belongs to another source altogether. A fact referring to the first version must join on property_key = 701, not merely on source_property_id = 42. How the appropriate version is selected during loading is a later design decision; the key structure makes that selection possible.

The Kimball Group reading establishes this distinction and the dimension table’s basic shape.

Kimball Dimensional Modeling Techniques

Read the Kimball Group’s “Kimball Dimensional Modeling Techniques” for its concise description of a dimension row, its primary key, and its descriptive attributes.

On PDF page 7, under “Basic Dimension Table Techniques,” read the “Dimension Table Structure” subsection from dimension structure. Continue into “Dimension Surrogate Keys,” from surrogate keys. Notice which key joins the fact table and why a source-created key cannot safely identify every historical dimension row.

The source key is worth keeping precisely because it has a different job. In ETL terms, it supports source-to-target matching and lineage; the surrogate key supports the fact-to-dimension relationship. Keeping both avoids making either do work it cannot reliably do.


Make attributes useful to an analyst

A dimension is not just a key-translation table. Its attributes supply the values used in report labels, filters, and GROUP BY clauses. Compare a report offering only property_type_code = 'APT' with one offering property_type_name = 'Apartment'. Retaining the code may help reconciliation with the source; the description makes the result intelligible without a lookup outside the model.

For PropertyForSale, a compact design sketch might be:

Column group Example columns Purpose
Warehouse identity property_key Unique primary key for one dimension row
Source identity source_system, source_property_id Source matching and traceability
Property description property_type_code, property_type_name, street_address, postal_code Filtering, display, and reconciliation
Explicit location levels city_code, city_name, region_code, region_name, country_code, country_name Grouping sales at named geographic levels

Keep names role-specific. The property’s city describes where the listed property is. A Branch dimension’s city would describe where the branch is. Both could be legitimate ways to analyze the same sale, but calling them simply “city” in a reporting layer would invite a misleading result.

Codes and names serve different purposes. Keep a code where it identifies a category reliably, but provide a readable label alongside it. Where names can repeat—for example, two cities with the same name—do not treat the name alone as an identifier. Define codes and their scope clearly; if source-specific codes need reconciliation, the load process must establish the common location identities before you promise consistent regional reporting.

Put stable hierarchy levels on the row

Assume each listing has one city, each city belongs to one region, and each region belongs to one country under the agreed reporting geography. Then a listing row can carry separate city, region, and country columns. An analyst can group sale prices by region without navigating additional geography tables.

This is deliberate duplication: many listing rows may repeat the same region name. In an operational model you might normalize that name into one region record. In an analytical dimension, a wide, descriptive row often makes the intended query easier to write and understand. The load process, however, must maintain consistent codes and labels. Denormalization is not permission for one region code to acquire conflicting names.

The hierarchy also needs a reality check. Do not automatically treat postal_code as a level between property and city; that would require the business’s postal codes to map cleanly to one city in the reporting geography. Likewise, property_type_name is a classification attribute, not a level of the location hierarchy.

Kimball Dimensional Modeling Techniques

Return to the Kimball Group’s “Kimball Dimensional Modeling Techniques” for guidance on flattened dimensions and named hierarchy levels.

On PDF page 8, under “Denormalized Flattened Dimensions,” read flattened dimensions. Then, under “Multiple Hierarchies in Dimensions,” read multiple hierarchies. Finally, on PDF page 14, under “Dealing with Dimension Hierarchies” and “Fixed Depth Positional Hierarchies,” read positional levels. Focus on the condition behind flattening: the levels must have agreed meanings and dependable many-to-one relationships.

Explicit levels support an equally explicit query. If each sale fact row has one property_key, a report can group sales by the property’s region with one fact-to-dimension join:

SELECT
    p.country_code,
    p.country_name,
    p.region_code,
    p.region_name,
    SUM(f.selling_price) AS total_selling_price
FROM property_sale_fact AS f
JOIN dim_property_for_sale AS p
    ON p.property_key = f.property_key
GROUP BY
    p.country_code, p.country_name,
    p.region_code, p.region_name;

Grouping by the codes as well as the labels protects the report when labels are duplicated. The query presumes selling_price is a sale-level amount that can be summed across the selected sales; the dimension provides the grouping context, not the measurement.


A design you can review before building

You now have enough to describe a dimension without committing to database-specific DDL. A concise design contract for the worked example is:

dim_property_for_sale contains one row per property listing per recorded attribute version. property_key uniquely identifies the row. source_system and source_property_id retain its source identity. Descriptive attributes include property type and address. The agreed reporting-location hierarchy has separate country, region, and city codes and names, with each listing assigned to one city for that version.

Before accepting that contract, review it from three perspectives. ETL needs to know whether (source_system, source_property_id) identifies a listing consistently and how source locations are mapped. Reporting needs clear labels and dependable hierarchy membership. Fact loading needs exactly one intended property_key for each sale event—not every dimension row that happens to share a source identifier.

Those checks are more informative than counting the columns. A dimension with many attributes is useful if each has clear meaning at the declared grain; a small dimension is unsafe if its key or hierarchy permits ambiguous joins.


Conclusion

A sound dimension table separates row identity from source identity. Its surrogate primary key identifies the precise descriptive row a fact should use; its retained, source-qualified key supports matching and traceability. Readable attributes give facts analytical meaning, while explicit, validated hierarchy columns make grouping predictable.

Next, we’ll examine what changes when hierarchy levels are not consistently shaped—particularly ragged and parent-child relationships—and when flattening alone no longer describes them well.

Can't find a good explanation? Sign up and we'll make it for you

Sign up