Standard Operating Procedure

MySQL → Azure SQL Database

Cross-engine migration from MySQL (on-prem, AWS RDS, Azure DB for MySQL) to Azure SQL Database. Covers schema conversion, type mapping, binary log live replication, and post-migration Azure SQL configuration.

Source: MySQL 5.7 / 8.0 (on-prem / RDS / Azure Flexible)  ·  Target: Azure SQL Database  ·  Tool: MigrationBridge v1.3.0  ·  Validated June 22 2026

📄 Overview

This SOP covers a full cross-engine migration from MySQL (5.7 or 8.0) running on-premises, AWS RDS for MySQL, or Azure Database for MySQL Flexible Server, to Azure SQL Database (single database or elastic pool). MigrationBridge handles schema conversion, data transfer, and optional live replication via MySQL binary log streaming.

Bidirectional support: MigrationBridge also supports the reverse direction (Azure SQL → MySQL) — see SQL Server → MySQL SOP for that workflow. Live binlog replication in this SOP is unidirectional (MySQL is the source CDC publisher).

Key Engine Differences

MySQL and SQL Server use fundamentally different paradigms. The table below summarizes the most impactful cross-engine differences MigrationBridge resolves automatically:

MySQL Concept Azure SQL Equivalent Notes
AUTO_INCREMENT IDENTITY(1,1) Seed is set to MySQL MAX(id)+1 post-migration via DBCC CHECKIDENT
MySQL schemas (databases) SQL Server schemas (dbo by default) Each MySQL database maps to a SQL Server schema; configurable in MigrationBridge
ENUM('a','b','c') NVARCHAR(max_enum_len) Enum string values are preserved; no CHECK constraint added by default (see Known Issues)
TINYINT(1) BIT MySQL uses TINYINT(1) for boolean; MigrationBridge maps 0→0, 1→1
Backtick identifiers Square bracket identifiers All `column_name`[column_name] in generated DDL
DATETIME / TIMESTAMP DATETIME2 Higher precision and wider range than SQL Server DATETIME
INT UNSIGNED BIGINT Avoids overflow: MySQL UNSIGNED max (4,294,967,295) exceeds SQL Server INT max (2,147,483,647)
SET type NVARCHAR(MAX) Comma-delimited set values stored as a string
Stored procedures and MySQL events are not migrated automatically. MySQL stored procedure syntax is incompatible with T-SQL. All stored procedures require manual conversion. MySQL scheduled events must be re-implemented as Azure Elastic Jobs or Azure Logic Apps.

🔑 Prerequisites — MySQL Source

1. Driver

MigrationBridge uses mysql-connector-python. Verify it is installed in the MigrationBridge Python environment:

pip show mysql-connector-python

If missing:

pip install mysql-connector-python

2. MySQL User Permissions

The migration user requires the following grants. For a bulk (snapshot) migration, SELECT is sufficient. For live binlog replication, additional replication privileges are required.

-- Bulk migration only
GRANT SELECT ON your_database.* TO 'migration'@'%';

-- Live binlog replication (in addition to SELECT)
GRANT REPLICATION CLIENT ON *.* TO 'migration'@'%';
GRANT REPLICATION SLAVE  ON *.* TO 'migration'@'%';

FLUSH PRIVILEGES;

3. Validation Queries

Run these on the MySQL source before starting MigrationBridge:

-- Check MySQL version
SHOW VARIABLES LIKE 'version';

-- Verify grants for the migration user
SHOW GRANTS FOR 'migration'@'%';

-- Confirm binary logging is enabled (required for live mode)
SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'binlog_format';

-- Check current binary log position (note for live sync baseline)
SHOW MASTER STATUS;

4. Network Connectivity

💡
Test connectivity before starting: mysql -h <host> -P 3306 -u migration -p -e "SELECT 1" — a clean response confirms driver, network, and credentials are all valid.

Prerequisites — Azure SQL Target

1. Service Tier Recommendation

For production migrations, provision at least:

Avoid Basic / S0 / S1 tiers during migration. These tiers throttle heavily under bulk INSERT workloads and will cause timeouts. Upgrade before starting; Azure SQL allows online scaling with no downtime.

