Back to Cube

Dimensions

docs-mintlify/docs/data-modeling/dimensions.mdx

1.6.6515.6 KB
Original Source

Dimensions represent attributes of individual rows in your data. They are the fields you group by and filter on — things like status, city, product_name, or created_at. Each dimension maps to a column or SQL expression in your data source.

<Note>

See the dimensions reference for the full list of parameters and configuration options.

</Note>

Defining dimensions

A dimension specifies the SQL expression and its type:

<CodeGroup>
yaml
cubes:
  - name: orders
    sql_table: orders

    dimensions:
      - name: id
        sql: id
        type: number
        primary_key: true

      - name: status
        sql: status
        type: string

      - name: created_at
        sql: created_at
        type: time
javascript
cube(`orders`, {
  sql_table: `orders`,

  dimensions: {
    id: { sql: `id`, type: `number`, primary_key: true },
    status: { sql: `status`, type: `string` },
    created_at: { sql: `created_at`, type: `time` }
  }
})
</CodeGroup>

Dimension types

Data type in SQLDimension type in Cube
timestamp, date, timetime
text, varcharstring
integer, bigint, decimalnumber
booleanboolean

Primary keys

Every cube that participates in joins should define a primary_key dimension. Cube uses primary keys to avoid fanouts — when rows get duplicated during joins and aggregates are over-counted. Composite primary keys can be created by concatenating columns:

yaml
dimensions:
  - name: composite_key
    sql: "CONCAT({CUBE}.order_id, '-', {CUBE}.product_id)"
    type: string
    primary_key: true

Time dimensions

Time dimensions are dimensions of the time type. They enable grouping by time granularity (year, quarter, month, week, day, hour, minute, second) and are essential for time-series analysis.

yaml
dimensions:
  - name: created_at
    sql: created_at
    type: time

When queried, you can group by any built-in granularity without defining additional dimensions.

Custom granularities

You can define custom granularities for time dimensions when the built-in ones don't fit — for example, weeks starting on Sunday or fiscal years:

<CodeGroup>
yaml
cubes:
  - name: orders
    # ...

    dimensions:
      - name: created_at
        sql: created_at
        type: time
        granularities:
          - name: sunday_week
            interval: 1 week
            offset: -1 day

          - name: fiscal_year
            interval: 1 year
            offset: 1 month
javascript
cube(`orders`, {
  // ...

  dimensions: {
    created_at: {
      sql: `created_at`,
      type: `time`,
      granularities: {
        sunday_week: { interval: `1 week`, offset: `-1 day` },
        fiscal_year: { interval: `1 year`, offset: `1 month` }
      }
    }
  }
})
</CodeGroup>

Time dimensions are essential for performance features like partitioned pre-aggregations and incremental refreshes.

<Note>

See the following recipes:

</Note>

Proxy dimensions

Proxy dimensions reference dimensions from the same cube or other cubes, providing a way to reuse existing definitions and reduce code duplication.

Within the same cube

Reference existing dimensions to build derived ones without duplicating SQL:

<CodeGroup>
yaml
cubes:
  - name: users
    sql_table: users

    dimensions:
      - name: initials
        sql: "SUBSTR(first_name, 1, 1)"
        type: string

      - name: last_name
        sql: "UPPER(last_name)"
        type: string

      - name: full_name
        sql: "{initials} || '. ' || {last_name}"
        type: string
javascript
cube(`users`, {
  sql_table: `users`,

  dimensions: {
    initials: { sql: `SUBSTR(first_name, 1, 1)`, type: `string` },
    last_name: { sql: `UPPER(last_name)`, type: `string` },
    full_name: { sql: `${initials} || '. ' || ${last_name}`, type: `string` }
  }
})
</CodeGroup>

From other cubes

If cubes are joined, you can bring a dimension from one cube into another. Cube generates the necessary joins automatically:

