SQL notebooks
A tab made of cells instead of one editor. Each SQL cell runs on its own and its result becomes a table the cells below can query by name, so a long analysis becomes a chain of small, checkable steps instead of a 200-line WITH.
SELECT make, COUNT(*) AS vehicle_count FROM `ace-analytics.warehouse.vehicles` WHERE first_registered >= '2020-01-01' GROUP BY make
-- Aggregate by manufacturer using results from the vehicle_data cell SELECT make, SUM(vehicle_count) AS total_vehicles FROM vehicle_data GROUP BY make ORDER BY total_vehicles DESC
| make⏷ | total_vehicles⏷ | |
|---|---|---|
| 1 | FORD | 4252980 |
| 2 | VAUXHALL | 2130011 |
| 3 | HONDA | 616242 |
| 4 | MERCEDES-BENZ | 305002 |
| 5 | RENAULT | 248352 |
Creating one
- ⌘ ⇧ E, or the caret next to New query in the sidebar → New notebook.
- ⌘ K → New SQL notebook.
- From a query tab: the editor’s ⋯ menu → Convert to notebook. A multi-statement script becomes one cell per statement.
A new notebook has one empty SQL cell called query1.
Kinds of cells
The toolbar has Run All and three add buttons: + SQL, + Text, + Chart. The same buttons appear when you hover below the last cell.
| Kind | What it is |
|---|---|
| SQL | An editor with the same completions, diagnostics and cost footer as a query tab, and a results grid underneath |
| Text | Markdown; click to edit, click away to render |
| Chart | A bar, line, pie, scatter or heatmap over another cell’s result: pick Source Cell, Type, X Axis, Y Axis and an optional Group By |
Every SQL and chart cell has a name, shown in its header. Click it to change it. Names start with a letter and contain only letters, digits and underscores; duplicates are refused. Cells are auto-named query1, chart1, note1, …
Cell header buttons: run, collapse, focus (opens the cell full-width; close it with the button at the top), move up, move down, a ⋯ menu with Copy to clipboard and Duplicate, and delete (with Undo). Cells can also be dragged by their handle to reorder.
Running cells
| Action | How |
|---|---|
| Run this cell | The ▶ in the cell header, or ⌘Enter in its editor |
| Run this cell and everything that depends on it | Shift-click ▶, or ⌘⇧Enter |
| Run every SQL cell, top to bottom | Run All; stops at the first cell that fails |
Each cell runs as its own BigQuery job, with its own dry-run and its own byte limit. If any cell is over its limit, Run All asks before starting.
Referencing other cells
Write the upstream cell’s name where a table would go:
SELECT d.make, d.model, COUNT(*) AS defects FROM make_summary AS m JOIN `ace-analytics.warehouse.defects` AS d ON d.make = m.make GROUP BY d.make, d.model
A later cell reads the upstream cell’s stored result; nothing is re-run.
The upstream cell has run in this session, or its result is still available from a previous one (results are kept about 24 hours)
The upstream cell is a SELECT. CREATE, INSERT, MERGE, DECLARE and other statements don’t produce a result table
- The reference is directly after FROM or JOIN
If you reference a cell that hasn’t run, the feedback bar says so before you waste a job: Cell “make_summary” has not been executed yet. Run it first. Upstream cells are never run for you.
Completions know cell names: after FROM, cells are offered like tables, and once a cell has run its result columns are offered too.
Stale cells
Edit, re-run, rename, move or delete a cell and every cell that reads from it gets a Stale marker in its header, with the reason on hover. Its old results stay visible; click the marker to re-run.
Variables
Three kinds, all flowing downward from the cell that defines them.
Typed parameters are the same -- querylab:param directive lines query tabs use (query parameters). A cell that contains only declarations is skipped when run; it exists only to be read.
-- querylab:param start_date DATE = '2025-09-01' -- querylab:param min_tests INT64 = 100
SELECT make, COUNT(*) AS tests FROM `ace-analytics.warehouse.mot_tests` WHERE completed_date >= @start_date GROUP BY make HAVING tests >= @min_tests
Each run and estimate of a cell sends the parameters it reads as typed BigQuery query parameters. A cell lower in the notebook can redeclare a name to change the value for the cells below it; the same name declared twice in one cell is an error.
DECLARE variables are ordinary BigQuery scripting variables. A cell that uses one without declaring it gets the declaration prepended at run time:
DECLARE start_date DATE DEFAULT '2025-09-01'; DECLARE min_tests INT64 DEFAULT 100;
SELECT make, COUNT(*) AS tests FROM `ace-analytics.warehouse.mot_tests` WHERE completed_date >= start_date GROUP BY make HAVING tests >= min_tests
Template variables are plain assignments in a cell of their own. A cell that contains nothing else is skipped when run; it exists only to be read.
table_name = 'mot_tests'; result = 'FAILED';
SELECT make, COUNT(*) AS failed FROM `ace-analytics.warehouse.{{ table_name }}` WHERE test_result = @result GROUP BY make
{{ name }} is a text substitution, so it works for identifiers: tables, columns, project IDs. @name from an assignment is a query parameter that is always STRING; the editor offers Convert to typed parameter on an assignment used that way, which turns it into a -- querylab:param line. A typed STRING parameter feeds {{ name }} too. A missing @name fails before the job is sent, naming the parameter to declare.
Cost and billing per cell
The tune icon in the toolbar opens Notebook Configuration; the one in a cell header opens Cell Configuration. Both have the same fields:
TABLESAMPLE appears as a comment in the cell’s SQL; edit it there or in the settings. Setting it at notebook level applies it to every cell that hasn’t set its own. See TABLESAMPLE.
Getting the SQL out
The toolbar’s ⋮ menu:
- Copy SQL as CTEs gives one statement: every SELECT cell but the last becomes
name AS (…)in aWITH, the last is the body. DECLARE and DDL cells go in front as-is. - Copy SQL as temp tables gives a script:
CREATE TEMP TABLE name AS (…);per cell, the last SELECT unchanged,DROP TABLE IF EXISTSlines at the end. - Copy to clipboard copies the cells as JSON. Paste into any notebook (⌘V with the notebook focused) to append them; a single cell’s ⋯ → Copy to clipboard does the same for one cell.
Both SQL exports show a toast with Open in new tab, which drops the generated SQL into a fresh query tab. Template-variable cells are left out; the variables aren’t substituted, so fill them in before running.
A cell’s results grid has the usual export options; the BigQuery and Cloud Storage ones add a new SQL cell with the CREATE TABLE or EXPORT DATA statement instead of opening a tab.
Share Notebook creates an encrypted link to the whole notebook: cells, text, chart configs. Billing projects and destination tables are stripped. See Sharing.