2. Firewall Rule

Add the MigrationBridge host IP to the Azure SQL Server firewall:

-- Azure CLI (run from migration host or Cloud Shell)
az sql server firewall-rule create \
  --resource-group <rg-name> \
  --server <sql-server-name> \
  --name MigrationBridgeHost \
  --start-ip-address <migration-host-ip> \
  --end-ip-address <migration-host-ip>

3. Contained Database User

Create a contained database user with db_owner in the target database:

-- Connect to master first to create login (optional for contained user)
-- Then connect to the target database:
CREATE USER [migration_user] WITH PASSWORD = '<StrongPassword123!>';
ALTER ROLE db_owner ADD MEMBER [migration_user];

-- Verify
SELECT dp.name, dp.type_desc, rp.name AS role_name
FROM  sys.database_principals dp
JOIN  sys.database_role_members drm ON dp.principal_id = drm.member_principal_id
JOIN  sys.database_principals rp  ON drm.role_principal_id = rp.principal_id
WHERE dp.name = 'migration_user';

4. Test Connection from MigrationBridge

sqlcmd -S <server>.database.windows.net,1433 -d <database> -U migration_user -P "<password>" -Q "SELECT @@VERSION"
Contained database users are preferred for Azure SQL — they do not depend on a server-level login and survive failover group promotion without re-syncing logins.
1

Configure Connections

Open MigrationBridge and navigate to Connections. Configure the MySQL source and Azure SQL target.

MigrationBridge v1.3.0 — Connection Manager
Connections
Assessment
Migrate
Live Sync

Source — MySQL

MySQL
mysql-prod.corp.local
3306
sakila
migration
••••••••••
REQUIRED
Test Connection ✓ Connected — MySQL 8.0.36

Target — Azure SQL Database

Azure SQL Database
prod-sql.database.windows.net
1433
SakilaAzure
migration_user
••••••••••
Yes (TLS 1.2)
Test Connection ✓ Connected — Azure SQL S3
Save & Continue →
💡
Always use SSL/TLS for both connections — MySQL with ssl_mode=REQUIRED and Azure SQL with Encrypt=yes;TrustServerCertificate=no. MigrationBridge enforces TLS 1.2 by default.
2

Assessment — MySQL → Azure SQL Compatibility

MigrationBridge scans the MySQL source schema and produces a compatibility report before any data is moved. This is a read-only pass — no changes are made to either database.

MigrationBridge — Assessment Report: sakila → SakilaAzure
Connections
Assessment
Migrate
Live Sync
Table Column MySQL Type Converted Type Status
actor actor_id SMALLINT UNSIGNED AUTO_INCREMENT SMALLINT IDENTITY(1,1) Auto-converted
film film_id SMALLINT UNSIGNED AUTO_INCREMENT SMALLINT IDENTITY(1,1) Auto-converted
film rating ENUM('G','PG','PG-13','R','NC-17') NVARCHAR(5) Informational
film special_features SET('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') NVARCHAR(MAX) Informational
film description TEXT NVARCHAR(MAX) Auto-converted
film last_update TIMESTAMP DATETIME2 Auto-converted
payment amount DECIMAL(5,2) DECIMAL(5,2) Direct map
staff active TINYINT(1) BIT Auto-converted
store manager_staff_id TINYINT UNSIGNED TINYINT Auto-converted
16 tables assessed  ·  0 blocking issues  ·  4 informational warnings  ·  0 stored procedures (not migrated)
ENUM / SET columns are informational, not blocking. MigrationBridge converts them to NVARCHAR. The original string values are preserved. If you require a CHECK constraint on the target to enforce ENUM domain, add it manually post-migration.
🚫
Stored procedures are not migrated. MySQL stored procedure syntax (e.g., DELIMITER, CALL, lack of schema scope) is incompatible with T-SQL. All PROCEDURE, FUNCTION, TRIGGER, and EVENT objects must be manually rewritten in T-SQL before cutover.
3

Select Tables

Choose which tables to migrate. MigrationBridge lists all tables from the MySQL source database with row counts, size estimates, and any conversion warnings.

