HOW-TO · DATA SYNC

Get a Google Sheet into MySQL on a schedule.

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

A vendor list, a pricing sheet, or a manually curated mapping table often lives in Google Sheets because that's where the person maintaining it is comfortable. Getting it into MySQL for an app to actually use it is a mapped sync, not a migration project.

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 Google Sheets and MySQL as connections, open Pipelines, Build, New Sync, and map the sheet's columns to a MySQL table. Use Upsert with a real unique key if the sheet gets edited in place, then save it as a scheduled job. Pipelines tier.

Why the sheet stays the sheet

The person who maintains a vendor list or pricing table often isn't a database user, and asking them to work in MySQL directly instead of a spreadsheet they already understand is a fight not worth having. The practical answer is letting the sheet stay the interface for the person maintaining it, and syncing it into MySQL for the systems that need to consume it.

Before you start

Steps

  1. Add a Google Sheets connection under Databases, pointed at the sheet, and confirm your MySQL connection works.
  2. Open Pipelines → Build and click New Sync.
  3. On the left, pick the Google Sheets connection and the sheet or range as the source.
  4. On the right, pick the MySQL connection and the target table.
  5. Drag fields across, or click AI Map, then pick a MODE.
  6. Click Dry Run to preview, then Save it as a scheduled job.

A worked example

A procurement team keeps a vendor sheet with columns vendor_id, vendor_name, category, and approved, edited by hand as vendors are added or their approval status changes:

Target table: procurement.vendors
Columns: vendor_id, vendor_name, category, approved

Set MODE to Upsert and MATCH ON to vendor_id, a stable identifier assigned once and never edited. An approval status flip updates the existing row in MySQL instead of creating a second entry for the same vendor. Schedule it nightly, after the team's typical end-of-day updates.

Why the key column matters more than it looks like it should

A sheet-maintained list is exactly where a name gets used as an identifier because it feels obvious, until a vendor is renamed and the old name's row becomes an orphan while a new row appears under the new name. Pick a column that's assigned once and never edited, even if it means adding an ID column to the sheet that didn't exist before, purely to give Upsert something stable to match on.

What this doesn't do

Upsert adds and updates; it doesn't remove a MySQL row just because it disappeared from the sheet. If a vendor gets deleted from the list entirely, that requires a separate cleanup step, since the sync has no way to distinguish "vendor removed" from "vendor just wasn't in this run's range" on its own.

Extending the pattern to other sheets

Once one sheet-to-MySQL sync works, the same shape, a stable key column, Upsert for anything hand-edited, a schedule matched to how often the sheet actually changes, applies to any other manually maintained list worth getting into the database: a discount code table, a regional tax rate sheet, an approved-domains list.

A note on the procurement team's workflow

The team maintaining the sheet doesn't need to know or care that a sync exists at all, and that's the point. They keep working in the interface they're used to, adding a vendor row or flipping an approval flag, and the MySQL table simply reflects that on whatever schedule you've set. The only thing worth telling them is not to delete or rename the header row, since that's the one change that can break the mapping without any other symptom.

Handling a vendor that gets un-approved and re-approved later

Because Upsert on a stable ID just overwrites the current field values, a vendor's approved flag flipping back and forth over time doesn't need any special handling, the MySQL row simply reflects whatever the sheet currently says as of the last run. If a downstream system needs the history of approval changes rather than just the current state, that requires a separate append-only log table fed by a different sync, not this one.

A gotcha with category values that drift over time

A free-text category column in a sheet tends to accumulate near-duplicate values over time, "Office Supplies" alongside "office supplies" and "Office Supply" entered by different people. MySQL's default collation on many setups treats those as equal for matching purposes but a downstream report grouping by the raw string won't, so it's worth periodically checking the distinct values in that column rather than assuming the sheet stays internally consistent on its own.

Extending this to a two-way need, carefully

If procurement eventually wants MySQL-side changes, a system automatically flagging a vendor as inactive, reflected back in the sheet, that's a materially different and harder problem than this one-way sync, since it means writing to a document humans are actively editing and risking a collision with a concurrent manual edit. Treat that as a separate project with its own conflict-handling design, not an extension of this sync.

Setting an expectation with procurement about timing

A nightly sync means a vendor added to the sheet at 4 PM won't show up in the system that consumes the MySQL table until the next morning's run. For most procurement workflows that's a non-issue, but it's worth stating it plainly to the team maintaining the sheet, since the alternative, someone adding a vendor and immediately expecting it usable elsewhere within minutes, leads to a confused support ticket rather than a schedule adjustment.

A note on multiple people editing the same sheet

Google Sheets handles concurrent edits from multiple people gracefully at the spreadsheet level, but if two people add what's meant to be the same vendor under two different rows before either notices, the sync will happily create two MySQL rows for what should be one vendor, since nothing about Upsert catches a duplicate that wasn't flagged as a duplicate in the source. A periodic manual review of the vendor list for near-duplicate names catches this in a way the sync itself structurally can't.

What this costs against Fivetran

Fivetran's pricing page (September 26, 2026) meters Google Sheets the same way as any standard connector: 500,000 MAR free, then a $5 base charge per connection between 1 and 1,000,000 MAR, usage above that behind a quote. A vendor list in the hundreds of rows sits well inside the free tier regardless, which makes the real comparison setup effort and where the sync runs, not row-based pricing.

Check it worked

Run Dry Run and check the preview against the sheet. After a real run, check the job's history for rows synced with no errors.

Troubleshooting

If you seeFix
No MySQL connections availableAdd a MySQL connection under Databases first.
Duplicate key values in sourceThe MATCH ON column isn't actually unique; switch to a stable ID column.
A blank row in the sheet breaks the runTighten the source range to exclude stray blank rows past your data.

Sources

Sync Google Sheets to BigQuery MySQL Mac client Every warehouse, one client Integrations Load a CSV into MySQL. Sync PostgreSQL to MySQL.
QueryFlow Studio $9.99/mo · $99/yr
QueryFlow Pipelines $29.99/mo · $199.99/yr

Frequently asked

Do I need Apps Script for this?

No. Data Sync reads the sheet directly; there's no script to write or maintain inside the spreadsheet itself.

What happens if a vendor code gets renamed in the sheet?

If MATCH ON is set to the vendor code and it changes, the sync treats it as a new row rather than an update to the old one, leaving a stale row behind. Use a stable ID column as the key, not a value someone might edit.

How does Upsert write into MySQL?

An INSERT with ON DUPLICATE KEY UPDATE against the MATCH ON column you set.

Which tier includes this?

Pipelines. Studio covers querying both Google Sheets and MySQL, not building a sync between them.

Let the sheet stay the sheet.

14-day free trial, no card. Map it once, sync it nightly.

Start 14-day free trial

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