Docs
Docs /SQL Editor /Code quality
SQL EditorDiagnostics

Code quality

Prism 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.

Query 1
1234
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

UnderlineSeverityMeans
RedErrorBigQuery will reject this
OrangeWarningRuns, but costs more or does something you probably didn’t mean
BlueInfoWorth knowing; no action required
NoneHintShown 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 (the suggest-* family, COUNT(DISTINCT), clustering order, large table scan) draw nothing; the lightbulb in the gutter is the only sign they fired.

Two sources feed the same underlines. Prism’s static analysis runs on every keystroke with no round-trip. The BigQuery dry run runs when you pause; its errors, like a missing required partition filter, also land in the status bar:

!Error Cannot query over table 'ace-analytics.warehouse.fact_mot_test' without a filter over column(s) 'completed_date' Fix with AIExplain ▷ Run Query

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.

Quick Fix
1234567891011
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 parse errors or rules your organization locks), and Fix with Prism AI, which sends the diagnostics and their suggested fixes to the agent.

Fixes you’ll meet most:

RuleFix inserted
SELECT * on Large TableExpand SELECT * to explicit columns
Missing Partition Filter / Missing Cluster FilterAdd column filter — a WHERE condition on the partition or cluster column
Missing LIMIT on Large TableAdd LIMIT 1000 (or |> LIMIT 1000 in pipe syntax)
Wildcard Table Scan Without FilterAdd _TABLE_SUFFIX filter — today only, last 7 days, last 30 days
Large Table ScanAdd TABLESAMPLE SYSTEM (1 PERCENT) — see TABLESAMPLE
Undefined Column / Table Alias / CTEDid you mean ’…’? — nearest name, STRUCT fields marked
Ambiguous Column ReferenceQualify with ‘alias.column’
Non-Aggregated ColumnAdd ‘column’ to GROUP BY · Use GROUP BY ALL
Aggregate in WHEREChange WHERE to HAVING · Move aggregate condition to existing HAVING
NULL Comparison with EqualsReplace with IS NULL / IS DISTINCT FROM
COUNT(DISTINCT) PerformanceReplace with APPROX_COUNT_DISTINCT
Cross Join DetectedUse explicit CROSS JOIN · Move condition to ON clause
Unused CTERemove unused CTE ‘name’
Suggest QUALIFY / LIKE ANY / GROUP BY ALL / UNION BY NAMEConvert 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 diagnostic: 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. The rule fires for ROW_NUMBER(), RANK() and DENSE_RANK():

123456
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
123
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

1
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.

Cost OptimizationControl warnings for expensive query patterns and missing filters
Query Pattern Warnings
Warn about SELECT * on tables larger than 1 GB
Show warnings when using SELECT * on large tables that could scan excessive data.
Warn about missing LIMIT on tables larger than 1 GB
Show warnings when queries lack LIMIT clause on large tables.
Warn about scanning tables larger than 100 GB
Show warnings when querying very large tables without filters.
Partition & Clustering
Warn about missing partition filtersAlert when querying partitioned tables without filtering the partition column (critical for BigQuery cost optimization).
Warn about missing cluster filtersAlert when querying clustered tables without filtering cluster columns (improves query performance).

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, so a filtered query on a big table still counts. Missing-LIMIT only fires on unclustered tables; CROSS JOIN is not reported for CROSS JOIN UNNEST(...).

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.

Every rule, by category

All rules are on by default. The id in brackets is what the hover shows.

Performance

RuleSeverityCatches
SELECT * on Large Table select-star-large-tableWarningSELECT * on a table above the threshold
Missing Partition Filter missing-partition-filterWarningPartitioned table with no filter on the partition column
Missing Cluster Filter missing-cluster-filterWarningClustered table with no filter on a cluster column
Missing LIMIT on Large Table missing-limit-large-tableInfoNo LIMIT on a large unclustered table
ORDER BY Without LIMIT order-by-without-limitWarningSorting a large table with no LIMIT, the “Resources exceeded” case
Wildcard Table Scan Without Filter wildcard-table-scan-without-filterWarningtable_* with no _TABLE_SUFFIX filter
Large Table Scan large-table-scanHintTable above the scan threshold, no filter, no sampling
Join with OR Condition join-with-or-conditionWarningON a = b OR c = d
Cross Join Detected cross-join-detectedInfoExplicit or comma cross join
COUNT(DISTINCT) Performance count-distinct-performanceHintCOUNT(DISTINCT x) where an approximation would do
Clustering Order Suboptimal clustering-order-suboptimalHintWHERE conditions out of clustering-key order