MigrationBridge — Table Selection: sakila
Connections
Assessment
Migrate
Live Sync
Select All
16 of 16 tables selected
Schema.Table Rows Size Warnings
sakila.actor 200 32 KB
sakila.film 1,000 740 KB ENUM: rating SET: special_features
sakila.film_actor 5,462 256 KB
sakila.customer 599 88 KB
sakila.payment 16,049 1.2 MB
sakila.rental 16,044 1.1 MB
sakila.staff 2 8 KB
sakila.store 2 8 KB
... 8 more tables (inventory, category, film_category, film_text, address, city, country, language)
Full Load
Full Load + Live Binlog
Incremental Only
Start Migration → Save Profile
4

Run Migration

MigrationBridge executes in phases: (1) DDL generation and schema creation on Azure SQL, (2) data transfer table-by-table with batched INSERTs, (3) index and constraint creation. Monitor the real-time log below.

MigrationBridge — Migration Log: sakila → SakilaAzure
[2026-06-22 09:14:02] MigrationBridge v1.3.0 — starting full load
[2026-06-22 09:14:02] Source: MySQL 8.0.36 @ mysql-prod.corp.local:3306 / sakila
[2026-06-22 09:14:02] Target: Azure SQL Database @ prod-sql.database.windows.net:1433 / SakilaAzure
[2026-06-22 09:14:03] [PHASE 1] Generating DDL from MySQL schema...
[2026-06-22 09:14:03] AUTO_INCREMENT detected on 12 columns — converting to IDENTITY(1,1)
[2026-06-22 09:14:03] TINYINT(1) detected on staff.active — converting to BIT
[2026-06-22 09:14:03] ENUM detected: film.rating ENUM('G','PG','PG-13','R','NC-17') — converting to NVARCHAR(5) [informational]
[2026-06-22 09:14:03] SET detected: film.special_features SET(...) — converting to NVARCHAR(MAX) [informational]
[2026-06-22 09:14:03] TEXT columns (film.description) — converting to NVARCHAR(MAX)
[2026-06-22 09:14:03] TIMESTAMP columns (6 total) — converting to DATETIME2
[2026-06-22 09:14:03] Backtick identifiers converted to square brackets
[2026-06-22 09:14:04] DDL written: 16 CREATE TABLE statements
[2026-06-22 09:14:04] [PHASE 1] Executing DDL on Azure SQL...
[2026-06-22 09:14:05] Schema [dbo] exists — using as default schema
[2026-06-22 09:14:06] 16 tables created on target
[2026-06-22 09:14:06] [PHASE 2] Data transfer — batch size: 1000 rows
[2026-06-22 09:14:06] [1/16] actor — 200 rows migrated (0.2s)
[2026-06-22 09:14:07] [2/16] film — 1,000 rows migrated (1.1s)
[2026-06-22 09:14:08] [3/16] film_actor — 5,462 rows migrated (3.4s)
[2026-06-22 09:14:09] [4/16] film_category — 1,000 rows migrated (0.8s)
[2026-06-22 09:14:10] [5/16] film_text — 1,000 rows migrated (0.7s)
[2026-06-22 09:14:11] [6/16] address — 603 rows migrated (0.5s)
[2026-06-22 09:14:11] [7/16] city — 600 rows migrated (0.4s)
[2026-06-22 09:14:11] [8/16] country — 109 rows migrated (0.2s)
[2026-06-22 09:14:12] [9/16] customer — 599 rows migrated (0.5s)
[2026-06-22 09:14:13] [10/16] inventory — 4,581 rows migrated (2.9s)
[2026-06-22 09:14:15] [11/16] language — 6 rows migrated (0.1s)
[2026-06-22 09:14:15] [12/16] payment — 16,049 rows migrated (8.7s)
[2026-06-22 09:14:24] [13/16] rental — 16,044 rows migrated (8.4s)
[2026-06-22 09:14:32] [14/16] staff — 2 rows migrated (0.1s)
[2026-06-22 09:14:32] [15/16] store — 2 rows migrated (0.1s)
[2026-06-22 09:14:32] [16/16] category — 16 rows migrated (0.1s)
[2026-06-22 09:14:33] [PHASE 3] Creating indexes and foreign key constraints...
[2026-06-22 09:14:38] 48 indexes created
[2026-06-22 09:14:39] 12 foreign keys created
[2026-06-22 09:14:39] [PHASE 4] Reseeding IDENTITY columns to match MySQL AUTO_INCREMENT max values...
[2026-06-22 09:14:39] DBCC CHECKIDENT([actor], RESEED, 200) — done
[2026-06-22 09:14:39] DBCC CHECKIDENT([film], RESEED, 1000) — done
[2026-06-22 09:14:40] 12 identity columns reseeded
[2026-06-22 09:14:40] ============================================================
[2026-06-22 09:14:40] MIGRATION COMPLETE — 46,273 total rows | 16 tables | 00:00:38 elapsed
[2026-06-22 09:14:40] ============================================================
✓ Complete Download Full Log View Report
5

