Skip to content
IntoSQL home

Convert MySQL to SQL Server

Drop a dump from mysqldump, mariadb-dump, mysqlpump, phpMyAdmin or Adminer, as .sql or .sql.gz, and download a script for SQL Server with the same tables, keys, indexes and data. Every value SQL Server can't hold as it is, and every object it doesn't carry, is listed in the report.

Your file
sqlshop_2024.sqlSample file, 5.1 kB
Your database

18 rows converted, 10 fixes, none rejected

Show

Tables in shop_2024.sqlMySQL 8.0.46 dump · written by mysqldump · utf8mb4

  • customers8 rows4
    7 columns
    idbigint not null
    namenvarchar(200) not null
    emailnvarchar(150) not null
    countrynvarchar(2) not null
    is_activebit not null
    signed_update
    created_atdatetimeoffset(0) not null
  • orders10 rows6
    9 columns
    idbigint not null
    customer_idbigint not null
    statusnvarchar(9) not null
    totaldecimal(10,2) not null
    itemsint not null
    ordered_atdatetime2(0) not null
    shipped_atdatetime2(0)
    notenvarchar(510)
    updated_atdatetimeoffset(0) not null
-- Written by IntoSQL from a MySQL dump. Load it with: sqlcmd -b -f 65001 -d <database> -i <file>
SET NOCOUNT ON;
SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
SET ANSI_PADDING ON;
SET ANSI_WARNINGS ON;
SET ARITHABORT ON;
SET CONCAT_NULL_YIELDS_NULL ON;
SET NUMERIC_ROUNDABORT OFF;
GO

CREATE TABLE [dbo].[customers] (
    [id] bigint IDENTITY(1001, 1) NOT NULL,
    [name] nvarchar(200) NOT NULL,
    [email] nvarchar(150) NOT NULL,
    [country] nvarchar(2) DEFAULT N'US' NOT NULL,
    [is_active] bit DEFAULT 1 NOT NULL,
    [signed_up] date NULL,
    [created_at] datetimeoffset(0) DEFAULT SYSDATETIMEOFFSET() NOT NULL,
    CONSTRAINT [CK_customers_id] CHECK ([id] >= 0),
    CONSTRAINT [PK_customers] PRIMARY KEY ([id])
);
GO

CREATE TABLE [dbo].[orders] (
    [id] bigint IDENTITY(5001, 1) NOT NULL,
    [customer_id] bigint NOT NULL,
    [status] nvarchar(9) DEFAULT N'pending' NOT NULL,
    [total] decimal(10,2) NOT NULL,
    [items] int DEFAULT 1 NOT NULL,
    [ordered_at] datetime2(0) NOT NULL,
    [shipped_at] datetime2(0) NULL,
    [note] nvarchar(510) NULL,
    [updated_at] datetimeoffset(0) DEFAULT SYSDATETIMEOFFSET() NOT NULL,
    CONSTRAINT [CK_orders_id] CHECK ([id] >= 0),
    CONSTRAINT [CK_orders_customer_id] CHECK ([customer_id] >= 0),
    CONSTRAINT [CK_orders_status] CHECK ([status] IN (N'pending', N'paid', N'shipped', N'cancelled', N'')),
    CONSTRAINT [CK_orders_items] CHECK ([items] >= 0),
    CONSTRAINT [PK_orders] PRIMARY KEY ([id])
);
GO

SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(1,N'Ana Martínez',N'ana@example.com',N'MX',1,'2023-01-15','2023-01-15 10:24:00+00:00'),
(2,N'Zoë Müller',N'zoe@example.de',N'DE',1,'2023-02-03','2023-02-03 08:11:42+00:00'),
(3,N'José Peña',N'jose@example.es',N'ES',0,'2023-03-21','2023-03-21 19:05:17+00:00'),
(4,N'李雷',N'lilei@example.cn',N'CN',1,'2023-05-09','2023-05-09 02:40:00+00:00'),
(5,N'Noah O''Brien',N'noah@example.ie',N'IE',1,NULL,'2023-06-30 14:00:00+00:00'),
(6,N'Amélie Laurent',N'amelie@example.fr',N'FR',1,'2024-01-02','2024-01-02 09:30:00+00:00'),
(7,N'Ravi Patel',N'ravi@example.in',N'IN',0,'2024-02-29','2024-02-29 23:59:59+00:00'),
(8,N'Sofia Rossi 🌻',N'sofia@example.it',N'IT',1,'2024-04-12','2024-04-12 12:12:12+00:00');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [updated_at]) VALUES
(1,1,N'shipped',129.90,3,'2024-03-01 10:00:00','2024-03-02 16:30:00',NULL,'2024-03-02 16:30:00+00:00'),
(2,2,N'paid',54.00,1,'2024-03-02 11:15:00',NULL,N'Gift wrap 🎁','2024-03-02 11:20:00+00:00'),
(3,1,N'cancelled',12.50,1,'2024-03-03 09:45:00',NULL,N'Customer changed their mind','2024-03-03 10:00:00+00:00'),
(4,4,N'shipped',980.00,12,'2024-03-05 02:10:00','2024-03-07 08:00:00',N'请小心轻放','2024-03-07 08:00:00+00:00'),
(5,5,N'pending',45.75,2,'2024-03-08 17:20:00',NULL,N'Leave at the door; ring twice','2024-03-08 17:20:00+00:00'),
(6,6,N'shipped',310.40,5,'2024-03-10 13:00:00','2024-03-11 09:00:00',N'Fragile: "glass"','2024-03-11 09:00:00+00:00'),
(7,8,N'paid',76.20,2,'2024-03-11 18:45:00',NULL,NULL,'2024-03-11 18:46:00+00:00'),
(8,2,N'',19.99,1,'2024-03-12 08:00:00',NULL,N'Status lost in an old import','2024-03-12 08:00:00+00:00'),
(9,3,N'shipped',64.00,2,'2024-03-14 15:30:00',NULL,N'Shipping date never recorded','2024-03-15 10:00:00+00:00'),
(10,7,N'paid',1499.00,1,'2024-03-15 21:05:00',NULL,N'C:\Users\ravi\invoice.pdf','2024-03-15 21:05:00+00:00');
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO

-- Indexes, foreign keys, counters and triggers, after the rows.
CREATE UNIQUE INDEX [uq_email] ON [dbo].[customers] ([email]);
CREATE INDEX [idx_customer] ON [dbo].[orders] ([customer_id]);
CREATE INDEX [idx_status_date] ON [dbo].[orders] ([status], [ordered_at]);
ALTER TABLE [dbo].[orders] WITH NOCHECK ADD CONSTRAINT [fk_orders_customer] FOREIGN KEY ([customer_id]) REFERENCES [dbo].[customers] ([id]);
BEGIN TRY ALTER TABLE [dbo].[orders] WITH CHECK CHECK CONSTRAINT [fk_orders_customer]; END TRY BEGIN CATCH PRINT N'Foreign key ' + N'[fk_orders_customer]' + N' has rows without a parent: it stays unchecked, and new rows are checked.'; END CATCH;
DECLARE @next decimal(38,0) = (SELECT MAX([id]) FROM [dbo].[customers]); IF @next IS NOT NULL BEGIN IF @next < 1000 SET @next = 1000; DBCC CHECKIDENT (N'[dbo].[customers]', RESEED, @next) WITH NO_INFOMSGS; END;
GO
EXEC sys.sp_addextendedproperty @name = N'MS_Description', @value = N'People who bought at least once', @level0type = N'SCHEMA', @level0name = N'dbo', @level1type = N'TABLE', @level1name = N'customers';
DECLARE @next decimal(38,0) = (SELECT MAX([id]) FROM [dbo].[orders]); IF @next IS NOT NULL BEGIN IF @next < 5000 SET @next = 5000; DBCC CHECKIDENT (N'[dbo].[orders]', RESEED, @next) WITH NO_INFOMSGS; END;
GO
GO
CREATE TRIGGER [dbo].[TR_orders_updated_at_on_update] ON [dbo].[orders] AFTER UPDATE AS
BEGIN
    SET NOCOUNT ON;
    IF NOT UPDATE([updated_at])
        UPDATE x SET [updated_at] = SYSDATETIMEOFFSET() FROM [dbo].[orders] AS x JOIN inserted AS i ON x.[id] = i.[id];
END;
GO
GO