Code Quality

RuleSeverityCatches
Unused CTE unused-cteWarningA CTE nothing references
Duplicate CTE duplicate-cteErrorTwo CTEs with one name
Duplicate Column Alias duplicate-column-aliasWarningThe same alias twice in a SELECT list
Suggest QUALIFY Clause suggest-qualifyHintSubquery wrapper around a window function
Suggest GROUP BY ALL suggest-group-by-allHintA GROUP BY that just repeats the non-aggregated columns
Suggest UNION BY NAME suggest-union-by-nameHintUNION arms with the same columns in a different order
Suggest LIKE ANY suggest-like-anyHintChained LIKE … OR LIKE … on one column
DRAW line Without ORDER BY ggsql-line-without-order-byInfoLine chart with no explicit ordering
ggsql Geom Not Yet Rendered ggsql-geom-phase-not-supportedInfohistogram, smooth, boxplot, density — parsed, not drawn
Unknown LABEL Key ggsql-label-key-not-renderedInfoLABEL key other than title, subtitle, caption

Correctness

RuleSeverityCatches
Non-Aggregated Column non-aggregated-columnErrorColumn neither grouped nor aggregated
Aggregate in WHERE aggregate-in-whereErrorWHERE SUM(x) > 0
Nested Aggregate nested-aggregateErrorSUM(COUNT(x))
HAVING Without GROUP BY having-without-group-byWarning
SELECT * with GROUP BY select-star-with-group-byWarning
Window Function in WHERE window-function-in-whereError
QUALIFY Without Window Function qualify-without-window-functionError
Non-Boolean WHERE non-boolean-whereErrorWHERE user_id
NULL Comparison with Equals null-comparison-with-equalsWarning= NULL, <> NULL
Missing Join ON missing-join-onErrorJOIN with no ON
Missing Join Condition missing-join-conditionErrorJOIN whose condition doesn’t relate the tables
ORDER BY Not in SELECT order-by-not-in-selectErrorWith DISTINCT
ORDER BY Invalid Position order-by-invalid-positionErrorORDER BY 0
ORDER BY Position Out of Range order-by-position-out-of-rangeErrorORDER BY 5 with four columns
CASE Type Inconsistency case-type-inconsistencyWarningBranches of different types
Conflicting Column Alias conflicting-column-aliasWarningAlias that shadows a column
Multi-level aggregation family, 8 rules multi-level-aggregation-*ErrorThe forms BigQuery rejects: too deep, empty arguments, GROUPING(), HAVING MAX/MIN, COLLATE keys, ORDER BY / LIMIT / IGNORE NULLS modifiers, PIVOT, privacy clauses
DRAW Without VISUALIZE ggsql-draw-without-visualizeError
Unknown Geom Type ggsql-unknown-geomError
Unknown Aesthetic ggsql-unknown-aestheticWarning
Unresolved Column in VISUALIZE ggsql-unresolved-mapping-columnError

Semantic

RuleSeverityCatches
Undefined Column undefined-columnErrorColumn not in any table in scope
Undefined Table Alias undefined-table-aliasErrorx.col where x isn’t declared
Undefined CTE undefined-cteError
Column Without FROM column-without-fromError
Ambiguous Column Reference ambiguous-column-referenceWarningUnqualified name present in two joined tables
Ambiguous Star Expansion ambiguous-star-expansionWarningBare * over a JOIN
Duplicate Column duplicate-columnWarningTwo result columns with one name
Duplicate Table Alias duplicate-table-aliasError
USING Column Not Found using-column-not-foundError
Scalar Subquery Multiple Columns scalar-subquery-multiple-columnsError
IN Subquery Multiple Columns in-subquery-multiple-columnsError
String in Numeric Comparison string-in-numeric-comparisonWarningodometer > '1000'
Invalid Hierarchical Name invalid-hierarchical-nameErrorNot project.dataset.table

Parser

All errors: Parse Error parse-error, ON After Comma Cross-Join comma-join-unnest-on-ambiguous, and Struct Path In Graph KEY Columns property-graph-key-struct-path.

What the checks need

  • Schema loaded for the tables in FROM; column rules and size thresholds are silent otherwise
  • Table sizes come from the schema tree; a table you’ve never expanded has none until the dry run fills it in
  • dbt and Dataform files are checked after Jinja / SQLX preprocessing, so ref() and {{ … }} don’t count as syntax errors