Verify Migration

After migration completes, validate row counts on both sides and confirm IDENTITY seeds are correct.

Row Count Validation — Azure SQL Target

-- Row counts on Azure SQL target
SELECT
    t.name        AS table_name,
    p.rows        AS row_count
FROM  sys.tables t
JOIN  sys.partitions p
      ON t.object_id = p.object_id
      AND p.index_id IN (0, 1)
ORDER BY t.name;

Cross-Engine Row Count Comparison

Run this on MySQL source to get counts, then compare against Azure SQL output above:

-- MySQL source: row counts per table
SELECT
    table_name,
    table_rows AS estimated_rows
FROM  information_schema.tables
WHERE table_schema = 'sakila'
  AND table_type  = 'BASE TABLE'
ORDER BY table_name;

-- For exact counts (slower but accurate):
SELECT 'actor', COUNT(*) FROM actor
UNION ALL
SELECT 'film', COUNT(*) FROM film
UNION ALL
SELECT 'payment', COUNT(*) FROM payment
UNION ALL
SELECT 'rental', COUNT(*) FROM rental;

Verify IDENTITY Seeds (Azure SQL)

-- Check current IDENTITY seed vs max value for actor
SELECT
    OBJECT_NAME(object_id) AS table_name,
    last_value,
    seed_value,
    increment_value
FROM  sys.identity_columns
ORDER BY table_name;

-- Confirm actor IDENTITY matches MySQL MAX(actor_id)
SELECT MAX(actor_id) AS max_id FROM [dbo].[actor];

Spot-Check Data Integrity

-- Verify ENUM-converted column values are intact
SELECT DISTINCT rating FROM [dbo].[film] ORDER BY rating;
-- Expected: G, NC-17, PG, PG-13, R

-- Verify TINYINT(1)->BIT conversion for staff.active
SELECT staff_id, first_name, last_name, active FROM [dbo].[staff];
-- active should be 0 or 1 (BIT)

-- Verify TIMESTAMP->DATETIME2 conversion
SELECT TOP 5 last_update FROM [dbo].[film] ORDER BY last_update DESC;
-- Should display as DATETIME2 with full precision
💡
MigrationBridge's built-in Verify tab runs an automated row count comparison between source and target for all migrated tables and flags any discrepancies. Always run this before marking the migration complete.
6

Post-Migration Tasks

Complete these tasks after data validation passes, before handing off to application teams or cutting over traffic.

1. Reseed Identity Columns

MigrationBridge auto-reseeds during Phase 4, but verify and manually correct if needed:

-- Reseed actor identity to max value + 1
DBCC CHECKIDENT('dbo.actor', RESEED, 200);   -- replace 200 with MAX(actor_id)
DBCC CHECKIDENT('dbo.film', RESEED, 1000);
DBCC CHECKIDENT('dbo.customer', RESEED, 599);
DBCC CHECKIDENT('dbo.payment', RESEED, 16049);
DBCC CHECKIDENT('dbo.rental', RESEED, 16044);

-- Verify all identity seeds
SELECT OBJECT_NAME(object_id) table_name, last_value, seed_value
FROM  sys.identity_columns ORDER BY table_name;

2. Update Statistics

-- Update all statistics on freshly loaded tables
EXEC sp_updatestats;