Fixes

  1. "id" of customers is unsigned, so it became bigint with a check that it isn't negative.
  2. "name" of customers holds characters SQL Server counts twice, such as emoji, so it is nvarchar(200).
  3. "signed_up" of customers allows NULL now: some of its values couldn't be stored and are NULL.
  4. The default of "signed_up" in customers, 0000‑00‑00, isn't a date SQL Server accepts, so the column has no default.
  5. "customer_id" of orders is unsigned, so it became bigint with a check that it isn't negative.
  6. "id" of orders is unsigned, so it became bigint with a check that it isn't negative.
  7. "items" of orders is unsigned, so it became int with a check that it isn't negative.
  8. "note" of orders holds characters SQL Server counts twice, such as emoji, so it is nvarchar(510).
  9. 1 row of "status" in orders holds the empty value MySQL stores for invalid entries; the check of its values allows it.
  10. "updated_at" of orders changes to the current time on every update; a trigger does that in SQL Server.

Needs a look

  • 1 value in "signed_up" of customers is a zero or impossible date such as 0000‑00‑00, which SQL Server can't store, so it is NULL. If those dates mean something, replace them in the source with real ones before dumping.
  • 1 value in "shipped_at" of orders is a zero or impossible date such as 0000‑00‑00 00:00:00, which SQL Server can't store, so it is NULL. If those dates mean something, replace them in the source with real ones before dumping.

Your file is converted on this computer and never uploaded.

Other samples:

What changes from MySQL to SQL Server

