Migration

MySQL and MSSQL to Supabase migration: crossing the dialect gap

MySQL and MSSQL to Supabase migration

Why teams still make this move, exactly where the dialect differences bite, and how to plan a conversion that doesn’t quietly corrupt data on the way through.

Every other migration in this series has one thing in common that makes life easier: the source is already Postgres. A MySQL or MSSQL to Supabase migration doesn’t get that shortcut. Both are mature, capable relational databases — this isn’t a “your database was wrong” story — but they’re different dialects with different type systems, different procedural languages, and different assumptions about case sensitivity and quoting. That’s why this is the one migration in our series we rate as higher complexity, and it’s worth being upfront about that rather than pretending it’s a weekend project.

This guide covers why teams still make the move, exactly where the dialect differences bite, and how to plan a conversion that doesn’t quietly corrupt data on the way through.

TL;DR
  • MSSQL’s per-core licensing gets expensive at scale, and MySQL’s JSON support and extension ecosystem are limited compared to modern Postgres.
  • Teams want the Postgres ecosystem specifically — pgvector, PostGIS, and a genuinely open-source engine instead of a commercially licensed or narrower one.
  • This is a shift from a traditional RDBMS to a modern backend stack, not just a database swap — Auth, Storage, and instant APIs come with it.
  • Migration complexity is higher than any other source in this series: different dialects mean real schema conversion, not a straight copy. pgloader handles the mechanical parts; stored procedures, triggers, and application queries need human review.
  • Gart Solutions plans and executes migrations of any complexity — from MySQL and MSSQL specifically, and from the wider Postgres and BaaS ecosystem more broadly.

Why teams are leaving MySQL and MSSQL for Supabase

MSSQL licensing costs, MySQL’s JSON and extension limits

These are two different databases with two different reasons to leave, and it’s worth treating them separately rather than lumping them together.

MSSQL is licensed, and that licensing is per-core, which means the cost of your database scales with your compute in a way that’s very different from an open-source engine. For a growing application, that’s a bill that climbs whether or not the extra cores are actually the bottleneck. It’s a predictable, well-understood cost — but it’s a cost Postgres simply doesn’t have.

MySQL added a JSON column type years ago, and it works — but it’s noticeably more limited than Postgres’s JSONB: fewer indexing strategies, a smaller set of JSON-specific functions, and less of an ecosystem built around treating semi-structured data as a first-class citizen. MSSQL’s JSON support is even more bolted-on — JSON is stored as NVARCHAR with a set of functions layered on top, not a native type at all. If your application increasingly stores flexible, semi-structured data alongside your relational tables, Postgres’s JSONB with GIN indexing is a meaningfully different experience, not just a syntax change.

The pull of the Postgres ecosystem

Beyond the specific pain points, there’s a pull toward Postgres as a platform. Extensions like pgvector for AI/embedding workloads, PostGIS for geospatial data, and a large, genuinely open-source ecosystem of tooling built around Postgres specifically make it a different kind of database to build on — one where the community and the extension library are working with you rather than around a commercial licensing model.

Moving from a traditional RDBMS to a modern backend stack

This is often the underlying motivation once you look past the technical bullet points: teams aren’t just swapping a database engine, they’re moving from “database plus everything else we built separately” to a bundled platform. That’s the same structural shift we’ve covered for other sources in this series — Auth, Storage, Realtime, and instant APIs arriving as part of the platform instead of separate integration work — except here it’s paired with a genuine dialect migration underneath it.

What actually happens during a MySQL/MSSQL to Supabase migration

Data types: there’s no exact 1:1 mapping

This is where the real engineering work lives. MySQL and MSSQL each have their own type systems, and while most types have a reasonable Postgres equivalent, “reasonable” still means decisions, not a mechanical copy:

