MySQL to Snowflake

Copy the tables once, or keep the warehouse following the binlog

Schema created for you
Tables, types and primary keys come from the MySQL schema before a row moves
Counted, not assumed
A load that lands fewer rows than MySQL handed over fails the run
No mysqldump
Both ends are live connections, with nothing to export or upload by hand

DBConvert Streams copies MySQL tables into Snowflake, and can keep the warehouse current afterwards. Snowflake's type system is much smaller than MySQL's, so the schema is the part that needs deciding before any row moves - and it is decided from the source schema rather than written by hand.

The stream's mode then decides how the data itself follows.

Load writes the selected tables as Parquet through your user stage, then counts what arrived against what MySQL handed over and fails the run on a shortfall.

CDC reads the binary log instead and streams changes over Snowpipe Streaming, which requires binlog_format = ROW and binlog_row_image = FULL on the MySQL side and key-pair authentication on the Snowflake side.

What MySQL Types Become in Snowflake

Every mapping below is a default applied from the source schema.

In MySQL In Snowflake Worth knowing
TINYINT(1) BOOLEAN MySQL declares a true boolean and a small numeric code identically. A length of 1 is taken as a boolean; anything wider stays numeric as NUMBER(3,0).
SMALLINT, MEDIUMINT, INT, BIGINT NUMBER(5,0), NUMBER(8,0), NUMBER(10,0), NUMBER(19,0) Snowflake expresses integer width as precision on a single type, so the declared precision is what carries the range across.
DECIMAL(p,s) NUMBER(p,s) Precision and scale carry over as declared. A DECIMAL with neither becomes NUMBER(38,18).
FLOAT, DOUBLE FLOAT One 64-bit floating type covers both. A column that needs exactness belongs in DECIMAL on both sides.
CHAR(n), VARCHAR(n), TEXT family VARCHAR(n), or unbounded VARCHAR There is no fixed-width character type, so CHAR stops being blank-padded. TINYTEXT through LONGTEXT collapse to one unbounded VARCHAR.
ENUM, SET VARCHAR The values arrive intact as text; the restriction to a fixed list does not, so the warehouse will accept anything written into that column later.
DATETIME, TIMESTAMP TIMESTAMP_NTZ Parquet logical types survive the load, so values are not re-parsed from text. MySQL normalizes TIMESTAMP to UTC on write and TIMESTAMP_NTZ carries no zone, so the value lands as stored with nothing recording which zone that was.
YEAR NUMBER(4,0) No year type exists, so it arrives as the number it always was.
JSON VARIANT Snowflake's native semi-structured type, so the document stays queryable instead of becoming a string to parse on every read.
BLOB family VARCHAR, base64-encoded Encoding is what avoids UTF-8 validation errors during the load. Budget roughly 33% more storage for those columns, and decode on read.
GEOMETRY, POINT, POLYGON GEOGRAPHY Snowflake's own spatial type, so the geometry stays a geometry.

An AUTO_INCREMENT key arrives as a number, not as a generator

The existing key values come across and the primary key is declared, but nothing in Snowflake is set up to issue the next one. That is the right outcome for a warehouse holding a copy, since MySQL keeps issuing the keys - it matters only if you meant to insert into the Snowflake table yourself. Views, stored procedures, functions and triggers stay in MySQL: Streams moves table data.

What CDC Requires Before It Will Start

Each of these is checked at configuration time, so a stream that cannot work is refused rather than started.

Requirement Value What goes wrong without it
log_bin 1 No binary log is written, so there is no change history to follow.
binlog_format ROW Statement-based logging records the SQL that ran rather than the rows it changed, which cannot be replayed into a warehouse.
binlog_row_image FULL Under MINIMAL, MySQL logs a deleted row's key columns and nothing else, so the change reaching Snowflake describes a row it does not contain.
Primary key On every replicated table MySQL permits a table without one, and older schemas often have a few. Current state is rebuilt by keeping the newest change per key, so such a table is named and refused.
Snowflake authentication Key pair Snowpipe Streaming accepts a signed key and neither a password nor an access token. A load is unaffected; only CDC is refused.

Ordering comes from the binlog, not from arrival

Every change carries its binlog position, and current state is collapsed in that order rather than in the order changes reached the warehouse. A restart replays from the last checkpoint, and replaying the same changes twice cannot alter the result.

Checking What Landed

Loaded tables appear in the Data Explorer beside the MySQL connection they came from.

DBConvert Streams Data Explorer with a Snowflake connection expanded to show a loaded table

Quote the table name in Snowsight

Tables are created as quoted lowercase identifiers, faithful to the MySQL name. Snowflake folds unquoted identifiers to upper case, so SELECT * FROM orders looks for ORDERS and reports that it does not exist.

SELECT COUNT(*) FROM ANALYTICS.PUBLIC."orders";

The Data Explorer quotes for you. This matters in Snowsight, or when a BI tool is pointed at the schema.

Open the file in your browser first

Your file is opened straight from your disk and stays on your computer. Each one gives you the file's structure, a read-only SQL editor over it, and a CSV export of the rows you select.

Load One MySQL Table First, and Count It

Pick a table whose row count you already know and check it in the warehouse before pointing a schema at it. Runs on Linux, Windows, macOS and Docker.