Each row is a rule the converter applies, with the report line it writes when the rule changes something in your data.

  • TINYINT(1), BOOL

    bit when every value is 0 or 1, else smallint

    MySQL's BOOL is a TINYINT(1) that can hold any number from -128 to 127. A column that only holds 0 and 1 becomes bit; one that holds 2 or -1 stays a number so no value changes meaning.

    Report line: bool_kept_numeric

  • UNSIGNED integers

    TINYINT UNSIGNED as tinyint; the others the next wider integer with a CHECK that it isn't negative, BIGINT UNSIGNED as bigint when every value fits, else decimal(38,0)

    SQL Server has no unsigned integers except tinyint, which is 0 to 255. A wider type holds the whole unsigned range, and the check keeps negative values out as MySQL did.

    Report line: unsigned_widened

  • ZEROFILL and display widths

    dropped

    Zero padding is a way of showing a number, not part of it. The numbers are kept; format them when you read them if the padding matters.

    Report line: zerofill_dropped

  • FLOAT(M,D)

    real or float, without the scale

    SQL Server floats have no fixed scale. The stored values are copied as they are.

    Report line: float_scale_dropped

  • DECIMAL(p,s)

    decimal(p,s) up to 38 digits; nvarchar beyond

    SQL Server's decimal holds 38 digits. Wider decimals are kept as text so every digit stays exact.

    Report line: decimal_as_text

  • BIT(1), BIT(n)

    bit; bigint up to 63 bits; decimal(20,0) for BIT(64)

    A single bit is a yes or no; wider bit fields are stored as the number their bits make.

  • zero, partial or impossible dates (0000-00-00, 2024-02-30)

    NULL

    SQL Server only stores real calendar dates. MySQL keeps zero and impossible dates when its SQL mode allows them; they become NULL and the report counts them.

    Report line: invalid_date_null

  • NOT NULL columns with values that become NULL

    NOT NULL dropped

    A column whose values had to become NULL can't keep NOT NULL, or the load would stop.

    Report line: not_null_relaxed

  • DEFAULT '0000-00-00'

    no default

    A default SQL Server can't store is left out rather than replaced with a made-up date.

    Report line: zero_date_default

  • TIME

    time when every value is within a day, else nvarchar

    MySQL's TIME is a duration from -838:59:59 to 838:59:59; SQL Server's time is a time of day. A column with longer or negative values keeps them as text.

    Report line: time_as_interval

  • YEAR

    smallint with a CHECK of its range

    SQL Server has no year type; the check keeps the same values MySQL allows.

  • CHAR(n), VARCHAR(n)

    nvarchar(n)

    nvarchar holds every character on every supported version, and MySQL returns CHAR values without trailing spaces, as nvarchar keeps them.

  • text with characters past U+FFFF (emoji) in VARCHAR(n)

    nvarchar(2n), or nvarchar(max) past 4000

    nvarchar counts UTF-16 units and an emoji takes two, so the column gets room for every value.

    Report line: text_length_widened

  • DATETIME

    datetime2

    A DATETIME is a wall-clock reading without a time zone, like datetime2.

  • TIMESTAMP

    datetimeoffset in the time zone the dump states, UTC when it states none

    A TIMESTAMP is a moment. Dumps state the zone they wrote it in (mysqldump writes UTC); without that, the values are read as UTC and the report says so.

    Report line: timestamp_zone_assumed

  • ENUM

    nvarchar with a CHECK of its values

    Text with a check keeps the same values.

  • ENUM holding '' (MySQL's error value)

    the empty value allowed by the check

    MySQL stores an empty string when an invalid value goes into an ENUM in a lenient SQL mode. Those rows are kept and the check allows them.

    Report line: enum_invalid_value

  • ENUM holding values outside its list

    no CHECK

    Values the list doesn't name would fail the check, so the check isn't created.

    Report line: enum_check_skipped

  • SET

    nvarchar as MySQL writes it ('a,b')

    SQL Server has no arrays; the text is the one MySQL returns.

  • JSON

    nvarchar(max)

    SQL Server's JSON functions read JSON from text. An ISJSON check would refuse scalar values such as 1 or "x" before SQL Server 2022, which MySQL keeps.

  • NUL bytes in text

    kept

    nvarchar can hold a NUL character, so the value is copied as it is.

  • text that isn't valid in its character set

    the bad bytes as the replacement character

    Text has to be read as characters to be written as nvarchar.

    Report line: invalid_utf8_replaced

  • text decoded twice (ñ)

    copied as it is

    The dump holds exactly these characters, so they are copied; the report points them out.

    Report line: mojibake_suspected

  • AUTO_INCREMENT

    IDENTITY, with the counter set past the copied ids

    The copied ids load with IDENTITY_INSERT, and the counter continues after the highest one or the dump's AUTO_INCREMENT, whichever is higher.

  • MyISAM AUTO_INCREMENT on the second column of a key

    one counter for the table

    MyISAM counts per group of the key's first column; SQL Server has one counter per table.

    Report line: myisam_group_autoincrement

  • ON UPDATE CURRENT_TIMESTAMP

    an AFTER UPDATE trigger

    The trigger sets the column to the current time when an update doesn't set it. It joins on the primary key, so the table needs one.

    Report line: on_update_trigger

  • ON UPDATE CURRENT_TIMESTAMP on a table without a primary key

    not carried

    The trigger finds the updated rows by their primary key; without one it can't.

    Report line: on_update_without_key

  • generated columns

    computed columns, PERSISTED when the source stores them

    SQL Server computes them the same two ways.

  • generated columns with functions SQL Server lacks

    a plain column

    A formula that can't be written for SQL Server leaves a plain column and a report line.

    Report line: generated_not_translated

  • INVISIBLE columns

    regular columns

    SQL Server has no invisible columns.

    Report line: invisible_column

  • CHECK constraints

    translated, inline in CREATE TABLE

    Inline checks are tested as each row loads.

  • CHECK constraints with functions SQL Server lacks

    not created

    A check that can't be written for SQL Server is left out and reported.

    Report line: check_skipped

  • CHECK ... NOT ENFORCED

    not created

    MySQL keeps these checks without applying them, so the rows may break them.

    Report line: check_not_enforced

  • defaults with functions SQL Server lacks

    no default

    A default that can't be written for SQL Server is left out and reported.

    Report line: default_not_translated

  • keys and indexes on a prefix, c(20)

    an index on the whole column

    SQL Server indexes whole columns; a key unique on a prefix is unique on the whole column too, so every row loads.

    Report line: prefix_whole_column

  • FULLTEXT indexes

    not created

    SQL Server's full-text search needs a catalog and its own queries.

    Report line: fulltext_not_created

  • SPATIAL indexes

    not created

    A spatial index on a geometry column needs a bounding box the dump doesn't state.

    Report line: spatial_index_not_created

  • functional indexes, and indexes on columns of unlimited length

    not created

    SQL Server indexes neither expressions nor nvarchar(max) and varbinary(max) columns.

    Report line: index_skipped

  • foreign keys

    after the data WITH NOCHECK, then checked with a warning on orphans

    Dumps load with FOREIGN_KEY_CHECKS=0 and MyISAM never checks, so rows without a parent are common. The key is created first and checked last; orphans leave it unchecked with a warning instead of stopping the load.

  • ON DELETE or ON UPDATE that cascades in a cycle or along a second path

    NO ACTION

    SQL Server refuses cascades that could reach a table twice; MySQL allows them.

    Report line: foreign_key_action_dropped

  • foreign keys to columns without a unique key

    not created

    SQL Server needs a primary key or a unique index without NULLs on the referenced columns; InnoDB doesn't.

    Report line: foreign_key_skipped

  • table and column names

    as written; renamed when they differ only in case

    SQL Server names ignore case under the usual collation.

    Report line: identifier_renamed

  • names over 116 characters

    shortened

    SQL Server names hold 128 characters, some of them for the numbers that keep names apart.

    Report line: name_shortened

  • GEOMETRY, POINT, POLYGON...

    geometry, with each value's SRID

    SQL Server's own geometry type keeps every shape and its spatial reference system.

  • INET4, INET6, UUID (MariaDB)

    nvarchar(39), uniqueidentifier

    uniqueidentifier is SQL Server's UUID; addresses are kept as text.

  • BINARY, VARBINARY, BLOB

    binary, varbinary, varbinary(max)

    Bytes stay bytes.

  • PARTITION BY

    one table

    The rows are the same; partition the table again in SQL Server if it needs it.

    Report line: partitions_dropped

  • WITH SYSTEM VERSIONING (MariaDB)

    a table with the current rows

    A dump holds the current rows only.

    Report line: system_versioning_dropped

  • MyISAM, MEMORY and other engines

    a regular table

    SQL Server has one table engine.

    Report line: engine_dropped

  • views, triggers, procedures, functions and events

    listed in the report

    Their code is written in MySQL's language, so they are listed for you to recreate.

    Report line: not_converted_objects

  • dumps without CREATE TABLE

    types read from the values

    A dump made with --no-create-info only has rows, so the types come from the values.

    Report line: no_schema

  • geometries in a dump without CREATE TABLE

    geometry

    MySQL's geometry format is recognised from the values.

  • _ci collations

    the database's collation, usually case-insensitive

    SQL Server compares text without case under its usual collation, as MySQL's _ci does.

  • SET NAMES utf8 from utf8mb4 tables

    converted, with a warning

    MySQL 5.x mysqldump writes 3-byte utf8 by default and replaces every emoji with a question mark before any converter sees the file.

    Report line: dump_lost_four_byte_chars

  • dumps without SET NAMES whose text isn't UTF-8

    read as Latin-1 (Windows-1252)

    MySQL's latin1 is Windows-1252, the usual encoding of such dumps.

    Report line: charset_guessed

  • sjis, cp932, gbk, big5 dumps

    read as UTF-8, with a warning

    These encodings are not decoded yet.

    Report line: unsupported_charset

  • UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows

    evaluated

    Some dump tools write values as these calls; they are read as the values they produce, with geographic WKT turned to longitude first.

  • other expressions in rows

    NULL

    A converter that doesn't run a database can't evaluate them.

    Report line: expression_value

  • DEFAULT in VALUES

    the column's default value

    A literal default is copied into the row.

    Report line: default_keyword_filled

  • DEFAULT in VALUES without a literal default

    NULL

    There is no value to copy.

    Report line: default_keyword_null

  • REPLACE and INSERT IGNORE

    plain rows

    A dump has one row per key, so the rows are the same.

    Report line: replace_as_insert

  • types IntoSQL doesn't know

    nvarchar(max)

    Text holds any value; change the type after loading.

    Report line: unknown_type

  • values that don't fit their column

    NULL

    A damaged or hand-written dump can hold values its column can't; they become NULL so the rest loads.

    Report line: invalid_value_null

  • unreadable geometries

    NULL

    Bytes that aren't a geometry can't be stored as one.

    Report line: bad_geometry

  • CREATE SEQUENCE (MariaDB)

    CREATE SEQUENCE ... AS bigint, at its next value

    SQL Server has sequences with the same options; each one continues where the dump left it.

  • VECTOR(n) (MariaDB 11.7+)

    nvarchar(max) with the floats as a JSON array

    Only SQL Server 2025 has a vector type; a JSON array keeps every float on every version.

    Report line: type_replaced

In MySQLIn SQL ServerReport line
TINYINT(1), BOOLbit when every value is 0 or 1, else smallintMySQL's BOOL is a TINYINT(1) that can hold any number from -128 to 127. A column that only holds 0 and 1 becomes bit; one that holds 2 or -1 stays a number so no value changes meaning.bool_kept_numeric
UNSIGNED integersTINYINT UNSIGNED as tinyint; the others the next wider integer with a CHECK that it isn't negative, BIGINT UNSIGNED as bigint when every value fits, else decimal(38,0)SQL Server has no unsigned integers except tinyint, which is 0 to 255. A wider type holds the whole unsigned range, and the check keeps negative values out as MySQL did.unsigned_widened
ZEROFILL and display widthsdroppedZero padding is a way of showing a number, not part of it. The numbers are kept; format them when you read them if the padding matters.zerofill_dropped
FLOAT(M,D)real or float, without the scaleSQL Server floats have no fixed scale. The stored values are copied as they are.float_scale_dropped
DECIMAL(p,s)decimal(p,s) up to 38 digits; nvarchar beyondSQL Server's decimal holds 38 digits. Wider decimals are kept as text so every digit stays exact.decimal_as_text
BIT(1), BIT(n)bit; bigint up to 63 bits; decimal(20,0) for BIT(64)A single bit is a yes or no; wider bit fields are stored as the number their bits make.
zero, partial or impossible dates (0000-00-00, 2024-02-30)NULLSQL Server only stores real calendar dates. MySQL keeps zero and impossible dates when its SQL mode allows them; they become NULL and the report counts them.invalid_date_null
NOT NULL columns with values that become NULLNOT NULL droppedA column whose values had to become NULL can't keep NOT NULL, or the load would stop.not_null_relaxed
DEFAULT '0000-00-00'no defaultA default SQL Server can't store is left out rather than replaced with a made-up date.zero_date_default
TIMEtime when every value is within a day, else nvarcharMySQL's TIME is a duration from -838:59:59 to 838:59:59; SQL Server's time is a time of day. A column with longer or negative values keeps them as text.time_as_interval
YEARsmallint with a CHECK of its rangeSQL Server has no year type; the check keeps the same values MySQL allows.
CHAR(n), VARCHAR(n)nvarchar(n)nvarchar holds every character on every supported version, and MySQL returns CHAR values without trailing spaces, as nvarchar keeps them.
text with characters past U+FFFF (emoji) in VARCHAR(n)nvarchar(2n), or nvarchar(max) past 4000nvarchar counts UTF-16 units and an emoji takes two, so the column gets room for every value.text_length_widened
DATETIMEdatetime2A DATETIME is a wall-clock reading without a time zone, like datetime2.
TIMESTAMPdatetimeoffset in the time zone the dump states, UTC when it states noneA TIMESTAMP is a moment. Dumps state the zone they wrote it in (mysqldump writes UTC); without that, the values are read as UTC and the report says so.timestamp_zone_assumed
ENUMnvarchar with a CHECK of its valuesText with a check keeps the same values.
ENUM holding '' (MySQL's error value)the empty value allowed by the checkMySQL stores an empty string when an invalid value goes into an ENUM in a lenient SQL mode. Those rows are kept and the check allows them.enum_invalid_value
ENUM holding values outside its listno CHECKValues the list doesn't name would fail the check, so the check isn't created.enum_check_skipped
SETnvarchar as MySQL writes it ('a,b')SQL Server has no arrays; the text is the one MySQL returns.
JSONnvarchar(max)SQL Server's JSON functions read JSON from text. An ISJSON check would refuse scalar values such as 1 or "x" before SQL Server 2022, which MySQL keeps.
NUL bytes in textkeptnvarchar can hold a NUL character, so the value is copied as it is.
text that isn't valid in its character setthe bad bytes as the replacement characterText has to be read as characters to be written as nvarchar.invalid_utf8_replaced
text decoded twice (ñ)copied as it isThe dump holds exactly these characters, so they are copied; the report points them out.mojibake_suspected
AUTO_INCREMENTIDENTITY, with the counter set past the copied idsThe copied ids load with IDENTITY_INSERT, and the counter continues after the highest one or the dump's AUTO_INCREMENT, whichever is higher.
MyISAM AUTO_INCREMENT on the second column of a keyone counter for the tableMyISAM counts per group of the key's first column; SQL Server has one counter per table.myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMPan AFTER UPDATE triggerThe trigger sets the column to the current time when an update doesn't set it. It joins on the primary key, so the table needs one.on_update_trigger
ON UPDATE CURRENT_TIMESTAMP on a table without a primary keynot carriedThe trigger finds the updated rows by their primary key; without one it can't.on_update_without_key
generated columnscomputed columns, PERSISTED when the source stores themSQL Server computes them the same two ways.
generated columns with functions SQL Server lacksa plain columnA formula that can't be written for SQL Server leaves a plain column and a report line.generated_not_translated
INVISIBLE columnsregular columnsSQL Server has no invisible columns.invisible_column
CHECK constraintstranslated, inline in CREATE TABLEInline checks are tested as each row loads.
CHECK constraints with functions SQL Server lacksnot createdA check that can't be written for SQL Server is left out and reported.check_skipped
CHECK ... NOT ENFORCEDnot createdMySQL keeps these checks without applying them, so the rows may break them.check_not_enforced
defaults with functions SQL Server lacksno defaultA default that can't be written for SQL Server is left out and reported.default_not_translated
keys and indexes on a prefix, c(20)an index on the whole columnSQL Server indexes whole columns; a key unique on a prefix is unique on the whole column too, so every row loads.prefix_whole_column
FULLTEXT indexesnot createdSQL Server's full-text search needs a catalog and its own queries.fulltext_not_created
SPATIAL indexesnot createdA spatial index on a geometry column needs a bounding box the dump doesn't state.spatial_index_not_created
functional indexes, and indexes on columns of unlimited lengthnot createdSQL Server indexes neither expressions nor nvarchar(max) and varbinary(max) columns.index_skipped
foreign keysafter the data WITH NOCHECK, then checked with a warning on orphansDumps load with FOREIGN_KEY_CHECKS=0 and MyISAM never checks, so rows without a parent are common. The key is created first and checked last; orphans leave it unchecked with a warning instead of stopping the load.
ON DELETE or ON UPDATE that cascades in a cycle or along a second pathNO ACTIONSQL Server refuses cascades that could reach a table twice; MySQL allows them.foreign_key_action_dropped
foreign keys to columns without a unique keynot createdSQL Server needs a primary key or a unique index without NULLs on the referenced columns; InnoDB doesn't.foreign_key_skipped
table and column namesas written; renamed when they differ only in caseSQL Server names ignore case under the usual collation.identifier_renamed
names over 116 charactersshortenedSQL Server names hold 128 characters, some of them for the numbers that keep names apart.name_shortened
GEOMETRY, POINT, POLYGON...geometry, with each value's SRIDSQL Server's own geometry type keeps every shape and its spatial reference system.
INET4, INET6, UUID (MariaDB)nvarchar(39), uniqueidentifieruniqueidentifier is SQL Server's UUID; addresses are kept as text.
BINARY, VARBINARY, BLOBbinary, varbinary, varbinary(max)Bytes stay bytes.
PARTITION BYone tableThe rows are the same; partition the table again in SQL Server if it needs it.partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)a table with the current rowsA dump holds the current rows only.system_versioning_dropped
MyISAM, MEMORY and other enginesa regular tableSQL Server has one table engine.engine_dropped
views, triggers, procedures, functions and eventslisted in the reportTheir code is written in MySQL's language, so they are listed for you to recreate.not_converted_objects
dumps without CREATE TABLEtypes read from the valuesA dump made with --no-create-info only has rows, so the types come from the values.no_schema
geometries in a dump without CREATE TABLEgeometryMySQL's geometry format is recognised from the values.
_ci collationsthe database's collation, usually case-insensitiveSQL Server compares text without case under its usual collation, as MySQL's _ci does.
SET NAMES utf8 from utf8mb4 tablesconverted, with a warningMySQL 5.x mysqldump writes 3-byte utf8 by default and replaces every emoji with a question mark before any converter sees the file.dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8read as Latin-1 (Windows-1252)MySQL's latin1 is Windows-1252, the usual encoding of such dumps.charset_guessed
sjis, cp932, gbk, big5 dumpsread as UTF-8, with a warningThese encodings are not decoded yet.unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rowsevaluatedSome dump tools write values as these calls; they are read as the values they produce, with geographic WKT turned to longitude first.
other expressions in rowsNULLA converter that doesn't run a database can't evaluate them.expression_value
DEFAULT in VALUESthe column's default valueA literal default is copied into the row.default_keyword_filled
DEFAULT in VALUES without a literal defaultNULLThere is no value to copy.default_keyword_null
REPLACE and INSERT IGNOREplain rowsA dump has one row per key, so the rows are the same.replace_as_insert
types IntoSQL doesn't knownvarchar(max)Text holds any value; change the type after loading.unknown_type
values that don't fit their columnNULLA damaged or hand-written dump can hold values its column can't; they become NULL so the rest loads.invalid_value_null
unreadable geometriesNULLBytes that aren't a geometry can't be stored as one.bad_geometry
CREATE SEQUENCE (MariaDB)CREATE SEQUENCE ... AS bigint, at its next valueSQL Server has sequences with the same options; each one continues where the dump left it.
VECTOR(n) (MariaDB 11.7+)nvarchar(max) with the floats as a JSON arrayOnly SQL Server 2025 has a vector type; a JSON array keeps every float on every version.type_replaced

Load the script into SQL Server

The script creates the tables, copies the rows, then adds keys, indexes and foreign keys, so it loads fast and stops at the first error.

sqlcmd -b -f 65001 -S localhost -d mydb -i mysql.sql_server.sql

Foreign keys are added WITH NOCHECK and then checked. If a row points to a row that doesn't exist, the script says which constraint and leaves it unchecked, instead of failing half way.

For a large dump, run sqlcmd on the same machine as the server. Each batch of up to 1000 rows commits on its own, so the transaction log stays small.

Dump MySQL for the best result

The converter reads what the dump holds. These options keep more of your data in it:

  • --default-character-set=utf8mb4 keeps emoji and other 4-byte characters, which a utf8 dump loses before the converter sees it.
  • --hex-blob writes binary columns as hexadecimal, which survives copying between systems.
  • --routines --triggers --events add those objects to the dump, so the report can list them.

What isn't converted

Views, triggers, stored procedures, functions and events are written in each database's own language. They are listed in the report by name, never dropped silently, so you can rewrite them for SQL Server.

Questions about MySQL to SQL Server

Not answered here? Write to us from the contact page and a person replies. Contact page

Is my dump uploaded?

No. The converter runs in your browser and reads the dump from your disk in pieces, so it never leaves your computer. The desktop app and the command line convert on your computer too.

How large can the dump be?

The converter never holds the whole dump in memory: it reads the file twice, by ranges, and writes the script as it goes. Multi-gigabyte dumps convert in the browser, in the desktop app and with the command line.

Does it read MariaDB dumps?

Yes. Dumps from mariadb-dump and from MariaDB 10.3 to 12 convert the same way, including MariaDB's INET6, INET4 and UUID columns.

What happens to 0000-00-00 dates?

SQL Server has no zero date, so they become NULL and the column accepts NULL. The report says how many there were in each column.

Are AUTO_INCREMENT values kept?

Yes. Each AUTO_INCREMENT column becomes an IDENTITY column, every row keeps its id, and the next id continues after the highest one, or after the table's AUTO_INCREMENT if that is higher.

Does TINYINT(1) become boolean?

It becomes bit only when every value in the column is 0 or 1. If it holds other numbers, such as 2 or -1, it stays a number and the report says why, so no value turns into 1 by accident.

What about stored procedures and triggers?

They are listed in the report by name and not converted, because each database writes them in its own language. ON UPDATE CURRENT_TIMESTAMP is the exception: it becomes a SQL Server trigger.

Which SQL Server versions does the script load on?

SQL Server 2017 to 2025. The same script loads on each of them, stopping at the first error if anything goes wrong.