-- Or per-table (recommended for large tables):
UPDATE STATISTICS [dbo].[payment] WITH FULLSCAN;
UPDATE STATISTICS [dbo].[rental]  WITH FULLSCAN;

3. Verify ENUM Values and Add CHECK Constraints (Optional)

-- Confirm ENUM values are correct on target
SELECT DISTINCT rating FROM [dbo].[film];

-- Optionally add CHECK constraint to enforce ENUM domain on Azure SQL
ALTER TABLE [dbo].[film]
    ADD CONSTRAINT chk_film_rating
    CHECK (rating IN ('G', 'PG', 'PG-13', 'R', 'NC-17'));

4. Review JSON Columns (if any)

-- MySQL JSON columns are migrated as NVARCHAR(MAX)
-- Test JSON path queries with SQL Server's JSON functions (SQL 2016+)
SELECT
    id,
    JSON_VALUE(metadata_col, '$.key') AS extracted_value
FROM  [dbo].[your_table]
WHERE ISJSON(metadata_col) = 1;

5. Replace MySQL Events with Azure Elastic Jobs

MySQL scheduled events have no direct equivalent in Azure SQL Database. Re-implement them using:

Azure SQL Database does not support SQL Server Agent. Use Azure Elastic Jobs as the direct replacement for scheduled T-SQL tasks. See the Jobs Guide for setup instructions.

6. Index Review and Rebuild

-- Check index fragmentation after bulk load
SELECT
    OBJECT_NAME(ips.object_id)     AS table_name,
    i.name                          AS index_name,
    ips.avg_fragmentation_in_percent,
    ips.page_count
FROM  sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
JOIN  sys.indexes i
      ON ips.object_id = i.object_id
      AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 30
  AND ips.page_count > 100
ORDER BY ips.avg_fragmentation_in_percent DESC;

Live Binlog Replication — MySQL → Azure SQL

MigrationBridge supports live change replication using MySQL binary log (binlog) in ROW format. This enables a low-downtime cutover strategy: perform the full load first, then tail the binlog to keep Azure SQL in sync until you are ready to cut over application traffic.

Live binlog replication requires binlog_format=ROW on the MySQL source. Statement-based and mixed formats are not supported. Verify this before enabling live sync mode.

1. Enable Binary Logging on MySQL (on-premises)

Edit my.cnf (Linux) or my.ini (Windows) and restart MySQL:

[mysqld]
server-id          = 1
log_bin            = /var/log/mysql/mysql-bin.log
binlog_format      = ROW
binlog_row_image   = FULL
expire_logs_days   = 30
# MySQL 8.0+ alternative to expire_logs_days:
# binlog_expire_logs_seconds = 2592000

Verify after restart:

SHOW VARIABLES LIKE 'log_bin';          -- should be ON
SHOW VARIABLES LIKE 'binlog_format';    -- should be ROW
SHOW VARIABLES LIKE 'binlog_row_image'; -- should be FULL
SHOW MASTER STATUS;

2. Enable Binary Logging on AWS RDS for MySQL

RDS manages the MySQL config file — use Parameter Group and the RDS procedure:

-- Set binlog retention in RDS (hours) — call from any MySQL client connected to RDS
CALL mysql.rds_set_configuration('binlog retention hours', 720);

-- Verify binlog format (set via Parameter Group: binlog_format = ROW)
SHOW VARIABLES LIKE 'binlog_format';
SHOW MASTER STATUS;
For RDS, set binlog_format = ROW in the Parameter Group and apply it. A reboot may be required if the parameter group was not previously associated. binlog retention hours must be set via the stored procedure — it cannot be set via Parameter Group.

3. Azure Database for MySQL Flexible Server

-- Binlog is enabled by default on Azure DB for MySQL Flexible Server
-- Verify format via Azure Portal: Server Parameters > binlog_format
-- Or via CLI:
az mysql flexible-server parameter show \
  --resource-group <rg> \
  --server-name <mysql-server> \
  --name binlog_format

