Lens

Join Tables

A join table combines two or more existing Lens tables into one, matching rows on shared dimensions. Use it when the metrics you need live in separate tables and you want them side by side.

Overview

Data rarely arrives in one place. GA4 sessions live in one table, ad spend in another, and an offline conversion export in a third. Each is useful alone, but the interesting questions, like cost per session by channel, need all three lined up together. A join table does that lining up.

A join table has no external source of its own. It starts from a base table and joins additional tables onto it, matching rows on join keys, which are the dimensions the tables have in common. The output is a single wider table where each row carries the columns from every joined source. Boards and reports then query that one table instead of stitching numbers together by hand.

A join adds columns. If you instead need to stack rows from similar tables, that is a Union, and if you need to reshape a single table, that is a Pivot. Reach for a join specifically when different tables hold different measures for the same entities.

Prepare the tables to join

Because a join reads from other Lens tables, every table you want to combine must already exist and have synced successfully. A join cannot pull data that its sources have not loaded yet.

Just as important, the tables must share at least one dimension you can match on. Date is the most common join key, often paired with a second dimension like source or campaign. Before you build the join, confirm those shared columns exist in every table and are named and formatted consistently, because the join matches on exact values.

Create the join table

Open Lens > Tables from the left sidebar and click Add Table. Choose Join as the source type. The setup page switches to the join layout, and the breadcrumb reads "New Joined Table".

Give the table a name that describes the combined result, such as "Sessions and Spend by Channel". This is the table boards will reference, so a clear name saves confusion later.

Pick the base table

Choose the base table. The base is the starting set of rows for the join, and every joined table adds its columns onto those rows. The base determines which rows can appear in the output, so pick the table whose row coverage you want to anchor on.

As a rule of thumb, make the most complete table the base. If GA4 sessions cover every day but ad spend only covers days a campaign ran, use sessions as the base so you keep every day and simply see blank spend on the quiet days.

Add joined tables and keys

Add one or more tables to join onto the base. For each, choose the join keys, the shared dimensions used to match rows. A join on date alone matches one row per day. A join on date plus source matches one row per day per source, which is usually what channel-level analysis needs.

Pick keys that exist in every table being joined and that hold the same values in each. A mismatch, such as one table storing google and another storing Google, will cause rows to fail to match even though they look related. Consistency on the upstream side is what makes a join behave.

A join brings in the dimensions from every joined table, not just their metrics, so any extra grouping columns a joined table carries become available on the combined table. When two tables share a dimension, the base table's value is the one that is kept. Because a join keeps every row from the base and attaches matching data from the others, rows that exist only in a joined table, with no match in the base, are not included; anchor the join on the table whose row coverage you want.

Check your join keys are unique

The keys you join on must identify at most one row per day on both sides: in the base table and in every table you join onto it. This is the single most common reason a join produces numbers that look believable but are wrong.

If several rows within the same day share the same key values, Lens stops the build and shows an error naming the table, the worst number of rows sharing a key, and an example of the offending key. It refuses rather than building, because joining on a repeated key matches every row against every other row with that key, which multiplies the metrics instead of matching them. A real case on a client board produced a 50 percent overstatement that was invisible in the UI, because the row count still looked normal.

Date and Date + Hour are not join keys

Google Analytics Date and Date + Hour are time grains, not stored dimension values. They control how rows are bucketed into days and hours, but they are never saved as dimensions, so joining on them matches everything against everything. Use a real dimension such as Device Category or Channel Group.

Save and build

Click Save, then build the join. Lens runs the join query and stores the combined result as its own ClickHouse table, so boards query the join directly with no extra work at view time.

A join is a snapshot of its sources at build time. Whenever any source table re-syncs with new data, rebuild the join so it reflects the change. The table view flags when a rebuild is due.

Tip

Joins only emit rows where the keys match. If you want to keep every row from the base even when a joined table has no match, make sure the base table is the one with the fullest coverage. The base anchors the output.

Watch out

A join table is only as fresh as its sources. If any source syncs on a schedule, the join drifts behind until you rebuild it. For dashboards that must stay current, rebuild the join after its sources refresh.

Troubleshooting

Build fails: join keys do not uniquely identify a row

The keys you chose repeat within a single day on at least one side of the join. The error names the table and shows an example key. You have two ways forward. Add another key so each row becomes unique, choosing a dimension that is actually stored rather than a time grain. Or roll the sources up to the key grain first with a Pivot table, then join the pivots, which guarantees one row per key.

The join shows fewer rows than expected

A join only emits rows where the join key matches across the tables. If one source has dates or values the other does not, those rows drop out. Open each source table and compare the coverage of the join-key columns. The fix is almost always on the upstream side, by widening a source's date range or aligning its key values, rather than in the join itself.

The join reports a source table is missing

One of the joined tables was deleted or moved. The join view shows a notice naming the missing table. Open the join configuration, point it at a valid table, and rebuild. Lens normally blocks deleting a table that a join depends on, so this usually means the dependency chain was broken in an unusual way.

A joined column comes back empty

The rows matched on the join key, but the joined table had no value to contribute, or its key values did not line up. Check for formatting differences between the key columns, such as casing or leading spaces. Also confirm the joined table actually synced data for the dates in question.