info
Make ordering deterministic
If a query uses QUALIFY or windowed dedupe, enforce explicit tie-breakers (event time + stable offset + ingest time). BigQuery will happily execute an underspecified ORDER BY—until results drift.
Workload
Translate Snowflake-specific constructs—QUALIFY, VARIANT/FLATTEN, IFF/DECODE, time semantics, and MERGE/upsert patterns—into BigQuery Standard SQL with validation gates that catch semantic drift.
Quick answer
Translate Snowflake-specific constructs—QUALIFY, VARIANT/FLATTEN, IFF/DECODE, time semantics, and MERGE/upsert patterns—into BigQuery Standard SQL with validation gates that catch semantic drift.
Back to pair pageContext
Most Snowflake SQL estates contain implicit correctness rules—NULL handling, casting, timezone assumptions, and semi-structured extraction—that can compile in BigQuery yet drift in meaning. The goal is not just syntactic translation, but a provable parity contract for your highest-impact queries. Common symptoms after cutover:
Approach
We convert Snowflake SQL to BigQuery by combining deterministic rewrite rules with a review-and-validate loop for the constructs that commonly carry hidden business meaning. - Inventory & classify queries/views/models by consumer (BI, ELT, app), complexity, and risk patterns (QUALIFY, JSON, timezone, MERGE). - Rewrite with rule-anchored mappings function equivalents, identifier/quoting normalization, and explicit cast strategies. - Apply pattern libraries for QUALIFY/window filters, FLATTEN→UNNEST, VARIANT→JSON, and timezone normalization. - Validate & gate with compilation, catalog checks, and golden-query parity + regression tests for edge windows.
Coverage
Representative constructs we commonly handle for Snowflake → BigQuery SQL conversion (exact coverage depends on your estate).
| Source | Target | Notes |
|---|---|---|
| QUALIFY + windowed filters | QUALIFY (BigQuery) with deterministic ORDER BY | Tie-breakers enforced to avoid nondeterministic drift. |
| VARIANT access (col:path / GET / PARSE_JSON) | JSON_VALUE/JSON_QUERY + explicit casts | Extraction boundary defines types; ambiguous paths are review-marked. |
| FLATTEN + LATERAL joins | UNNEST patterns (INNER/LEFT as required) | Empty arrays preserved when needed (LEFT JOIN UNNEST). |
| LISTAGG / STRING aggregation | STRING_AGG with ORDER BY | Ordering preserved; distinct/NULL handling made explicit. |
| IFF / DECODE / NVL | IF / CASE / COALESCE | Explicit casts added where BigQuery coercion differs. |
| DATEADD/DATEDIFF/DATE_TRUNC | DATE_ADD/DATE_DIFF/DATE_TRUNC | Unit semantics normalized; timestamp vs date handled explicitly. |
Compare
| Topic | Snowflake | BigQuery | Notes |
|---|---|---|---|
| Dialect + functions | Snowflake SQL + rich VARIANT helpers | BigQuery Standard SQL + JSON functions | Function mapping is easy; type intent and casting rules are the hard part. |
| Semi-structured | VARIANT + FLATTEN patterns | JSON + UNNEST patterns | Choose scalar vs object extraction and preserve empty-array semantics intentionally. |
| Performance model | Micro-partitions + clustering effects | Partition pruning + clustering + slot usage | Converted queries often need pruning-aware rewrites to keep cost predictable. |
Examples
Examples show deterministic windowing, VARIANT/JSON extraction, and FLATTEN→UNNEST rewrites. Adjust paths and types to your schema.
-- Snowflake: QUALIFY + deterministic dedupe
SELECT *
FROM events
QUALIFY ROW_NUMBER() OVER (
PARTITION BY business_key
ORDER BY event_ts DESC, src_lsn DESC NULLS LAST, ingested_at DESC
) = 1; -- BigQuery: QUALIFY supported; enforce tie-breakers explicitly
SELECT *
FROM `proj.ds.events`
QUALIFY ROW_NUMBER() OVER (
PARTITION BY business_key
ORDER BY event_ts DESC, SAFE_CAST(src_lsn AS INT64) DESC, ingested_at DESC
) = 1; -- Snowflake VARIANT access
SELECT
payload:customer_id::STRING AS customer_id,
payload:amount::NUMBER(18,2) AS amount
FROM raw; -- BigQuery JSON extraction + explicit casts
SELECT
CAST(JSON_VALUE(payload, '$.customer_id') AS STRING) AS customer_id,
CAST(JSON_VALUE(payload, '$.amount') AS NUMERIC) AS amount
FROM `proj.ds.raw`; -- Snowflake FLATTEN
SELECT t.id, f.value:item_id::STRING AS item_id
FROM t, LATERAL FLATTEN(input => t.items) f; -- BigQuery UNNEST
SELECT t.id, CAST(JSON_VALUE(item, '$.item_id') AS STRING) AS item_id
FROM `proj.ds.t` t
CROSS JOIN UNNEST(JSON_QUERY_ARRAY(t.items)) AS item; info
If a query uses QUALIFY or windowed dedupe, enforce explicit tie-breakers (event time + stable offset + ingest time). BigQuery will happily execute an underspecified ORDER BY—until results drift.
info
Snowflake VARIANT access frequently relies on implicit casts. In BigQuery, choose JSON_VALUE vs JSON_QUERY intentionally and cast to the target scalar type at the extraction boundary.
Workload Assessment
We inventory your Snowflake SQL estate, convert a representative slice, and produce parity evidence on golden queries—plus a risk register for the constructs that carry business meaning.
Book AssessmentAvoid
Proof
We validate SQL conversion as a repeatable system: compile correctness, semantic parity for golden queries, and regression protection for edge cases (ties, late data windows, null-heavy cohorts). Execution checks - Compiles under BigQuery Standard SQL - Explicit casts + deterministic ordering enforced Parity checks - Golden-query output parity (agreed windows + parameters) - KPI aggregates by key dimensions; checksum aggregates for critical facts
Execution
A typical sequence that keeps correctness measurable and cutover controlled.
01
Collect BI extracts, view DDL, dbt/ELT models, and app-embedded SQL. Rank by business impact, frequency, and risk patterns (QUALIFY, VARIANT, timezones, MERGE).
02
Lock in semantic expectations: NULL rules, casting strategy, timezone contract, tie-breakers for window logic, and JSON typing. Select a ‘golden query’ set for sign-off.
03
Apply deterministic mappings for the common cases; flag ambiguous intent (implicit casts, JSON path uncertainty, collation expectations) with inline review markers.
04
Compile in BigQuery, run smoke tests, execute golden queries, and compare aggregates/checksums over agreed windows. Fail fast on drift and fix at the pattern level.
05
Add pruning-aware patterns (partition filters, join order, pre-aggregation) so converted queries don’t become unbounded scans. Confirm cost/latency with representative volumes.
FAQ
Often it compiles after basic rewrites, but semantic parity depends on explicit ordering, casts, timezone rules, and JSON typing. We treat those as contracts and validate them with golden queries.
Yes if we make ordering deterministic. When Snowflake queries rely on implicit ordering or ties, we add tie-breakers and validate with edge-case cohorts so ‘top-1’ selection never drifts.
We rewrite to JSON_VALUE/JSON_QUERY and UNNEST patterns, and we cast at extraction boundaries. We also preserve empty-array semantics intentionally (INNER vs LEFT UNNEST).
Yes. We treat pruning as part of the conversion: partition filters, join patterns, and pre-aggregation where needed. Validation includes cost/latency baselines for the top queries.
Migration Acceleration
Get a conversion plan, review markers, and validation artifacts (compile + parity + regression) so cutover is gated by evidence and rollback criteria.
Next reads
End-to-end approach: what breaks, validation gates, and cutover plan.
How we define parity contracts, thresholds, and evidence for sign-off.
Keep spend predictable and catch regressions early.