4. Full Load First, Then Enable Live Sync

  1. Record the binlog position before starting the full load: SHOW MASTER STATUS; — note File and Position.
  2. Run the full load migration in MigrationBridge (Steps 1–5 above).
  3. In MigrationBridge, navigate to Live Sync tab and enter the binlog file and position recorded in step 1.
  4. Start the binlog tail — MigrationBridge will replay all INSERTs, UPDATEs, and DELETEs that occurred during and after the full load.
  5. Monitor lag in the Live Sync panel. When lag reaches < 1 second, the target is caught up.
  6. Cut over application connection strings from MySQL to Azure SQL. Stop the live sync job in MigrationBridge.
MigrationBridge — Live Sync: sakila → SakilaAzure
Connections
Migrate
Live Sync

Binlog Start Position

mysql-bin.000042
14823
101 (replica)

Live Sync Status

Running
0.3 s
142
48,291
[2026-06-22 09:18:12] Binlog tail started at mysql-bin.000042:14823
[2026-06-22 09:18:12] INSERT sakila.rental row_id=16045 → [dbo].[rental] INSERT applied
[2026-06-22 09:18:12] INSERT sakila.payment row_id=16050 → [dbo].[payment] INSERT applied
[2026-06-22 09:18:13] UPDATE sakila.customer customer_id=14 → [dbo].[customer] UPDATE applied
[2026-06-22 09:18:13] DELETE sakila.rental row_id=16045 → [dbo].[rental] DELETE applied
[2026-06-22 09:18:14] Binlog position advanced to mysql-bin.000042:19201
[2026-06-22 09:18:14] Lag: 0.3s | Events applied: 48,291 | Errors: 0
Pause Sync Stop & Cutover

5. Validation During Live Sync

-- Compare row counts during live sync (run on both sides)
-- MySQL source:
SELECT COUNT(*) FROM rental;   -- e.g. 16,052

-- Azure SQL target:
SELECT COUNT(*) FROM [dbo].[rental]; -- should match within lag window

-- Confirm a specific row was replicated
SELECT * FROM [dbo].[rental] WHERE rental_id = 16052;

🔄 Type Conversion Map

Complete MySQL → Azure SQL (T-SQL) data type mapping used by MigrationBridge. All conversions are applied automatically during schema generation (Phase 1).

MySQL Type Azure SQL / T-SQL Type Notes
AUTO_INCREMENT (column attribute)IDENTITY(1,1)Seed reseeded to MySQL MAX(id) post-load
TINYINT(1)BITBoolean convention; 0=false, 1=true
TINYINT(n > 1)TINYINTRange 0–255; UNSIGNED stays TINYINT
SMALLINTSMALLINTDirect map
SMALLINT UNSIGNEDINTUnsigned max 65535 fits in INT
INT / INTEGERINTDirect map
INT UNSIGNEDBIGINTUnsigned max 4,294,967,295 exceeds SQL Server INT; mapped to BIGINT to prevent overflow
BIGINTBIGINTDirect map
BIGINT UNSIGNEDDECIMAL(20,0)BIGINT UNSIGNED exceeds SQL Server BIGINT max; use DECIMAL
FLOATFLOATDirect map (24-bit precision)
DOUBLE / DOUBLE PRECISIONFLOAT(53)53-bit precision (SQL Server default FLOAT)
DECIMAL(p,s) / NUMERIC(p,s)DECIMAL(p,s)Direct map; preserves precision/scale
DATEDATEDirect map
DATETIMEDATETIME2Higher precision; DATETIME2 supports wider date range
TIMESTAMPDATETIME2MySQL TIMESTAMP is UTC-stored; converted to DATETIME2 preserving UTC value
TIMETIMEDirect map
YEARSMALLINT2 or 4-digit year stored as integer
CHAR(n)CHAR(n)Direct map; N doubled for Unicode if charset is utf8/utf8mb4
VARCHAR(n)NVARCHAR(n)N is character count; for utf8mb4 charset, Azure SQL NVARCHAR handles Unicode natively
TEXTNVARCHAR(MAX)65,535 byte MySQL max; NVARCHAR(MAX) is 2 GB
MEDIUMTEXTNVARCHAR(MAX)16 MB MySQL max
LONGTEXTNVARCHAR(MAX)4 GB MySQL max
TINYTEXTNVARCHAR(255)255 byte MySQL max
BLOBVARBINARY(MAX)Binary large object
MEDIUMBLOBVARBINARY(MAX)
LONGBLOBVARBINARY(MAX)
TINYBLOBVARBINARY(255)
BINARY(n)BINARY(n)Direct map
VARBINARY(n)VARBINARY(n)Direct map
JSONNVARCHAR(MAX)Azure SQL validates JSON via ISJSON(); use JSON_VALUE / JSON_QUERY for path access
ENUM('a','b',...)NVARCHAR(max_enum_len)Max length = longest enum value. No CHECK constraint added by default
SET('a','b',...)NVARCHAR(MAX)Comma-delimited string; no constraint
ZEROFILL (column attribute)(dropped)ZEROFILL is display-only formatting; dropped silently. Numeric value is preserved
GEOMETRY / spatialNVARCHAR(MAX) (WKT)Spatial types require manual conversion to SQL Server geography/geometry types

