Code quality
The editor checks every statement as you type: syntax, unknown columns, aggregation mistakes, and the BigQuery cost traps a dry run won't tell you about. Problems get a squiggle, a hover explanation, and usually a one-keystroke fix.
SELECT * FROM `ace-analytics.warehouse.fact_mot_test` WHERE completed_date >= '2024-01-01' LIMIT 10
SELECT * from large table ‘ace-analytics.warehouse.fact_mot_test’ (506.7GB).
Consider selecting specific columns to reduce costs.
sql-best-practices(select-star-large-table)
View Problem (Alt+F8) · Quick Fix… (Ctrl+.)
Reading a squiggle
| Underline | Severity | Means |
|---|---|---|
| Red | Error | BigQuery will reject this |
| Orange | Warning | Runs, but costs more or does something you probably didn’t mean |
| Blue | Info | Worth knowing; no action required |
| None | Hint | Shown only in the quick-fix menu when the cursor is on the code |
Hover an underline for the message; every message ends with the rule id in parentheses. ⌥F8 (AltF8 on Windows and Linux) jumps to the next problem. Hint-level rules draw nothing; the lightbulb in the gutter is the only sign of them.
Some problems are found as you type; others come back from BigQuery’s dry run when you pause. Dry-run errors, like a missing required partition filter, also land in the status bar:
Fix with AI hands the error and the query to Prism AI; Explain asks it to explain the message. A syntax error shows the dry-run wording, Syntax error: Unexpected identifier "SELEC" at [1:1], with No quick fixes available.
Quick fixes
Put the cursor on the underlined code and press ⌘. (Ctrl. on Windows and Linux), or click the lightbulb. Enter applies the highlighted fix.
WITH tests AS ( SELECT make, test_result FROM `ace-analytics.warehouse.fact_mot_test` ), vehicles AS ( SELECT make, model FROM `ace-analytics.warehouse.dim_vehicle` ) SELECT make, test_result FROM tests JOIN vehicles USING (make)
The menu always ends with two entries: Disable ’…’ rule, which switches that rule off in settings (not offered for syntax errors or rules your organization locks), and Fix with Prism AI, which sends the problems and their suggested fixes to the agent.
Fixes you’ll meet most:
| Rule | Fix inserted |
|---|---|
| SELECT * on Large Table | Expand SELECT * to explicit columns |
| Missing Partition Filter / Missing Cluster Filter | Add column filter: a WHERE condition on the partition or cluster column |
| Missing LIMIT on Large Table | Add LIMIT 1000 (or |> LIMIT 1000 in pipe syntax) |
| Wildcard Table Scan Without Filter | Add _TABLE_SUFFIX filter: today only, last 7 days, last 30 days |
| Large Table Scan | Add TABLESAMPLE SYSTEM (1 PERCENT); see TABLESAMPLE |
| Undefined Column / Table Alias / CTE | Did you mean ’…’? (nearest name, STRUCT fields marked) |
| Ambiguous Column Reference | Qualify with ‘alias.column’ |
| Non-Aggregated Column | Add ‘column’ to GROUP BY · Use GROUP BY ALL |
| Aggregate in WHERE | Change WHERE to HAVING · Move aggregate condition to existing HAVING |
| NULL Comparison with Equals | Replace with IS NULL / IS DISTINCT FROM |
| COUNT(DISTINCT) Performance | Replace with APPROX_COUNT_DISTINCT |
| Cross Join Detected | Use explicit CROSS JOIN · Move condition to ON clause |
| Unused CTE | Remove unused CTE ‘name’ |
| Suggest QUALIFY / LIKE ANY / GROUP BY ALL / UNION BY NAME | Convert to QUALIFY clause · Replace with LIKE ANY · Use GROUP BY ALL · Use UNION BY NAME |
The same menu also lists refactorings that don’t need a warning: expand t.*, extract a subquery to a CTE, inline a CTE, convert CROSS JOIN to INNER JOIN, flip JOIN sides, CASE → COALESCE / NULLIF, convert pipe syntax to standard SQL. Keyword casing and backtick cleanup live on the editor toolbar instead.
Suggest QUALIFY
The subquery + WHERE rn = 1 idiom has a shorter form. Offered for ROW_NUMBER(), RANK() and DENSE_RANK():
SELECT * FROM ( SELECT vehicle_id, test_result, ROW_NUMBER() OVER (PARTITION BY vehicle_id ORDER BY completed_date DESC) AS rn FROM `ace-analytics.warehouse.fact_mot_test` ) WHERE rn = 1
SELECT vehicle_id, test_result FROM `ace-analytics.warehouse.fact_mot_test` QUALIFY ROW_NUMBER() OVER (PARTITION BY vehicle_id ORDER BY completed_date DESC) = 1
Suggest LIKE ANY
WHERE make LIKE 'FORD%' OR make LIKE 'VAUXHALL%' OR make LIKE 'MINI%' becomes WHERE make LIKE ANY ('FORD%', 'VAUXHALL%', 'MINI%'). NOT LIKE … AND NOT LIKE … becomes NOT LIKE ANY.
Tuning thresholds and switching rules off
Settings → Code Quality (also listed under Settings). Two sections: Cost Optimization holds the thresholds, Diagnostic Rules the on/off switch for every rule.
The size choices are 100 MB / 1 GB / 10 GB (10 GB / 100 GB / 1 TB for the scan threshold) and Never warn. Sizes are decimal (1 GB = 10⁹ bytes) and compare against the table’s stored size.
The Diagnostic Rules section lists the rules below with a switch each, Enable All / Disable All per group, and an Enable All Rules button with a N rules disabled badge. Severities are fixed. In an organization, an admin can lock rules on (they show an Org policy badge), raise a rule’s severity, and set thresholds; the stricter of yours and the org’s applies.
Rules by category
All rules are on by default. The groups match the Diagnostic Rules section in settings.
Performance
| Rule | Severity | Catches |
|---|---|---|
| SELECT * on Large Table | Warning | SELECT * on a table above the threshold |
| Missing Partition Filter | Warning | Partitioned table with no filter on the partition column |
| Missing Cluster Filter | Warning | Clustered table with no filter on a cluster column |
| Missing LIMIT on Large Table | Info | No LIMIT on a large table |
| ORDER BY Without LIMIT | Warning | Sorting a large table with no LIMIT, the “Resources exceeded” case |
| Wildcard Table Scan Without Filter | Warning | table_* with no _TABLE_SUFFIX filter |
| Large Table Scan | Hint | Table above the scan threshold, no filter, no sampling |
| Join with OR Condition | Warning | ON a = b OR c = d |
| Cross Join Detected | Info | Explicit or comma cross join |
| COUNT(DISTINCT) Performance | Hint | COUNT(DISTINCT x) where an approximation would do |
| Clustering Order Suboptimal | Hint | WHERE conditions out of clustering-key order |
Code Quality
| Rule | Severity | Catches |
|---|---|---|
| Unused CTE | Warning | A CTE nothing references |
| Duplicate CTE | Error | Two CTEs with one name |
| Duplicate Column Alias | Warning | The same alias twice in a SELECT list |
| Suggest QUALIFY Clause | Hint | Subquery wrapper around a window function |
| Suggest GROUP BY ALL | Hint | A GROUP BY that just repeats the non-aggregated columns |
| Suggest UNION BY NAME | Hint | UNION arms with the same columns in a different order |
| Suggest LIKE ANY | Hint | Chained LIKE … OR LIKE … on one column |
Correctness
| Rule | Severity | Catches |
|---|---|---|
| Non-Aggregated Column | Error | Column neither grouped nor aggregated |
| Aggregate in WHERE | Error | WHERE SUM(x) > 0 |
| Nested Aggregate | Error | SUM(COUNT(x)) |
| Non-Boolean WHERE | Error | WHERE user_id |
| NULL Comparison with Equals | Error | = NULL, <> NULL |
| Missing Join ON | Error | JOIN with no ON |
| Missing Join Condition | Error | JOIN whose condition doesn’t relate the tables |
| ORDER BY Not in SELECT | Error | With DISTINCT |
| ORDER BY Invalid Position | Error | ORDER BY 0 |
| ORDER BY Position Out of Range | Error | ORDER BY 5 with four columns |
| CASE Type Inconsistency | Warning | Branches of different types |
| Conflicting Column Alias | Warning | Alias that shadows a column |
| Multi-level aggregation | Error | The forms BigQuery rejects |
Semantic
| Rule | Severity | Catches |
|---|---|---|
| Undefined Column | Error | Column not in any table in scope |
| Undefined Table Alias | Error | x.col where x isn’t declared |
| Ambiguous Column Reference | Warning | Unqualified name present in two joined tables |
| Ambiguous Star Expansion | Warning | Bare * over a JOIN |
| Duplicate Column | Warning | Two result columns with one name |
| String in Numeric Comparison | Warning | odometer > '1000' |
| Invalid Hierarchical Name | Error | Not project.dataset.table |
Parser & Engine holds the syntax error itself and ON After Comma Cross-Join. The Charts (ggsql) and Graph (GQL) groups hold the rules described on the Charts and Graph queries pages.
Reusable column aliases
BigQuery can let a SELECT alias be read by later items and by the WHERE of the same query, but only when the project option reusable_column_aliases_mode is on. The editor asks BigQuery whether it is on for the project and location of the active tab, with a free dry run, and follows the answer. With the option on, SELECT x * 2 AS y, y + 1 is not flagged, completions and hover know the aliases, and the cases BigQuery refuses (a duplicate alias that is read, an alias in the WHERE of a grouped query) are errors. With it off, the read is an unrecognized name. See Reusable column aliases in BigQuery for how to turn the option on and what it allows.
What the checks need
- Schema loaded for the tables in FROM; column rules and size thresholds are silent otherwise
- dbt and Dataform files are checked on the resolved SQL, so
ref()and{{ … }}don’t count as syntax errors