Looker Guide

A practical guide to Looker development

View the Project on GitHub ashrithssreddy/looker-guide

📄 Views in Looker

1. What is a View?

In Looker, a View is like a blueprint for a table in your database.

Think of it as a wrapper around a database table or SQL result, where you define:

🧠 How it connects to tables

🔁 Where it fits in Looker’s architecture

Model → Explore → View

📌 TL;DR:
A View defines how Looker should understand and work with a single table (or derived result), including what to expose to business users.

2. View File Boilerplate

Every LookML view file starts with a view: declaration. It’s used to define fields like dimensions and measures based on an underlying table.

🧱 Basic Boilerplate

view: users {
  sql_table_name: analytics.users ;;
  dimension: user_id {
    type: number
    primary_key: yes
    sql: ${TABLE}.user_id ;;
  }
  measure: total_users {
    type: count
  }
}

✅ Required Fields

🧩 Optional Metadata

3. Dimensions

A dimension in Looker represents a raw column or a derived field — used for grouping, filtering, or displaying raw values in reports.

🧠 Concept

🛠️ Common type: Values

🧾 Example

dimension: product_name {
  type: string
  sql: ${TABLE}.product_name ;;
}

⏳ dimension_group (for time fields)

Used to auto-generate multiple time resolutions (e.g., day, week, month).

dimension_group: signup_date {
  type: time
  timeframes: [raw, date, week, month, year]
  sql: ${TABLE}.signup_ts ;;
}

📌 Notes

4. Measures

A measure is an aggregated field — used to compute metrics like totals, averages, counts. Measures are typically the numbers business users care about.

🧠 Concept

⚙️ Common type: Values

🧾 Simple Example

measure: total_sales {
  type: sum
  sql: ${TABLE}.sales_amount ;;
}

🧪 Custom SQL-based Measure

Use type: number when you’re writing full SQL logic yourself.

measure: new_vs_repeat_ratio {
  type: number
  sql: CASE WHEN ${is_new} THEN 1 ELSE 0 END * 1.0 / COUNT(*) ;;
}

📌 Notes

5. Time Handling in Views

Time-based fields in Looker are handled using dimension_group, which auto-generates multiple granularities from a single timestamp.

⏳ dimension_group with timeframes

Use this to expose fields like signup_date, signup_week, signup_year — all from one column.

dimension_group: signup_date {
  type: time
  timeframes: [raw, date, week, month, quarter, year]
  sql: ${TABLE}.signup_ts ;;
}

🌐 Timezone Handling with convert_tz

If your DB stores UTC but you want reports in a local timezone:

sql: CONVERT_TZ(${TABLE}.created_at, 'UTC', 'America/Los_Angeles') ;;

🔍 Using Time Filters

Once a dimension_group is defined, Looker automatically enables time filters like:

These filters are accessible from the Explore UI without extra config.

📌 Notes

6. Persistent Derived Tables (PDTs)

A Persistent Derived Table (PDT) is a temporary table that Looker creates in your warehouse based on a SQL query. It’s used when your logic is too complex to model directly off a raw table.

🧠 Why Use PDTs?

⚙️ How to Define a PDT

PDTs are defined inside a view using the derived_table block:

view: product_rollup {
  derived_table: {
    sql: SELECT product_id, SUM(sales) AS total_sales FROM orders GROUP BY 1 ;;
  }
  persist_for: "24 hours"
}

Alternatively, you can use:

🔁 Workflow

  1. Define SQL logic in a derived_table
  2. Add persist_for: or datagroup_trigger: for refresh policy
  3. Use the PDT view in your Explore just like any other view

📌 Notes

7. Real-World Examples

Here are two example view files to demonstrate how dimensions, measures, and time handling come together in real projects.

📦 orders.view.lkml

view: orders {
  sql_table_name: analytics.orders ;;

  dimension: order_id {
    type: number
    primary_key: yes
    sql: ${TABLE}.order_id ;;
  }

  dimension: customer_id {
    type: number
    sql: ${TABLE}.customer_id ;;
  }

  dimension_group: order_date {
    type: time
    timeframes: [date, week, month, year]
    sql: ${TABLE}.order_ts ;;
  }

  measure: total_revenue {
    type: sum
    sql: ${TABLE}.order_amount ;;
  }
}

👤 users.view.lkml

view: users {
  sql_table_name: analytics.users ;;

  dimension: user_id {
    type: number
    primary_key: yes
    sql: ${TABLE}.user_id ;;
  }

  dimension: email {
    type: string
    sql: ${TABLE}.email_address ;;
  }

  dimension_group: signup_date {
    type: time
    timeframes: [date, month, year]
    sql: ${TABLE}.signup_ts ;;
  }

  measure: total_users {
    type: count
  }
}