MySQL type MSSQL type Postgres equivalent Note
TINYINT(1) BIT boolean Often used as a boolean flag in both source systems; needs explicit conversion, not just a rename
INT AUTO_INCREMENT INT IDENTITY(1,1) integer generated always as identity Sequence behavior needs to be recreated, not just the column type
DATETIME DATETIME2 timestamptz Time zone handling differs meaningfully between all three — worth auditing, not assuming
ENUM(...) N/A (usually a CHECK constraint) text + check constraint, or a Postgres enum type MySQL’s ENUM has no MSSQL equivalent; Postgres supports true enum types but they’re less flexible to alter later
JSON NVARCHAR (with JSON functions) jsonb Postgres’s JSONB is a genuine upgrade here, not just a rename — but queries against it need to be rewritten
N/A UNIQUEIDENTIFIER uuid Direct conceptual match, but MSSQL’s GUID generation and ordering behavior differ from Postgres’s uuid functions

None of these are exotic problems — but each one is a decision, multiplied across every table in your schema, and getting even a handful wrong silently (a boolean stored as an integer that’s sometimes 2, a timestamp that’s off by a time zone) is how data quality issues survive a migration and surface months later.

Identifier quoting and case sensitivity: a quiet source of bugs

MySQL uses backticks for quoted identifiers; MSSQL uses square brackets; Postgres uses double quotes — and none of them are interchangeable syntax you can find-and-replace safely. More importantly, MySQL is case-insensitive for unquoted identifiers by default, and Postgres is case-sensitive. A table or column name that worked fine on MySQL regardless of casing can silently behave differently once it’s on Postgres, especially in hand-written queries that were never rigorously consistent about case to begin with. This is a common source of “why did this query stop working” confusion after a migration, and it’s worth auditing deliberately rather than discovering it one broken query at a time.

Stored procedures and triggers: rewritten, not translated

This is the part that most differentiates a MySQL/MSSQL migration from every other source in this series. MSSQL’s procedural language is T-SQL. MySQL has its own procedural SQL dialect. Postgres uses PL/pgSQL. These are not compatible dialects of the same language — they’re genuinely different languages with different syntax, different control-flow constructs, and different built-in functions. A stored procedure or trigger that encodes real business logic (and in older systems, plenty of them do) needs to be read, understood, and rewritten in PL/pgSQL — not mechanically translated by a tool. This is usually where migration timelines that started as “just a database move” turn into a genuine engineering project.

Data validation: the step that catches what type mapping missed

Because there’s real transformation happening — not a byte-for-byte copy — validation matters more here than in any other migration in this series. A thorough pass includes row count reconciliation per table, spot-checking transformed values against source data (especially for booleans, timestamps, and anything that went through an ENUM-to-text or JSON-to-JSONB conversion), and running your application’s actual query patterns against the migrated schema before cutover, not just confirming the data “looks right” in a table viewer.

How complex is a MySQL/MSSQL to Supabase migration, really?

Component Complexity Why
Schema and data types High No 1:1 type mapping; every table needs deliberate conversion decisions, not a mechanical copy
Stored procedures / triggers High T-SQL and MySQL’s procedural SQL are different languages from PL/pgSQL; business logic needs rewriting, not translation
Data validation Moderate–High Type conversions (booleans, timestamps, JSON) can silently produce subtly wrong data without thorough spot-checking
Application queries Moderate Case sensitivity, quoting, and dialect-specific SQL functions often need auditing even when the schema itself is sound

Taken together: higher complexity than any other source we cover. That’s not a reason to avoid the migration — plenty of teams have good reasons to make this move — but it’s a reason to scope it as a genuine engineering project with schema design and validation phases, not a weekend script.

Tooling: what pgloader handles, and what it doesn’t

pgloader is the standard open-source tool for this migration, and it supports MySQL, MSSQL, and several other sources directly:

pgloader mysql://user:password@source-host/dbname \
  postgresql://postgres:password@db.xxxxxxxxxx.supabase.co/postgres

