HOW-TO · DATA SYNC

Sync tables from Redshift into Snowflake.

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

A full cutover is a project. Mirroring a few tables while you evaluate Snowflake is a sync.

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 Redshift and Snowflake connections, then build a Data Sync: pick Redshift as the source, write the query for the table you're moving, and map it to a Snowflake table. Run Insert for the first load, then Upsert to keep both systems current. Pipelines tier.

Why teams move off Redshift, in stages

A full Redshift-to-Snowflake migration is a project, not a sync, and this page isn't that. What comes up more often is smaller: a team is evaluating Snowflake, or has already committed to it, and needs specific tables mirrored over while the two systems run side by side for a while. That's a sync problem with a migration flavor, not a one-time cutover.

Before you start

Steps

  1. Add your Redshift connection under Databases if you haven't already.
  2. Add your Snowflake connection: account identifier, warehouse, and a Programmatic Access Token.
  3. Open Pipelines → Build and click New Sync.
  4. On the left, pick Redshift as your source and write the query for the table or rows you're moving.
  5. On the right, pick your Snowflake connection and the target table, or create one.
  6. Drag columns across, or click AI Map to pair them by name.
  7. Pick a MODE: Insert for a first load, Update or Upsert for an ongoing refresh.
  8. For Update or Upsert, set a MATCH ON field, a unique key column.
  9. Click Dry Run, check the preview, then Save or run it now.

A worked example

Moving a customers dimension table over first, since dashboards elsewhere will need it as soon as fact tables start landing in Snowflake too:

-- Redshift source
SELECT customer_id, company_name, region, signed_up_at
FROM analytics.customers;
-- Snowflake target
CREATE TABLE IF NOT EXISTS ANALYTICS.PUBLIC.CUSTOMERS (
  CUSTOMER_ID NUMBER,
  COMPANY_NAME VARCHAR,
  REGION VARCHAR,
  SIGNED_UP_AT TIMESTAMP_NTZ
);

Run this once as Insert for the initial load, then switch the same sync to Upsert with MATCH ON set to CUSTOMER_ID for ongoing runs, so new customers get added and existing ones stay current while both systems are live.

Check it worked

Compare row counts on both sides after each run, and spot-check a handful of customer IDs directly rather than trusting an aggregate count alone, since a count can match even if a few individual rows landed with the wrong values.

SELECT COUNT(*) AS row_count FROM ANALYTICS.PUBLIC.CUSTOMERS;

Troubleshooting

If you seeFix
Timestamp values off by a fixed offsetRedshift and Snowflake handle timezone-aware columns differently; confirm both sides agree on UTC versus local before trusting a diff.
Choose at least one key fieldPick a MATCH ON column for Update or Upsert modes.
Job fails on the Redshift sideBackground jobs against Redshift authenticate with IAM keys rather than a password; confirm those are current.

A note on the migration itself

Data Sync moves rows. It doesn't carry over Redshift-specific objects like stored procedures, WLM queue configuration, or user grants, and connections themselves have to be re-added by hand rather than imported wholesale. Treat this as the table-by-table mechanism inside a bigger migration plan, not the entire plan.

A second worked example: a large fact table with Upsert

Dimension tables like customers are small and cheap to move in full every run. A fact table with years of order history is a different problem, moving all of it every time wastes both time and Snowflake compute. Filter the source to a recent window and let Upsert handle both new and changed rows:

-- Redshift source
SELECT order_id, customer_id, order_total, status, updated_at
FROM analytics.orders
WHERE updated_at >= GETDATE() - INTERVAL '7 days';

A 7-day window is usually enough to catch late-arriving updates without re-scanning the entire table's history on every run. If your source system occasionally corrects an order from months back, widen the window periodically, or run a one-time full sync alongside the regular incremental one, rather than assuming a week always covers every edit.

Performance on a large historical backfill

The very first load of a multi-year fact table is the slowest step in the whole process, and it's worth doing separately from the ongoing incremental syncs rather than as the same job. Run the full historical load once, unfiltered, as a dedicated Insert sync, confirm the row count lines up, and only then switch to the smaller windowed Upsert sync for everything going forward. Trying to do both in one sync tends to make the first run painfully slow while adding complexity the ongoing runs don't need.

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 this migrate stored procedures or grants too?

No, it moves table rows. Redshift-specific objects and permissions aren't part of a Data Sync and need to be recreated separately.

Can I run this continuously while both systems are live?

Yes, that's the common pattern: Insert once for the initial load, then Upsert on a schedule to keep Snowflake current.

Do I need IAM keys for this?

Only for background jobs against Redshift that need to run with the app closed; interactive syncs use your normal Redshift credentials.

Will my existing Redshift connections carry over automatically?

No, connections are re-added by hand in QueryFlow; there's no bulk import from another tool.

Move at your own pace.

14-day free trial, no card. Mirror the tables you need while you evaluate the rest.

Start 14-day free trial

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