HOW-TO

Get a Google Sheet into Snowflake without a script.

By Chris Davidson, founder of yForest · Updated September 26, 2026

A manually-edited planning sheet and a Snowflake warehouse table don't have to stay out of sync. Map the columns once, pick Upsert, and let the schedule do the rest.

Start 14-day free trial Download on theMac App Store

No credit card. 14 days. Cancel in one click.

macOS 15+ · Apple Silicon native · 14-day free trial · No credit card

Quick answer: Add a Google Sheets connection and a Snowflake connection under Databases, then in Pipelines → Build, start a New Sync with the sheet tab as source and Snowflake as target. Map columns, pick Upsert on a unique column or composite key, and Dry Run before saving. Pipelines tier; keep QueryFlow open or a Mac awake since Sheets isn't on the background helper's closed-app list.

Before you start

A working Google Sheets connection (Google sign-in) and a working Snowflake connection, both added under Databases, and a Snowflake table to write into. Pipelines tier. Like Salesforce, Google Sheets isn't on the background helper's supported list for running with the app fully closed, so keep QueryFlow open or a Mac awake for a scheduled sync to fire.

Steps

  1. Open Pipelines → Build and click New Sync.
  2. On the left, pick your Google Sheets Connection, then the spreadsheet and sheet tab.
  3. On the right, pick your Snowflake Connection and target Table.
  4. Drag from a source column to a target field, or click AI Map.
  5. Pick a MODE: Insert, Update, or Upsert.
  6. For Update or Upsert, choose a MATCH ON column that's unique per row in the sheet.
  7. Click Dry Run, then Save it as a job or run it now.
QueryFlow Data Sync mapper with a Google Sheets source and Snowflake target
A sheet tab on the left, Snowflake on the right, one line per mapped column.

A worked example

A planning team keeps a manually-edited quarterly targets sheet that finance wants joined against actuals in Snowflake. The sheet has a header row (Region, Quarter, Target) and QueryFlow reads it as a table with those three columns. The Snowflake target, fully qualified so there's no ambiguity about which database and schema it lands in:

PLANNING.FINANCE.QUARTERLY_TARGETS

Map Region, Quarter, and Target to matching Snowflake columns, set MODE to Upsert, and MATCH ON to a composite of Region and Quarter, since that pair is unique per row and a target can be edited in the sheet without creating a duplicate row in Snowflake. Once someone edits a number in the sheet, the next scheduled run picks it up and updates the matching Snowflake row rather than appending a new one.

Check it worked

Dry Run first, since a header row that's shifted by an added column at the top of the sheet is the most common source of a silently wrong mapping. After a live run, open the Snowflake table and compare it against the sheet directly, paying attention to any column that mixes numbers and text in different rows, which Sheets tolerates and Snowflake's typed columns don't.

Troubleshooting

If you seeFix
Records synced, with errors listedUsually a cell that doesn't match the target column's type, for example text in a numeric Target column. Fix the cell in the sheet or widen the Snowflake column to VARCHAR.
Choose at least one key fieldUpdate and Upsert need a MATCH ON column; pick one whose values never repeat within the sheet, or add a helper column that concatenates two fields if no single column is unique.
Sheet not found or permission errorConfirm the Google account connected to QueryFlow actually has view access to the specific spreadsheet, not just a Google account in general.

When to keep a spreadsheet connector instead

Tools built specifically around live spreadsheet formulas, like Coefficient, or Fivetran's own Sheets connector, are the better fit when you need Sheets-side formulas referencing warehouse data in near real time, or when non-technical stakeholders need to trigger a refresh from inside the sheet itself with no separate app involved. QueryFlow's version treats the sheet purely as a data source feeding a warehouse table on a schedule you control from QueryFlow, not from the sheet; if the workflow genuinely needs to run the other direction, warehouse data flowing back into a live sheet formula, that's a different job this sync doesn't do.

Cost math against Fivetran's own pricing

Fivetran's Google Sheets connector, like its others, is metered by MAR: distinct rows touched by a change in a calendar month, counted once no matter how many times that row is edited again. A planning sheet with a few hundred rows that gets updated occasionally generates a trivial MAR count and would sit well inside Fivetran's own free-plan allowance of 500,000 MAR, per its pricing page. The economics here really turn on volume: a small manually-maintained sheet is cheap on either platform, but the moment you're running many such sheets or a much larger one, Fivetran's $5-per-connection base charge times the number of sheet connectors adds up, where QueryFlow's Pipelines tier at $29.99/month or $199.99/year covers every sync you build inside it at one flat price.