pgloader does real work here — it handles type casting and data sanitization automatically for most common cases, which is exactly the tedious, error-prone part you don’t want to do by hand across dozens of tables. But it’s worth being precise about its boundaries: pgloader migrates schema and data. It does not migrate views, triggers, stored procedures, or functions. Those need to be read, understood, and recreated as PL/pgSQL by hand. Teams that treat a successful pgloader run as “the migration is done” are the ones who discover missing business logic in production weeks later.

Common pitfalls we see

  • Treating pgloader’s success as the finish line. A clean pgloader run migrates your tables and data correctly — it says nothing about whether your stored procedures, triggers, and application queries still work.
  • Missing silent type coercion issues. A TINYINT(1) that’s occasionally storing something other than 0 or 1, or a timestamp that’s technically correct but in the wrong time zone, won’t throw an error — it’ll just be quietly wrong until someone notices downstream.
  • Assuming case sensitivity doesn’t matter. Query and application code that was never disciplined about identifier casing on MySQL can break unpredictably on Postgres’s case-sensitive defaults.
  • Underestimating stored procedure complexity. Older MSSQL or MySQL systems often have years of business logic embedded in procedures and triggers that nobody has fully read in a long time. Auditing what’s actually there — not just what’s documented — is necessary before scoping a realistic timeline.
  • Skipping collation and character set review. Text comparison and sorting behavior can differ between source and destination if character set and collation settings aren’t deliberately matched or accounted for.

A quick pre-migration checklist

  • How many stored procedures, triggers, and functions does your database actually have, and how much genuine business logic (versus simple CRUD) do they contain?
  • Which columns rely on MySQL’s ENUM type or MSSQL-specific types (UNIQUEIDENTIFIER, MONEY, BIT) that need a deliberate Postgres equivalent chosen, not just a default mapping?
  • How disciplined is your existing codebase about identifier casing in hand-written SQL? This predicts how much post-migration query debugging to expect.
  • Are there application-level assumptions about case-insensitive matching (search, lookups) that were implicitly relying on MySQL’s default collation behavior?
  • What’s your realistic tolerance for a validation phase before cutover? Given the type-conversion risk here, this is the migration in our series where skipping validation is the most costly place to cut corners.
🔄
Planning a genuine dialect migration, not a copy-paste job? Gart Solutions handles MySQL and MSSQL to Supabase migrations end to end — schema conversion, stored procedure rewrites, and the validation pass that catches what tooling alone misses. See how our migration practice works →

How Gart Solutions approaches these migrations

A MySQL or MSSQL to Supabase migration is the one in this series where we lean hardest on doing the engineering work by hand rather than trusting automation to get it right unsupervised. Our process typically covers:

  • Schema and type audit — a deliberate conversion plan for every non-trivial type, not a default mapping applied blindly
  • pgloader-driven data migration — for the mechanical parts it’s genuinely good at, with full validation afterward
  • Stored procedure and trigger rewrites — read, understood, and reimplemented in PL/pgSQL, not mechanically translated
  • Thorough validation — row-level spot-checks and real application query testing before cutover, given how much silent-failure risk this migration carries compared to a Postgres-to-Postgres move

Just as important: we handle migrations of any level of complexity, not only from MySQL and MSSQL. The same team that rewrites T-SQL business logic into PL/pgSQL also runs comparatively simple migrations from Firebase, Amazon RDS, Neon, Heroku, and Render onto Supabase — and, where it’s the right call for a client, migrations away from Supabase too. Whatever the source, the underlying discipline is the same: understand exactly what the data and logic are doing before moving them, not just where they need to end up.

Conclusion

A MySQL or MSSQL to Supabase migration is honestly the most demanding one in this series, and it’s worth respecting that rather than underselling it. There’s no shortcut around the fact that these are different dialects with different type systems and different procedural languages — pgloader handles a meaningful chunk of the mechanical work, but the schema decisions, stored procedure rewrites, and validation pass need real engineering attention. Done properly, though, it’s a well-understood problem with mature tooling on the mechanical side, not an unmapped one — and the destination, a modern Postgres-based platform with a genuinely open extension ecosystem, is usually worth the more careful path to get there.

