将 MariaDB 转换为 SQL Server
拖入由 mariadb-dump、mysqldump、phpMyAdmin 或 Adminer 生成的转储(.sql 或 .sql.gz),即可下载包含相同表、键、索引和数据的 SQL Server 脚本。SQL Server 无法原样存储的每个值,以及它不支持的每个对象,都会在报告中列出。
已转换 18 行,10 处修正,无拒绝
shop_2024_mariadb.sql 中的表MariaDB 11.8.9-MariaDB-ubu2404 转储 · 由 mariadb-dump 写出 · utf8mb4
customers8 行4
8 列 id bigint not null name nvarchar(200) not null email nvarchar(150) not null country nvarchar(2) not null is_active bit not null signed_up date created_at datetimeoffset(0) not null last_ip nvarchar(39) orders10 行6
12 列 id bigint not null customer_id bigint not null status nvarchar(9) not null total decimal(10,2) not null items int not null ordered_at datetime2(0) not null shipped_at datetime2(0) note nvarchar(510) order_ref uniqueidentifier not null invoice int not null details nvarchar(max) updated_at datetimeoffset(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 SEQUENCE [dbo].[invoice_numbers] AS bigint START WITH 2000 INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775806;
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,
[last_ip] nvarchar(39) 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,
[order_ref] uniqueidentifier NOT NULL,
[invoice] int DEFAULT (NEXT VALUE FOR [dbo].[invoice_numbers]) NOT NULL,
[details] nvarchar(max) 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], [last_ip]) VALUES
(1,N'Ana Martínez',N'ana@example.com',N'MX',1,'2023-01-15','2023-01-15 10:24:00+00:00',N'::ffff:203.0.113.7'),
(2,N'Zoë Müller',N'zoe@example.de',N'DE',1,'2023-02-03','2023-02-03 08:11:42+00:00',N'2001:db8::1'),
(3,N'José Peña',N'jose@example.es',N'ES',0,'2023-03-21','2023-03-21 19:05:17+00:00',NULL),
(4,N'李雷',N'lilei@example.cn',N'CN',1,'2023-05-09','2023-05-09 02:40:00+00:00',N'::ffff:198.51.100.23'),
(5,N'Noah O''Brien',N'noah@example.ie',N'IE',1,NULL,'2023-06-30 14:00:00+00:00',NULL),
(6,N'Amélie Laurent',N'amelie@example.fr',N'FR',1,'2024-01-02','2024-01-02 09:30:00+00:00',N'2001:db8:85a3::8a2e:370:7334'),
(7,N'Ravi Patel',N'ravi@example.in',N'IN',0,'2024-02-29','2024-02-29 23:59:59+00:00',N'::ffff:192.0.2.200'),
(8,N'Sofia Rossi 🌻',N'sofia@example.it',N'IT',1,'2024-04-12','2024-04-12 12:12:12+00:00',NULL);
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], [order_ref], [invoice], [details], [updated_at]) VALUES
(1,1,N'shipped',129.90,3,'2024-03-01 10:00:00','2024-03-02 16:30:00',NULL,N'0b7a4c2e-3f1d-4e8a-9c55-1a2b3c4d5e6f',1000,N'{"gift": false}','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 🎁',N'1c8b5d3f-4a2e-4f9b-8d66-2b3c4d5e6f70',1001,N'{"gift": true, "wrap": "red"}','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',N'2d9c6e4a-5b3f-4a0c-9e77-3c4d5e6f7081',1002,NULL,'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'请小心轻放',N'3e0d7f5b-6c4a-4b1d-8f88-4d5e6f708192',1003,N'{"fragile": true}','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',N'4f1e806c-7d5b-4c2e-9a99-5e6f708192a3',1004,NULL,'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"',N'50f2917d-8e6c-4d3f-8baa-6f708192a3b4',1005,N'{"fragile": true}','2024-03-11 09:00:00+00:00'),
(7,8,N'paid',76.20,2,'2024-03-11 18:45:00',NULL,NULL,N'6103a28e-9f7d-4e40-9cbb-708192a3b4c5',1006,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',N'7214b39f-a08e-4f51-8dcc-8192a3b4c5d6',1007,NULL,'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',N'8325c4a0-b19f-4062-9edd-92a3b4c5d6e7',1008,N'{"parcels": [1, 2]}','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',N'9436d5b1-c2a0-4173-8fee-a3b4c5d6e7f8',1009,NULL,'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
修正
- customers 的 "id" 是无符号的,因此变为 bigint,并带有不能为负数的检查约束。
- customers 的 "name" 包含 SQL Server 按两个计数的字符(例如 emoji),因此为 nvarchar(200)。
- customers 的 "signed_up" 现在允许 NULL:它的部分值无法存储,已设为 NULL。
- customers 中 "signed_up" 的默认值 0000‑00‑00 不是 SQL Server 接受的日期,因此该列没有默认值。
- orders 的 "customer_id" 是无符号的,因此变为 bigint,并带有不能为负数的检查约束。
- orders 的 "id" 是无符号的,因此变为 bigint,并带有不能为负数的检查约束。
- orders 的 "items" 是无符号的,因此变为 int,并带有不能为负数的检查约束。
- orders 的 "note" 包含 SQL Server 按两个计数的字符(例如 emoji),因此为 nvarchar(510)。
- orders 中 "status" 有 1 行包含 MySQL 为无效输入存储的空值;对其值的检查约束允许该值。
- orders 的 "updated_at" 在每次更新时变为当前时间;在 SQL Server 中由触发器完成。
需要检查
- customers 的 "signed_up" 中有 1 个值是零日期或不可能的日期,例如 0000‑00‑00,SQL Server 无法存储,因此设为 NULL。 如果这些日期有意义,请在转储前于源数据中替换为真实日期。
- orders 的 "shipped_at" 中有 1 个值是零日期或不可能的日期,例如 0000‑00‑00 00:00:00,SQL Server 无法存储,因此设为 NULL。 如果这些日期有意义,请在转储前于源数据中替换为真实日期。
文件在这台电脑上转换,永远不会上传。
其他示例:
从 MariaDB 到 SQL Server 的变化
每一行是转换器应用的一条规则,以及该规则更改你的数据时在报告中写入的记录。
TINYINT(1), BOOL
所有值都是 0 或 1 时为 bit,否则为 smallint
MySQL 的 BOOL 是 TINYINT(1),可以存储 -128 到 127 之间的任何数字。只包含 0 和 1 的列变为 bit;包含 2 或 -1 的列保持为数字,因此没有值会改变含义。
报告记录: bool_kept_numeric
UNSIGNED integers
TINYINT UNSIGNED 为 tinyint;其他类型为更宽一级的整数,并带有不能为负数的 CHECK,所有值都能容纳时 BIGINT UNSIGNED 为 bigint,否则为 decimal(38,0)
SQL Server 除了范围为 0 到 255 的 tinyint 之外没有无符号整数。更宽的类型能容纳整个无符号范围,检查约束像 MySQL 一样阻止负值。
报告记录: unsigned_widened
ZEROFILL and display widths
去除
补零只是数字的显示方式,不是数字的一部分。数字会保留;如果需要补零,请在读取时格式化。
报告记录: zerofill_dropped
FLOAT(M,D)
real 或 float,不带小数位数
SQL Server 的浮点数没有固定的小数位数。已存储的值按原样复制。
报告记录: float_scale_dropped
DECIMAL(p,s)
不超过 38 位时为 decimal(p,s);超过则为 nvarchar
SQL Server 的 decimal 可容纳 38 位数字。更宽的小数保存为文本,使每一位都保持精确。
报告记录: decimal_as_text
BIT(1), BIT(n)
bit;不超过 63 位时为 bigint;BIT(64) 为 decimal(20,0)
单个位表示是或否;更宽的位字段存储为其各位组成的数字。
zero, partial or impossible dates (0000-00-00, 2024-02-30)
NULL
SQL Server 只存储真实的日历日期。当 SQL 模式允许时,MySQL 会保留零日期和不可能的日期;它们会变为 NULL,报告会统计其数量。
报告记录: invalid_date_null
NOT NULL columns with values that become NULL
去除 NOT NULL
值必须变为 NULL 的列无法保留 NOT NULL,否则导入会中止。
报告记录: not_null_relaxed
DEFAULT '0000-00-00'
无默认值
SQL Server 无法存储的默认值会被省略,而不是替换为编造的日期。
报告记录: zero_date_default
TIME
所有值都在一天以内时为 time,否则为 nvarchar
MySQL 的 TIME 是从 -838:59:59 到 838:59:59 的时长;SQL Server 的 time 是一天中的时刻。有更长或负值的列以文本保留这些值。
报告记录: time_as_interval
YEAR
smallint,并带有限定范围的 CHECK
SQL Server 没有年份类型;检查约束只允许 MySQL 允许的那些值。
CHAR(n), VARCHAR(n)
nvarchar(n)
nvarchar 在每个受支持的版本上都能容纳所有字符,而 MySQL 返回的 CHAR 值不带末尾空格,nvarchar 也按此保留。
text with characters past U+FFFF (emoji) in VARCHAR(n)
nvarchar(2n),超过 4000 时为 nvarchar(max)
nvarchar 按 UTF-16 单元计数,一个 emoji 占两个单元,因此列会留出容纳每个值的空间。
报告记录: text_length_widened
DATETIME
datetime2
DATETIME 是不带时区的挂钟读数,与 datetime2 相同。
TIMESTAMP
使用转储声明的时区的 datetimeoffset,未声明时为 UTC
TIMESTAMP 是一个时间点。转储会声明写入时使用的时区(mysqldump 写入 UTC);没有声明时,值按 UTC 读取,报告会说明这一点。
报告记录: timestamp_zone_assumed
ENUM
nvarchar,并带有限定其值的 CHECK
带检查约束的文本保留相同的值。
ENUM holding '' (MySQL's error value)
检查约束允许的空值
在宽松的 SQL 模式下,无效值写入 ENUM 时 MySQL 会存储空字符串。这些行会保留,检查约束允许它们。
报告记录: enum_invalid_value
ENUM holding values outside its list
不加 CHECK
列表中没有的值会使检查失败,因此不创建检查约束。
报告记录: enum_check_skipped
SET
nvarchar,按 MySQL 的写法('a,b')
SQL Server 没有数组;文本就是 MySQL 返回的文本。
JSON
nvarchar(max)
SQL Server 的 JSON 函数从文本读取 JSON。在 SQL Server 2022 之前,ISJSON 检查会拒绝 1 或 "x" 这样的标量值,而 MySQL 会保留它们。
NUL bytes in text
保留
nvarchar 可以包含 NUL 字符,因此值按原样复制。
text that isn't valid in its character set
错误的字节变为替换字符
文本必须先按字符读取,才能写为 nvarchar。
报告记录: invalid_utf8_replaced
text decoded twice (ñ)
按原样复制
转储中存储的正是这些字符,因此按原样复制;报告会指出它们。
报告记录: mojibake_suspected
AUTO_INCREMENT
IDENTITY,计数器设在已复制的 id 之后
复制过来的 id 通过 IDENTITY_INSERT 导入,计数器从最大的 id 或转储中的 AUTO_INCREMENT 之后继续,取两者中较大者。
MyISAM AUTO_INCREMENT on the second column of a key
整个表一个计数器
MyISAM 按键的第一列分组计数;SQL Server 每个表只有一个计数器。
报告记录: myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMP
一个 AFTER UPDATE 触发器
当更新语句没有设置该列时,触发器将其设为当前时间。它按主键连接,因此表需要有主键。
报告记录: on_update_trigger
ON UPDATE CURRENT_TIMESTAMP on a table without a primary key
不迁移
触发器按主键找到被更新的行;没有主键就无法找到。
报告记录: on_update_without_key
generated columns
计算列,源数据存储时为 PERSISTED
SQL Server 以同样的两种方式计算它们。
generated columns with functions SQL Server lacks
普通列
无法为 SQL Server 写出的公式会留下一个普通列和一条报告记录。
报告记录: generated_not_translated
INVISIBLE columns
普通列
SQL Server 没有不可见列。
报告记录: invisible_column
CHECK constraints
已转换,内联在 CREATE TABLE 中
内联的检查约束在每行导入时检验。
CHECK constraints with functions SQL Server lacks
不创建
无法为 SQL Server 写出的检查约束会被省略,并在报告中列出。
报告记录: check_skipped
CHECK ... NOT ENFORCED
不创建
MySQL 保留这些检查约束但不执行,因此行可能违反它们。
报告记录: check_not_enforced
defaults with functions SQL Server lacks
无默认值
无法为 SQL Server 写出的默认值会被省略,并在报告中列出。
报告记录: default_not_translated
keys and indexes on a prefix, c(20)
基于整列的索引
SQL Server 对整列建立索引;在前缀上唯一的键在整列上也唯一,因此每行都能导入。
报告记录: prefix_whole_column
FULLTEXT indexes
不创建
SQL Server 的全文搜索需要目录和专门的查询。
报告记录: fulltext_not_created
SPATIAL indexes
不创建
几何列上的空间索引需要一个边界框,而转储没有声明它。
报告记录: spatial_index_not_created
functional indexes, and indexes on columns of unlimited length
不创建
SQL Server 既不能为表达式建立索引,也不能为 nvarchar(max) 和 varbinary(max) 列建立索引。
报告记录: index_skipped
foreign keys
在数据之后以 WITH NOCHECK 添加,然后检查,有孤立行时给出警告
转储以 FOREIGN_KEY_CHECKS=0 导入,而 MyISAM 从不检查,因此没有父行的行很常见。外键先创建、最后检查;有孤立行时外键保持未检查状态并给出警告,而不是中止导入。
ON DELETE or ON UPDATE that cascades in a cycle or along a second path
NO ACTION
SQL Server 拒绝可能两次到达同一个表的级联;MySQL 允许。
报告记录: foreign_key_action_dropped
foreign keys to columns without a unique key
不创建
SQL Server 要求被引用的列上有主键或不含 NULL 的唯一索引;InnoDB 不要求。
报告记录: foreign_key_skipped
table and column names
按原样书写;仅大小写不同时重命名
在常用的排序规则下,SQL Server 名称不区分大小写。
报告记录: identifier_renamed
names over 116 characters
缩短
SQL Server 名称最多 128 个字符,其中一部分留给区分名称的编号。
报告记录: name_shortened
GEOMETRY, POINT, POLYGON...
geometry,带每个值的 SRID
SQL Server 自带的 geometry 类型能保留每个形状及其空间参考系。
INET4, INET6, UUID (MariaDB)
nvarchar(39), uniqueidentifier
uniqueidentifier 是 SQL Server 的 UUID;地址保存为文本。
BINARY, VARBINARY, BLOB
binary, varbinary, varbinary(max)
字节仍然是字节。
PARTITION BY
单个表
行保持不变;如有需要,请在 SQL Server 中重新对该表分区。
报告记录: partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)
只含当前行的表
转储只包含当前的行。
报告记录: system_versioning_dropped
MyISAM, MEMORY and other engines
普通表
SQL Server 只有一种表引擎。
报告记录: engine_dropped
views, triggers, procedures, functions and events
在报告中列出
它们的代码用 MySQL 的语言编写,因此会列出来,由你重新创建。
报告记录: not_converted_objects
dumps without CREATE TABLE
根据值推断类型
使用 --no-create-info 生成的转储只有行,因此类型来自值。
报告记录: no_schema
geometries in a dump without CREATE TABLE
geometry
MySQL 的几何格式根据值识别。
_ci collations
数据库的排序规则,通常不区分大小写
在常用的排序规则下,SQL Server 比较文本时不区分大小写,与 MySQL 的 _ci 相同。
SET NAMES utf8 from utf8mb4 tables
已转换,并给出警告
MySQL 5.x 的 mysqldump 默认写出 3 字节 utf8,在任何转换器看到文件之前就已把每个 emoji 替换为问号。
报告记录: dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8
按 Latin-1 (Windows-1252) 读取
MySQL 的 latin1 就是 Windows-1252,这是此类转储常用的编码。
报告记录: charset_guessed
sjis, cp932, gbk, big5 dumps
按 UTF-8 读取,并给出警告
这些编码暂时无法解码。
报告记录: unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows
求值
有些转储工具把值写成这些函数调用;它们按其产生的值读取,地理 WKT 会转为经度在前。
other expressions in rows
NULL
不运行数据库的转换器无法对它们求值。
报告记录: expression_value
DEFAULT in VALUES
该列的默认值
字面量默认值会复制到该行中。
报告记录: default_keyword_filled
DEFAULT in VALUES without a literal default
NULL
没有可复制的值。
报告记录: default_keyword_null
REPLACE and INSERT IGNORE
普通行
转储中每个键只有一行,因此行是相同的。
报告记录: replace_as_insert
types IntoSQL doesn't know
nvarchar(max)
文本可以容纳任何值;请在导入后更改类型。
报告记录: unknown_type
values that don't fit their column
NULL
损坏或手写的转储可能包含列无法容纳的值;这些值变为 NULL,以便其余数据能够导入。
报告记录: invalid_value_null
unreadable geometries
NULL
不是几何数据的字节无法作为几何数据存储。
报告记录: bad_geometry
CREATE SEQUENCE (MariaDB)
CREATE SEQUENCE ... AS bigint,带下一个值
SQL Server 有选项相同的序列;每个序列从转储停下的位置继续。
VECTOR(n) (MariaDB 11.7+)
nvarchar(max),浮点数存为 JSON 数组
只有 SQL Server 2025 有向量类型;JSON 数组在每个版本上都保留每个浮点数。
报告记录: type_replaced
| 在 MariaDB 中 | 在 SQL Server 中 | 报告记录 |
|---|---|---|
| TINYINT(1), BOOL | 所有值都是 0 或 1 时为 bit,否则为 smallintMySQL 的 BOOL 是 TINYINT(1),可以存储 -128 到 127 之间的任何数字。只包含 0 和 1 的列变为 bit;包含 2 或 -1 的列保持为数字,因此没有值会改变含义。 | bool_kept_numeric |
| UNSIGNED integers | TINYINT UNSIGNED 为 tinyint;其他类型为更宽一级的整数,并带有不能为负数的 CHECK,所有值都能容纳时 BIGINT UNSIGNED 为 bigint,否则为 decimal(38,0)SQL Server 除了范围为 0 到 255 的 tinyint 之外没有无符号整数。更宽的类型能容纳整个无符号范围,检查约束像 MySQL 一样阻止负值。 | unsigned_widened |
| ZEROFILL and display widths | 去除补零只是数字的显示方式,不是数字的一部分。数字会保留;如果需要补零,请在读取时格式化。 | zerofill_dropped |
| FLOAT(M,D) | real 或 float,不带小数位数SQL Server 的浮点数没有固定的小数位数。已存储的值按原样复制。 | float_scale_dropped |
| DECIMAL(p,s) | 不超过 38 位时为 decimal(p,s);超过则为 nvarcharSQL Server 的 decimal 可容纳 38 位数字。更宽的小数保存为文本,使每一位都保持精确。 | decimal_as_text |
| BIT(1), BIT(n) | bit;不超过 63 位时为 bigint;BIT(64) 为 decimal(20,0)单个位表示是或否;更宽的位字段存储为其各位组成的数字。 | |
| zero, partial or impossible dates (0000-00-00, 2024-02-30) | NULLSQL Server 只存储真实的日历日期。当 SQL 模式允许时,MySQL 会保留零日期和不可能的日期;它们会变为 NULL,报告会统计其数量。 | invalid_date_null |
| NOT NULL columns with values that become NULL | 去除 NOT NULL值必须变为 NULL 的列无法保留 NOT NULL,否则导入会中止。 | not_null_relaxed |
| DEFAULT '0000-00-00' | 无默认值SQL Server 无法存储的默认值会被省略,而不是替换为编造的日期。 | zero_date_default |
| TIME | 所有值都在一天以内时为 time,否则为 nvarcharMySQL 的 TIME 是从 -838:59:59 到 838:59:59 的时长;SQL Server 的 time 是一天中的时刻。有更长或负值的列以文本保留这些值。 | time_as_interval |
| YEAR | smallint,并带有限定范围的 CHECKSQL Server 没有年份类型;检查约束只允许 MySQL 允许的那些值。 | |
| CHAR(n), VARCHAR(n) | nvarchar(n)nvarchar 在每个受支持的版本上都能容纳所有字符,而 MySQL 返回的 CHAR 值不带末尾空格,nvarchar 也按此保留。 | |
| text with characters past U+FFFF (emoji) in VARCHAR(n) | nvarchar(2n),超过 4000 时为 nvarchar(max)nvarchar 按 UTF-16 单元计数,一个 emoji 占两个单元,因此列会留出容纳每个值的空间。 | text_length_widened |
| DATETIME | datetime2DATETIME 是不带时区的挂钟读数,与 datetime2 相同。 | |
| TIMESTAMP | 使用转储声明的时区的 datetimeoffset,未声明时为 UTCTIMESTAMP 是一个时间点。转储会声明写入时使用的时区(mysqldump 写入 UTC);没有声明时,值按 UTC 读取,报告会说明这一点。 | timestamp_zone_assumed |
| ENUM | nvarchar,并带有限定其值的 CHECK带检查约束的文本保留相同的值。 | |
| ENUM holding '' (MySQL's error value) | 检查约束允许的空值在宽松的 SQL 模式下,无效值写入 ENUM 时 MySQL 会存储空字符串。这些行会保留,检查约束允许它们。 | enum_invalid_value |
| ENUM holding values outside its list | 不加 CHECK列表中没有的值会使检查失败,因此不创建检查约束。 | enum_check_skipped |
| SET | nvarchar,按 MySQL 的写法('a,b')SQL Server 没有数组;文本就是 MySQL 返回的文本。 | |
| JSON | nvarchar(max)SQL Server 的 JSON 函数从文本读取 JSON。在 SQL Server 2022 之前,ISJSON 检查会拒绝 1 或 "x" 这样的标量值,而 MySQL 会保留它们。 | |
| NUL bytes in text | 保留nvarchar 可以包含 NUL 字符,因此值按原样复制。 | |
| text that isn't valid in its character set | 错误的字节变为替换字符文本必须先按字符读取,才能写为 nvarchar。 | invalid_utf8_replaced |
| text decoded twice (ñ) | 按原样复制转储中存储的正是这些字符,因此按原样复制;报告会指出它们。 | mojibake_suspected |
| AUTO_INCREMENT | IDENTITY,计数器设在已复制的 id 之后复制过来的 id 通过 IDENTITY_INSERT 导入,计数器从最大的 id 或转储中的 AUTO_INCREMENT 之后继续,取两者中较大者。 | |
| MyISAM AUTO_INCREMENT on the second column of a key | 整个表一个计数器MyISAM 按键的第一列分组计数;SQL Server 每个表只有一个计数器。 | myisam_group_autoincrement |
| ON UPDATE CURRENT_TIMESTAMP | 一个 AFTER UPDATE 触发器当更新语句没有设置该列时,触发器将其设为当前时间。它按主键连接,因此表需要有主键。 | on_update_trigger |
| ON UPDATE CURRENT_TIMESTAMP on a table without a primary key | 不迁移触发器按主键找到被更新的行;没有主键就无法找到。 | on_update_without_key |
| generated columns | 计算列,源数据存储时为 PERSISTEDSQL Server 以同样的两种方式计算它们。 | |
| generated columns with functions SQL Server lacks | 普通列无法为 SQL Server 写出的公式会留下一个普通列和一条报告记录。 | generated_not_translated |
| INVISIBLE columns | 普通列SQL Server 没有不可见列。 | invisible_column |
| CHECK constraints | 已转换,内联在 CREATE TABLE 中内联的检查约束在每行导入时检验。 | |
| CHECK constraints with functions SQL Server lacks | 不创建无法为 SQL Server 写出的检查约束会被省略,并在报告中列出。 | check_skipped |
| CHECK ... NOT ENFORCED | 不创建MySQL 保留这些检查约束但不执行,因此行可能违反它们。 | check_not_enforced |
| defaults with functions SQL Server lacks | 无默认值无法为 SQL Server 写出的默认值会被省略,并在报告中列出。 | default_not_translated |
| keys and indexes on a prefix, c(20) | 基于整列的索引SQL Server 对整列建立索引;在前缀上唯一的键在整列上也唯一,因此每行都能导入。 | prefix_whole_column |
| FULLTEXT indexes | 不创建SQL Server 的全文搜索需要目录和专门的查询。 | fulltext_not_created |
| SPATIAL indexes | 不创建几何列上的空间索引需要一个边界框,而转储没有声明它。 | spatial_index_not_created |
| functional indexes, and indexes on columns of unlimited length | 不创建SQL Server 既不能为表达式建立索引,也不能为 nvarchar(max) 和 varbinary(max) 列建立索引。 | index_skipped |
| foreign keys | 在数据之后以 WITH NOCHECK 添加,然后检查,有孤立行时给出警告转储以 FOREIGN_KEY_CHECKS=0 导入,而 MyISAM 从不检查,因此没有父行的行很常见。外键先创建、最后检查;有孤立行时外键保持未检查状态并给出警告,而不是中止导入。 | |
| ON DELETE or ON UPDATE that cascades in a cycle or along a second path | NO ACTIONSQL Server 拒绝可能两次到达同一个表的级联;MySQL 允许。 | foreign_key_action_dropped |
| foreign keys to columns without a unique key | 不创建SQL Server 要求被引用的列上有主键或不含 NULL 的唯一索引;InnoDB 不要求。 | foreign_key_skipped |
| table and column names | 按原样书写;仅大小写不同时重命名在常用的排序规则下,SQL Server 名称不区分大小写。 | identifier_renamed |
| names over 116 characters | 缩短SQL Server 名称最多 128 个字符,其中一部分留给区分名称的编号。 | name_shortened |
| GEOMETRY, POINT, POLYGON... | geometry,带每个值的 SRIDSQL Server 自带的 geometry 类型能保留每个形状及其空间参考系。 | |
| INET4, INET6, UUID (MariaDB) | nvarchar(39), uniqueidentifieruniqueidentifier 是 SQL Server 的 UUID;地址保存为文本。 | |
| BINARY, VARBINARY, BLOB | binary, varbinary, varbinary(max)字节仍然是字节。 | |
| PARTITION BY | 单个表行保持不变;如有需要,请在 SQL Server 中重新对该表分区。 | partitions_dropped |
| WITH SYSTEM VERSIONING (MariaDB) | 只含当前行的表转储只包含当前的行。 | system_versioning_dropped |
| MyISAM, MEMORY and other engines | 普通表SQL Server 只有一种表引擎。 | engine_dropped |
| views, triggers, procedures, functions and events | 在报告中列出它们的代码用 MySQL 的语言编写,因此会列出来,由你重新创建。 | not_converted_objects |
| dumps without CREATE TABLE | 根据值推断类型使用 --no-create-info 生成的转储只有行,因此类型来自值。 | no_schema |
| geometries in a dump without CREATE TABLE | geometryMySQL 的几何格式根据值识别。 | |
| _ci collations | 数据库的排序规则,通常不区分大小写在常用的排序规则下,SQL Server 比较文本时不区分大小写,与 MySQL 的 _ci 相同。 | |
| SET NAMES utf8 from utf8mb4 tables | 已转换,并给出警告MySQL 5.x 的 mysqldump 默认写出 3 字节 utf8,在任何转换器看到文件之前就已把每个 emoji 替换为问号。 | dump_lost_four_byte_chars |
| dumps without SET NAMES whose text isn't UTF-8 | 按 Latin-1 (Windows-1252) 读取MySQL 的 latin1 就是 Windows-1252,这是此类转储常用的编码。 | charset_guessed |
| sjis, cp932, gbk, big5 dumps | 按 UTF-8 读取,并给出警告这些编码暂时无法解码。 | unsupported_charset |
| UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows | 求值有些转储工具把值写成这些函数调用;它们按其产生的值读取,地理 WKT 会转为经度在前。 | |
| other expressions in rows | NULL不运行数据库的转换器无法对它们求值。 | expression_value |
| DEFAULT in VALUES | 该列的默认值字面量默认值会复制到该行中。 | default_keyword_filled |
| DEFAULT in VALUES without a literal default | NULL没有可复制的值。 | default_keyword_null |
| REPLACE and INSERT IGNORE | 普通行转储中每个键只有一行,因此行是相同的。 | replace_as_insert |
| types IntoSQL doesn't know | nvarchar(max)文本可以容纳任何值;请在导入后更改类型。 | unknown_type |
| values that don't fit their column | NULL损坏或手写的转储可能包含列无法容纳的值;这些值变为 NULL,以便其余数据能够导入。 | invalid_value_null |
| unreadable geometries | NULL不是几何数据的字节无法作为几何数据存储。 | bad_geometry |
| CREATE SEQUENCE (MariaDB) | CREATE SEQUENCE ... AS bigint,带下一个值SQL Server 有选项相同的序列;每个序列从转储停下的位置继续。 | |
| VECTOR(n) (MariaDB 11.7+) | nvarchar(max),浮点数存为 JSON 数组只有 SQL Server 2025 有向量类型;JSON 数组在每个版本上都保留每个浮点数。 | type_replaced |
将脚本导入 SQL Server
脚本先创建表、复制行,再添加键、索引和外键,因此导入速度快,并会在遇到第一个错误时停止。
sqlcmd -b -f 65001 -S localhost -d mydb -i mariadb.sql_server.sql外键先以 WITH NOCHECK 添加,然后再检查。如果某行指向不存在的行,脚本会说明是哪个约束,并让它保持未检查状态,而不是中途失败。
对于较大的转储,请在服务器所在的机器上运行 sqlcmd。每批最多 1000 行单独提交,因此事务日志保持较小。
如何转储 MariaDB 以获得最佳结果
转换器读取转储中的内容。以下选项能让转储保留更多数据:
- --default-character-set=utf8mb4 会保留表情符号和其他 4 字节字符,而 utf8mb3 转储在转换器读取之前就会丢失它们。
- --hex-blob 将二进制列和 VECTOR 值写为十六进制,在系统之间复制时不会损坏。
- --routines --triggers --events 会将这些对象加入转储,以便报告列出它们。
哪些内容不会转换
视图、触发器、存储过程、函数和事件都是用 MariaDB 自己的语言编写的。它们会按名称在报告中列出,绝不会被悄悄丢弃,以便你为 SQL Server 重写。
关于 MariaDB 转 SQL Server 的问题
这里没有你要的答案?请通过联系页面给我们留言,会有专人回复。 联系页面
我的转储会被上传吗?
不会。转换器在你的浏览器中运行,分块从磁盘读取转储,因此转储不会离开你的电脑。桌面应用和命令行也在你的电脑上转换。
转储可以多大?
转换器从不把整个转储放进内存:它按范围读取文件两次,边读边写出脚本。几 GB 的转储可以在浏览器、桌面应用和命令行中转换。
转储可以来自哪些 MariaDB 版本?
由 MariaDB 10.3 到 12 的 mariadb-dump 和 mysqldump 写出的转储,每个表一条 INSERT 或每行一条均可。每个测试转储都来自 MariaDB 10.11、11.4 和 11.8,并与其源数据逐个单元格比较。
UUID、INET6、JSON 和 VECTOR 列会保留吗?
UUID 变为 uniqueidentifier,INET 列变为 nvarchar,JSON 变为 nvarchar(max),VECTOR 变为由其浮点数组成的 JSON 数组。零日期变为 NULL,报告会统计其数量。
AUTO_INCREMENT 值和序列会保留吗?
会。每个 AUTO_INCREMENT 列变为 IDENTITY 列,从最大的 id 之后继续。序列变为 SQL Server 序列,从某个序列取值的默认值继续从该序列取值。
存储过程和触发器怎么处理?
它们会按名称在报告中列出,但不会转换,因为每种数据库都用自己的语言编写它们。
脚本可以导入哪些 SQL Server 版本?
SQL Server 2017 到 2025。同一个脚本可以在其中每个版本上导入,如有问题会在第一个错误处停止。