Type mapping and sheet quirks

Sheet cell contentSnowflake typeNote
Plain numberNUMBER or FLOATSheets stores all numbers as floating point internally; a NUMBER(p,s) column on the Snowflake side enforces the precision you actually want.
Date (formatted cell)DATEConfirm the sheet's date format matches what QueryFlow parses; ambiguous formats like 03/04/2026 read differently depending on locale.
TextVARCHARDirect mapping.
Checkbox (TRUE/FALSE)BOOLEANDirect mapping.
Blank cellNULLAn empty cell syncs as NULL, not as an empty string or zero.

The most common failure mode with a spreadsheet source isn't a type mismatch, it's a human one: someone inserts a column in the middle of the sheet, and every column to its right shifts one position over. QueryFlow's field mapping is drawn against column headers, not raw positions, so a header-based mapping survives an inserted column as long as the header text itself doesn't change; a mapping drawn against raw column letters instead would silently break.

A second worked variant: appending rather than upserting

Not every sheet-backed sync wants Upsert. A sheet used to log manually-entered survey responses, where each row is a new, distinct entry rather than an edit to an existing one, is a better fit for Insert mode instead: every row in the sheet at sync time gets appended to the Snowflake table, with no MATCH ON key required, and previously-synced rows are simply left alone rather than checked for changes. The tradeoff is that if someone edits a past row's value directly in the sheet by mistake, Insert mode won't reflect that correction in Snowflake; only a fresh row shows up. Pick Upsert when the sheet represents current state that gets edited in place, and Insert when the sheet represents a growing log of distinct entries.

Monitoring the sync over time

Because a manually-edited sheet has no schema enforcement at all, the most common failure after weeks of smooth running is someone typing a stray character into what was previously a clean numeric column. The Observatory's run history surfaces that as a per-row error on the next sync rather than a silent skip, which is worth checking the first time after any noticeable change to how the sheet is being edited day to day.

A note on sheet size limits

Google Sheets itself imposes a practical ceiling on how many cells a single spreadsheet can hold, and a very large sheet can also just be slow to read through the Sheets API compared to a purpose-built database. For a sheet that's grown into the tens of thousands of rows, it's worth asking whether the workflow has outgrown a spreadsheet as the source of truth entirely, in which case moving the underlying data into a small Postgres or MySQL table and syncing from there instead is often a better long-term fix than continuing to push a spreadsheet past what it's comfortable holding.

A note on retries

A run that fails partway, for example a Google API rate limit or an expired OAuth token, shows up in the run history with the underlying error, and re-authenticating the Google connection (if the token expired) followed by a retry re-reads the sheet from scratch rather than resuming a partial read.

Sources

Related syncs

See also: Integrations Sync BigQuery to Snowflake CSV to Snowflake on Mac.

Integrations Sync BigQuery to Snowflake CSV to Snowflake on Mac
QueryFlow Studio $9.99/mo · $99/yr
QueryFlow Pipelines $29.99/mo · $199.99/yr

Frequently asked

Does the sync see formula results or raw formulas?

It reads the calculated value a formula displays, not the formula text itself, the same way any API-based read of a Google Sheet works.

What happens if someone deletes a row in the sheet?

Upsert doesn't delete the matching Snowflake row when a sheet row disappears; it only inserts and updates. Handle deletions with a separate cleanup step if that matters for your table.

Can I sync from a specific named range instead of a whole tab?

Yes, point the source at a named range or a specific cell range within the tab rather than the full sheet.

Does this work with a shared drive spreadsheet, not just My Drive?

Yes, as long as the connected Google account has at least view access to the file, wherever it lives.

How often can this run?

As often as the schedule allows, down to a short interval; be mindful that very frequent polling of a manually-edited sheet rarely adds value over hourly or daily.

A sheet that keeps Snowflake current.

14-day free trial, no card. Map it once, let the edits flow in on a schedule.

Start 14-day free trial

No credit card. 14 days. Cancel in one click.