<CodeGroup>
yaml
cubes:
  - name: orders
    sql_table: orders

    joins:
      - name: users
        sql: "{CUBE}.user_id = {users.id}"
        relationship: many_to_one

    dimensions:
      - name: id
        sql: id
        type: number
        primary_key: true

      - name: user_name
        sql: "{users.name}"
        type: string
javascript
cube(`orders`, {
  sql_table: `orders`,

  joins: {
    users: {
      sql: `${CUBE}.user_id = ${users.id}`,
      relationship: `many_to_one`
    }
  },

  dimensions: {
    id: { sql: `id`, type: `number`, primary_key: true },
    user_name: { sql: `${users.name}`, type: `string` }
  }
})
</CodeGroup>

Time dimension granularity references

When referencing a time dimension, you can specify a granularity to create a proxy dimension at that specific granularity — including custom granularities:

yaml
dimensions:
  - name: created_at
    sql: created_at
    type: time
    granularities:
      - name: sunday_week
        interval: 1 week
        offset: -1 day

  - name: created_at_year
    sql: "{created_at.year}"
    type: time

  - name: created_at_sunday_week
    sql: "{created_at.sunday_week}"
    type: time

Subquery dimensions

Subquery dimensions reference measures from other cubes, effectively turning an aggregate into a per-row value. This enables nested aggregations — for example, calculating the average of per-customer order counts.

<CodeGroup>
yaml
cubes:
  - name: orders
    sql_table: orders

    joins:
      - name: users
        sql: "{users}.id = {CUBE}.user_id"
        relationship: many_to_one

    dimensions:
      - name: id
        sql: id
        type: number
        primary_key: true

    measures:
      - name: count
        type: count

  - name: users
    sql_table: users

    dimensions:
      - name: id
        sql: id
        type: number
        primary_key: true

      - name: name
        sql: name
        type: string

      - name: order_count
        sql: "{orders.count}"
        type: number
        sub_query: true

    measures:
      - name: avg_order_count
        sql: "{order_count}"
        type: avg
javascript
cube(`orders`, {
  sql_table: `orders`,

  joins: {
    users: {
      sql: `${users}.id = ${CUBE}.user_id`,
      relationship: `many_to_one`
    }
  },

  dimensions: {
    id: { sql: `id`, type: `number`, primary_key: true }
  },

  measures: {
    count: { type: `count` }
  }
})

cube(`users`, {
  sql_table: `users`,

  dimensions: {
    id: { sql: `id`, type: `number`, primary_key: true },
    name: { sql: `name`, type: `string` },

    order_count: {
      sql: `${orders.count}`,
      type: `number`,
      sub_query: true
    }
  },

  measures: {
    avg_order_count: {
      sql: `${order_count}`,
      type: `avg`
    }
  }
})
</CodeGroup>

The order_count subquery dimension computes the order count per user. The avg_order_count measure then averages those per-user values. Cube implements this as a correlated subquery via joins for optimal performance.

<Note>

See the following recipes:

</Note>

Dimensions can declare links — clickable navigation targets that supporting tools (such as Cube Cloud Workbooks) surface next to the dimension's values.

links is a list, so a single dimension can declare any number of links — they all appear together in the cell menu. Each link points either to an external URL (url) or to another Cube Cloud dashboard (dashboard, a drill-in), and its URL is built per row from the dimension's data. The example below declares two links on one dimension (an external search and a drill-in).

<Note>

Dimension links require Cube v1.6.53 or newer.

</Note>

Parameters

links is a list of link objects. Each link accepts:

ParameterRequired?Description
nameRequiredIdentifier, unique within the dimension. Also used in the synthetic dimension name (see Behavior).
labelRequiredThe text shown for the link in the UI.
urlEither url or dashboardSQL expression that builds an external URL per row. May reference columns and other dimensions.
dashboardEither url or dashboardThe target Cube Cloud dashboard's slug (a drill-in). Each link sets exactly one of url or dashboard — never both — but different links on the same dimension can mix the two.
iconOptionalA Tabler icon name (see Icons). Defaults to a generic link icon.
targetOptionalWhere to open the link: blank (default — new tab) or self (same tab). Applies to external links only — drill-ins always navigate in-app (see Behavior).
paramsOptionalExtra per-row parameters. For url: they are appended as query parameters. For dashboard: they become equality filters on the target dashboard — each key is a member of the target dashboard's view (a cube path such as orders.status is auto-resolved to the matching view member), and value is the per-row value.