Known Issues & Limitations

Case-Insensitive Table Names (MySQL) vs Case-Sensitive Collation (SQL Server)

MySQL on Linux is case-sensitive by default (lower_case_table_names=0); MySQL on Windows is case-insensitive (lower_case_table_names=1). Azure SQL Database defaults to a case-insensitive collation (SQL_Latin1_General_CP1_CI_AS). If your MySQL source has tables that differ only by case, they will collide on Azure SQL. MigrationBridge will warn during assessment.

ENUM Values — No Constraint on Target

MySQL enforces ENUM domain at the storage engine level. After migration to NVARCHAR, any string can be inserted. If you require constraint enforcement, add a CHECK constraint manually (see Step 6).

INT UNSIGNED Overflow Risk

🚫
If your MySQL INT UNSIGNED column contains values greater than 2,147,483,647 (SQL Server INT max), they will overflow if mapped to INT. MigrationBridge maps all INT UNSIGNED to BIGINT to prevent this. Do not override this mapping unless you have verified your data range.

ZEROFILL Formatting Lost

MySQL's ZEROFILL attribute is a display-only zero-padding hint. It is not a storage attribute. MigrationBridge drops it during conversion — the numeric value is preserved but leading zeros are not rendered by default. If your application depends on zero-padded display, implement this in application code or a computed column.

MySQL Events → Azure Elastic Jobs

MySQL EVENT objects (scheduled tasks) are not migrated. Each event must be re-created manually as an Azure Elastic Job, Azure Logic App, or Azure Function Timer trigger. Review all events before cutover:

SELECT event_name, event_definition, interval_value, interval_field, status
FROM  information_schema.events
WHERE event_schema = 'sakila';

MySQL Stored Procedures Not Migrated

🚫
MySQL stored procedures use a syntax that is fundamentally incompatible with T-SQL: different delimiter handling (DELIMITER //), different variable declaration syntax (DECLARE x INT DEFAULT 0 vs DECLARE @x INT = 0), and different control flow. All stored procedures, functions, and triggers must be manually rewritten in T-SQL. Use the assessment report to get the full list.

Backtick Identifier Conversion

MySQL uses backtick (`) quoting for identifiers. MigrationBridge converts all backtick-quoted identifiers to SQL Server square bracket ([ / ]) notation in generated DDL and DML. Application queries that still use backtick syntax will fail against Azure SQL — update all application SQL to use square brackets or remove quoting if not needed.

MySQL Schema Mapping

In MySQL, a "database" (e.g., sakila) is analogous to a SQL Server "schema." MigrationBridge maps each MySQL database to the dbo schema by default. If you are migrating multiple MySQL databases into one Azure SQL database, configure the schema name mapping in MigrationBridge's Advanced Options to avoid table name collisions.

📷 UI Behavior During Migration

MigrationBridge's UI remains fully responsive during migration and live sync operations. All database work runs in background worker threads. The following behaviors are by design:

💡
If the MigrationBridge window is closed during a live sync operation, the background sync service continues running. Re-open MigrationBridge and navigate to Live Sync to reconnect to the running job.

MigrationBridge v1.3.0 · MySQL → Azure SQL Database · User Guide · Troubleshooting