Skip to main content
This guide covers how to combine data from multiple datasets using JOIN operations. JOINs are essential for enriching your data with information from other sources.

Prerequisites

What you’ll learn

  • How to use INNER JOIN to match records across datasets
  • How to use LEFT JOIN for optional matches
  • How to chain multiple JOINs
  • How to use anti-joins to find unmatched records
  • How to join with Rosetta Stone for identity resolution
  • Performance considerations for large joins

INNER JOIN

An INNER JOIN returns only rows that have matching values in both datasets.

Basic syntax

Example: Enriching customer data

Combine customer information with their orders:
Only customers who have orders (and orders with valid customers) appear in the results. Customers without orders are excluded.

Using table aliases

Aliases make queries more readable and are required when referencing columns:

LEFT JOIN

A LEFT JOIN returns all rows from the left table, plus matching rows from the right table. Unmatched rows have NULL values for right-table columns.

Basic syntax

Example: Find customers with or without orders

This returns all customers. Those without orders show order_count = 0.

Filtering LEFT JOIN results

To find records without matches, filter for NULL:

Multiple JOINs

Chain JOINs to combine data from three or more datasets:

Mixing JOIN types

You can mix INNER and LEFT JOINs:
This returns all customers with orders, optionally including ratings if they exist.

Anti-joins with NOT EXISTS

Find records in one dataset that don’t have matches in another:

Basic anti-join

This returns customers who have never placed an order.

Example: Find inactive users

Find users who haven’t logged in recently:

Joining with Rosetta Stone

Rosetta Stone enables identity resolution across datasets using normalized identifiers.

Access Rosetta Stone identifiers

Join datasets via Rosetta Stone

Match records across datasets using resolved identities:

Query Rosetta Stone directly

Rosetta Stone queries can be scoped to different levels: global (narrative.rosetta_stone), company-specific (company_data._rosetta_stone), or dataset-specific (company_data."123"._rosetta_stone). See Rosetta Stone table references for complete syntax.

Join conditions

Multiple conditions

Join on multiple columns:

Complex conditions

Use expressions in join conditions:

NULL-safe joins

Use IS NOT DISTINCT FROM for NULL-safe equality:

Self-joins

Join a dataset to itself for comparisons:

Subqueries in JOINs

Join with aggregated or transformed data:

Performance considerations

Use fully qualified column names

Always qualify column names in joins to avoid ambiguity and improve query parsing:

Filter before joining

Apply filters as early as possible to reduce the data being joined:

Avoid OR in join conditions

OR conditions in joins can cause performance issues:
See Query Optimization for more performance tips.

Include budget controls

Always include budget controls when joining datasets in materialized views:

Common patterns

Enrich with latest record

Get the most recent related record for each row:

Aggregate before joining

Pre-aggregate to reduce join complexity:

Join Performance

Understanding performance implications

Query Optimization

Tips for faster queries

Complex Joins Cookbook

Advanced join patterns and examples

Rosetta Stone

Identity resolution for cross-dataset matching