Example

<CodeGroup>
yaml
cubes:
  - name: orders
    sql_table: orders

    dimensions:
      - name: status
        sql: status
        type: string
        links:
          # External link — opens a URL built from the row's value
          - name: search
            label: Search the web
            url: "CONCAT('https://www.google.com/search?q=order+', {CUBE}.status)"
            icon: brand-google
            target: blank

          # Drill-in link — opens another Cube Cloud dashboard, filtered by the row
          - name: details
            label: Open order details
            dashboard: orders-detail   # the target dashboard's slug
            params:
              - key: orders_view.status
                value: "{CUBE}.status"
javascript
cube(`orders`, {
  sql_table: `orders`,

  dimensions: {
    status: {
      sql: `status`,
      type: `string`,
      links: [
        {
          name: `search`,
          label: `Search the web`,
          url: `CONCAT('https://www.google.com/search?q=order+', ${CUBE}.status)`,
          icon: `brand-google`,
          target: `blank`
        },
        {
          name: `details`,
          label: `Open order details`,
          dashboard: `orders-detail`,
          params: [{ key: `orders_view.status`, value: `${CUBE}.status` }]
        }
      ]
    }
  }
})
</CodeGroup>

Icons

icon accepts any name from the Tabler icon set — the kebab-case name without any prefix, for example brand-google, external-link, layout-dashboard, or send. Browse and search the available names at tabler.io/icons. If icon is omitted, a default link icon is shown.

Setting a dashboard slug

A dashboard: link targets another dashboard by its slug — a short, stable, human-readable identifier (e.g. orders-detail). The slug is portable across environments: it is resolved within the current deployment, so the same model works in development and production without hardcoding dashboard IDs.

To set a slug in Cube Cloud, open the target dashboard, open its options sidebar, and fill the Slug field (see Dashboards → Dashboard slug). Slugs are unique per deployment, and the dashboard: value in your link must match the slug exactly.

There is no required order. You can write the dashboard: slug in the model first (it won't break anything — a link whose slug doesn't yet resolve is simply skipped in the cell menu) and set the dashboard's slug later, or set the dashboard slug first and reference it from the model afterwards. The link starts working as soon as both sides use the same slug.

Behavior

  • Per-row resolution — each link's url (and its params) is evaluated for every row, so the destination reflects the clicked cell. Internally a link compiles to a hidden synthetic dimension named <dimension>___link_<name>_url holding the resolved URL; it is added to the query automatically and is never shown as a column.
  • Null values — if the source value (and thus the resolved URL) is null for a row, that link is omitted from the menu for that row.
  • Where links appear — in Cube Cloud, links surface in the table cell menu on every results table (dashboard and workbook table charts, embedded dashboards, and Explore / SQL results). A left-click on a cell opens the menu listing the dimension's links, alongside Copy value and, on measures, Drill down.
  • Drill-in vs external — a dashboard: link navigates in-app, in the same tab (Cmd/Ctrl-click opens a new tab), ignoring target; a url: link opens per its target (blank by default).
  • Filters — for dashboard: links, each params entry becomes an equality filter on the target (the key is a view member, the value is the per-row value). An unknown or inaccessible target slug is skipped gracefully — the user is notified and no navigation happens.

See the links reference for the canonical parameter list.

Hierarchies

Dimensions can be organized into hierarchies to define drill-down paths (e.g., Country → State → City):

yaml
cubes:
  - name: users
    # ...

    dimensions:
      - name: country
        sql: country
        type: string

      - name: state
        sql: state
        type: string

      - name: city
        sql: city
        type: string

    hierarchies:
      - name: location
        levels:
          - country
          - state
          - city

Next steps