Planning a Supabase or Postgres migration?

Gart Solutions’ database migration engineers plan and execute Postgres migrations — into Supabase, within a live schema, or off a managed platform entirely — with a rollback plan built in from day one, not bolted on after something breaks.

40% cost reduction on a comparable Postgres migration
25% query performance improvement
Minimal downtime during cutover
Database Migration Cloud Migration SRE & Monitoring Compliance Audit
Talk to a migration engineer  →

FAQ

Is migrating from MySQL or MSSQL to Supabase harder than other migrations?

Yes, and it's worth saying plainly: this is the highest-complexity migration in our series, specifically because MySQL and MSSQL are different SQL dialects from Postgres, with different type systems and different procedural languages. Migrations where the source is already Postgres (Neon, Amazon RDS, Heroku) skip the dialect conversion problem entirely — this one doesn't have that shortcut.

Can a tool handle this migration automatically?

Partially. pgloader handles schema and data migration well for most common cases, including type casting and data sanitization. But it explicitly does not migrate views, triggers, stored procedures, or functions — those need to be manually read and rewritten in PL/pgSQL. Treating a successful pgloader run as a complete migration is one of the most common mistakes we see.

What happens to our stored procedures and triggers?

They need to be rewritten, not translated. T-SQL (MSSQL) and MySQL's procedural SQL are different languages from PL/pgSQL, with different syntax and control-flow constructs. Anyone doing this migration needs to read each procedure and trigger, understand what it actually does, and reimplement that logic in PL/pgSQL — which is why auditing how much procedural logic exists is one of the first things worth doing before scoping a timeline.

Will our application code need to change?

Likely, in places. Beyond any code that directly references MySQL- or MSSQL-specific SQL syntax, the case-sensitivity difference between MySQL (case-insensitive by default) and Postgres (case-sensitive) is a common source of queries that need auditing, even when the underlying schema conversion was done correctly.

How do we make sure data wasn't silently corrupted during type conversion?

Through deliberate validation, not just confirming row counts match. That means spot-checking values that went through non-trivial conversions — booleans, timestamps, ENUM-to-text or JSON-to-JSONB fields — and running your application's actual query patterns against the migrated schema before cutover. This migration carries more silent-failure risk than a Postgres-to-Postgres move, so the validation phase deserves proportionally more attention.

How long does a MySQL/MSSQL to Supabase migration typically take?

Meaningfully longer than a same-dialect migration, and driven primarily by how much stored procedure and trigger logic exists, not by data volume. A schema with minimal procedural logic and mostly standard types can move in a few weeks; a system with years of accumulated business logic in triggers and stored procedures can take considerably longer to audit and rewrite properly. Gart Solutions scopes this during an initial technical audit rather than estimating from data size alone.

Does Supabase support the Postgres extensions we'd want after migrating from MSSQL or MySQL?

Supabase supports a broad set of standard Postgres extensions, including pgvector and PostGIS, which is often part of the appeal of making this move in the first place. It's still worth confirming any specific extension your target architecture depends on is available before finalizing the migration plan.

What other platforms can Gart Solutions help migrate to or from Supabase?

Beyond MySQL and MSSQL, we regularly handle migrations from Firebase, Amazon RDS, Neon, Heroku, and Render onto Supabase, as well as migrations away from Supabase when that's the right call for a client's scale or requirements. The common thread is the same engineering discipline: understand what's actually there before moving it, and validate thoroughly before cutover.
arrow arrow

Thank you
for contacting us!

Please, check your email

arrow arrow

Thank you

You've been subscribed

We use cookies to enhance your browsing experience. By clicking "Accept," you consent to the use of cookies. To learn more, read our Privacy Policy