将 Oracle 转换为 SQL Server
拖入 SQL Developer 的 Database Export 或 SQLcl 写出的 .sql 文件(包含表和 Insert 语句),即可下载包含相同表、键、索引和数据的 SQL Server 脚本。SQL Server 无法原样存储的每个值,以及它不支持的每个对象,都会在报告中列出。
已转换 18 行,2 处修正,无拒绝
shop_2024_oracle.sql 中的表Oracle 转储 · 由 SQL Developer 写出 · utf-8
customers8 行1
7 列 id bigint not null name nvarchar(200) not null email nvarchar(150) not null country nvarchar(2) not null is_active smallint not null signed_up datetime2(0) created_at datetime2(0) not null orders10 行1
9 列 id bigint not null customer_id bigint not null invoice bigint status nvarchar(10) 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)
-- Written by IntoSQL from a Oracle 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 1020 INCREMENT BY 1 MINVALUE 1;
GO
CREATE TABLE [dbo].[customers] (
[id] bigint IDENTITY(1, 1) NOT NULL,
[name] nvarchar(200) COLLATE Latin1_General_100_BIN2 NOT NULL,
[email] nvarchar(150) COLLATE Latin1_General_100_BIN2 NOT NULL,
[country] nvarchar(2) COLLATE Latin1_General_100_BIN2 DEFAULT N'US' NOT NULL,
[is_active] smallint DEFAULT 1 NOT NULL,
[signed_up] datetime2(0) NULL,
[created_at] datetime2(0) DEFAULT SYSDATETIME() NOT NULL,
CONSTRAINT [check1] CHECK ([is_active] IN (0, 1)),
CONSTRAINT [PK_customers] PRIMARY KEY ([id])
);
GO
CREATE TABLE [dbo].[orders] (
[id] bigint IDENTITY(1, 1) NOT NULL,
[customer_id] bigint NOT NULL,
[invoice] bigint DEFAULT (NEXT VALUE FOR [dbo].[invoice_numbers]) NULL,
[status] nvarchar(10) COLLATE Latin1_General_100_BIN2 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) COLLATE Latin1_General_100_BIN2 NULL,
CONSTRAINT [check1_2] CHECK ([status] IN (N'pending', N'paid', N'shipped', N'cancelled')),
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 00:00:00','2023-01-15 10:24:00.0000000');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(2,N'Zoë Müller',N'zoe@example.de',N'DE',1,'2023-02-03 00:00:00','2023-02-03 08:11:42.0000000');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(3,N'José Peña',N'jose@example.es',N'ES',0,'2023-03-21 00:00:00','2023-03-21 19:05:17.0000000');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(4,N'李雷',N'lilei@example.cn',N'CN',1,'2023-05-09 00:00:00','2023-05-09 02:40:00.0000000');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(5,N'Noah O''Brien',N'noah@example.ie',N'IE',1,NULL,'2023-06-30 14:00:00.0000000');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(6,N'Amélie Laurent',N'amelie@example.fr',N'FR',1,'2024-01-02 00:00:00','2024-01-02 09:30:00.0000000');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(7,N'Ravi Patel',N'ravi@example.in',N'IN',0,'2024-02-29 00:00:00','2024-02-29 23:59:59.0000000');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(8,N'Sofia Rossi 🌻',N'sofia@example.it',N'IT',1,'2024-04-12 00:00:00','2024-04-12 12:12:12.0000000');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [invoice], [status], [total], [items], [ordered_at], [shipped_at], [note]) VALUES
(1,1,1000,N'shipped',129.9,3,'2024-03-01 10:00:00','2024-03-02 16:30:00',NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [invoice], [status], [total], [items], [ordered_at], [shipped_at], [note]) VALUES
(2,2,1001,N'paid',54,1,'2024-03-02 11:15:00',NULL,N'Gift wrap 🎁');
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [invoice], [status], [total], [items], [ordered_at], [shipped_at], [note]) VALUES
(3,1,1002,N'cancelled',12.5,1,'2024-03-03 09:45:00',NULL,N'Customer changed their mind');
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [invoice], [status], [total], [items], [ordered_at], [shipped_at], [note]) VALUES
(4,4,1003,N'shipped',980,12,'2024-03-05 02:10:00','2024-03-07 08:00:00',N'请小心轻放');
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [invoice], [status], [total], [items], [ordered_at], [shipped_at], [note]) VALUES
(5,5,1004,N'pending',45.75,2,'2024-03-08 17:20:00',NULL,N'Leave at the door; ring twice');
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [invoice], [status], [total], [items], [ordered_at], [shipped_at], [note]) VALUES
(6,6,1005,N'shipped',310.4,5,'2024-03-10 13:00:00','2024-03-11 09:00:00',N'Fragile: "glass"');
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [invoice], [status], [total], [items], [ordered_at], [shipped_at], [note]) VALUES
(7,8,1006,N'paid',76.2,2,'2024-03-11 18:45:00',NULL,NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [invoice], [status], [total], [items], [ordered_at], [shipped_at], [note]) VALUES
(8,2,1007,N'paid',19.99,1,'2024-03-12 08:00:00',NULL,N'Paid at the counter');
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [invoice], [status], [total], [items], [ordered_at], [shipped_at], [note]) VALUES
(9,3,1008,N'shipped',64,2,'2024-03-14 15:30:00',NULL,N'Shipping date never recorded');
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [invoice], [status], [total], [items], [ordered_at], [shipped_at], [note]) VALUES
(10,7,1009,N'paid',1499,1,'2024-03-15 21:05:00',NULL,N'C:\Users\ravi\invoice.pdf');
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
-- Indexes, foreign keys, counters and triggers, after the rows.
CREATE UNIQUE INDEX [customers_email_uk] ON [dbo].[customers] ([email]);
CREATE INDEX [orders_status_date_ix] ON [dbo].[orders] ([status], [ordered_at]);
ALTER TABLE [dbo].[orders] WITH NOCHECK ADD CONSTRAINT [orders_customer_fk] FOREIGN KEY ([customer_id]) REFERENCES [dbo].[customers] ([id]);
BEGIN TRY ALTER TABLE [dbo].[orders] WITH CHECK CHECK CONSTRAINT [orders_customer_fk]; END TRY BEGIN CATCH PRINT N'Foreign key ' + N'[orders_customer_fk]' + 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 < 0 SET @next = 0; 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 < 0 SET @next = 0; DBCC CHECKIDENT (N'[dbo].[orders]', RESEED, @next) WITH NO_INFOMSGS; END;
GO
GO
修正
- customers 的 "name" 包含 SQL Server 按两个计数的字符(例如 emoji),因此为 nvarchar(200)。
- orders 的 "note" 包含 SQL Server 按两个计数的字符(例如 emoji),因此为 nvarchar(510)。
文件在这台电脑上转换,永远不会上传。
其他示例:
从 Oracle 到 SQL Server 的变化
每一行是转换器应用的一条规则,以及该规则更改你的数据时在报告中写入的记录。
BOOLEAN (23ai)
bit
SQL Server 没有布尔列,因此 TRUE 和 FALSE 为 1 和 0。
VARCHAR2(n BYTE|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONG
nvarchar(n)、nchar(n) 和 nvarchar(max)
无论 Oracle 按字节还是按字符计数,长度都按字符计算,因此每个值都能容纳。
VARCHAR2 and NVARCHAR2 past what the target's own types hold
nvarchar(2n),超过 4000 时为 nvarchar(max)
nvarchar 按 UTF-16 单元计数,一个 emoji 占两个单元,因此列会留出容纳每个值的空间。
报告记录: text_length_widened
NUMBER, NUMBER(p,s), FLOAT(b)
decimal(p,s),或 bigint
NUMBER 保留每一位数字;没有精度的 NUMBER 若所有值都是不超过 18 位的整数,则为 64 位整数;负的小数位数会在小数点前保留同样多的位数。
INTERVAL YEAR TO MONTH, INTERVAL DAY TO SECOND
nvarchar(100),存放 Oracle 的文本,例如 +01-06
SQL Server 没有 interval 类型;文本保留每个部分。
报告记录: type_replaced
DATE, TIMESTAMP(p)
datetime2(p)
Oracle 的 DATE 包含精确到秒的时间,因此是日期和时间。在美式或德式会话中导出时,日期不含时间和世纪(DD-MON-RR);请在 ISO 会话(SYYYY-MM-DD HH24:MI:SS)中导出以保留它们。
TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE
datetimeoffset,保留每个值的偏移量
类型和值都相同。
JSON (21c), and CLOB holding JSON
nvarchar(max)
JSON 值相同;导出写为 'null' 的 SQL NULL 仍为 NULL。
BLOB, RAW(n), LONG RAW
binary(n)、varbinary(n) 和 varbinary(max)
字节仍然是字节。
ROWID, UROWID, XMLTYPE, SDO_GEOMETRY
nvarchar(max),XMLTYPE 为 xml
Oracle 自有的类型没有对应类型;其值保留文本,包括 ROWID 值,尽管它们离开 Oracle 就没有意义。
collations that ignore case (BINARY_CI)
每列使用相同的排序规则
列的比较和排序方式不变,唯一键也区分相同的行。
identity columns, and a sequence a BEFORE INSERT trigger assigns
IDENTITY,从最大的 id 之后继续
复制的 id 按原样导入,计数器从最大的 id 之后继续。由触发器从序列取值填充的列(12c 之前的做法)也会成为标识列。
identity columns the target can't count
普通整数
SQL Server 每个表只有一个 IDENTITY 列计数;其他列保留其值,不带计数器。
报告记录: counter_dropped
CREATE SEQUENCE
CREATE SEQUENCE,选项相同
导出会把序列当前到达的位置写为其 START WITH,因此序列从那里继续。
virtual columns (GENERATED ALWAYS AS ... VIRTUAL)
计算列
SQL Server 用相同的公式计算它们;导出为它们写出的值会被省略。
virtual columns with functions IntoSQL doesn't write, and the || operator
普通列
无法为 SQL Server 写出的公式会留下一个普通列和一条报告记录;导出为虚拟列写出的值会被省略,SQL Server 也没有可计算的值。
报告记录: generated_not_translated
defaults with functions IntoSQL doesn't write (SYS_GUID(), ||)
无默认值
无法写出的默认值会被省略,并在报告中列出。
报告记录: default_not_translated
CHECK constraints
已转换
检查约束相同,在导入每一行时检查。
CHECK constraints with functions IntoSQL doesn't write
不创建
无法写出的检查约束会被省略,并在报告中列出。
报告记录: check_skipped
CHECK constraints that are DISABLE or ENABLE NOVALIDATE
不创建
Oracle 不对已有的行执行这些检查约束,因此行可能违反它们。
报告记录: check_not_enforced
function-based, bitmap and domain indexes
不创建
IntoSQL 在整列上写出 b-tree 索引;其他索引请在导入后重新创建。
报告记录: index_skipped
foreign keys
在数据之后以 WITH NOCHECK 添加,然后检查,有孤立行时给出警告
外键在行之后创建,最后检查;有孤立行时外键保持未检查状态并给出警告,而不是中止导入。
ON DELETE CASCADE that cascades in a cycle or along a second path
NO ACTION
SQL Server 拒绝形成循环或经由第二条路径的级联操作,因此创建该键时不带此操作,并在报告中列出。
报告记录: foreign_key_action_dropped
foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE ones
不创建
外键无法引用不存在的表,而 Oracle 不检查的外键可能存在没有父行的行。
报告记录: foreign_key_skipped
partitioned tables
单个表,包含每个分区的行
行保持不变;如有需要,请在 SQL Server 中重新对该表分区。
报告记录: partitions_dropped
NOT NULL columns with values that become NULL
去除 NOT NULL
值必须变为 NULL 的列无法保留 NOT NULL,否则导入会中止。
报告记录: not_null_relaxed
views, synonyms, types, packages, procedures, functions and other triggers
在报告中列出
它们的代码用 PL/SQL 编写,因此会列出来,由你重新创建。
报告记录: not_converted_objects
exports without CREATE TABLE (data only)
根据值推断类型
只含数据的导出只有行,因此类型来自值。
报告记录: no_schema
expressions in INSERT values other than the ones exports write
NULL
不运行数据库的转换器无法对它们求值;导出写出的 to_date、to_timestamp、TO_CLOB、HEXTORAW 等会被读取。
报告记录: expression_value
types IntoSQL doesn't know, and object types
nvarchar(max)
文本可以容纳任何值;请在导入后更改类型。
报告记录: unknown_type
dates before year 1 and time zones given as region names
NULL
不是日期的值变为 NULL,报告会统计其数量。
报告记录: date_out_of_range
NaN and Infinity in BINARY_FLOAT and BINARY_DOUBLE
NULL
SQL Server 的浮点类型没有 NaN 或 infinity,因此这些值变为 NULL,报告会统计其数量。
报告记录: invalid_value_null
NUL characters in text
保留
nvarchar 可以包含 NUL 字符,因此值按原样复制。
text that isn't valid in its encoding
错误的字节变为替换字符
脚本是 UTF-8,每个值都必须是有效文本。
报告记录: invalid_utf8_replaced
text decoded twice (ñ)
按原样复制
脚本中存储的正是这些字符,因此按原样复制;报告会指出它们。
报告记录: mojibake_suspected
| 在 Oracle 中 | 在 SQL Server 中 | 报告记录 |
|---|---|---|
| BOOLEAN (23ai) | bitSQL Server 没有布尔列,因此 TRUE 和 FALSE 为 1 和 0。 | |
| VARCHAR2(n BYTE|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONG | nvarchar(n)、nchar(n) 和 nvarchar(max)无论 Oracle 按字节还是按字符计数,长度都按字符计算,因此每个值都能容纳。 | |
| VARCHAR2 and NVARCHAR2 past what the target's own types hold | nvarchar(2n),超过 4000 时为 nvarchar(max)nvarchar 按 UTF-16 单元计数,一个 emoji 占两个单元,因此列会留出容纳每个值的空间。 | text_length_widened |
| NUMBER, NUMBER(p,s), FLOAT(b) | decimal(p,s),或 bigintNUMBER 保留每一位数字;没有精度的 NUMBER 若所有值都是不超过 18 位的整数,则为 64 位整数;负的小数位数会在小数点前保留同样多的位数。 | |
| INTERVAL YEAR TO MONTH, INTERVAL DAY TO SECOND | nvarchar(100),存放 Oracle 的文本,例如 +01-06SQL Server 没有 interval 类型;文本保留每个部分。 | type_replaced |
| DATE, TIMESTAMP(p) | datetime2(p)Oracle 的 DATE 包含精确到秒的时间,因此是日期和时间。在美式或德式会话中导出时,日期不含时间和世纪(DD-MON-RR);请在 ISO 会话(SYYYY-MM-DD HH24:MI:SS)中导出以保留它们。 | |
| TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE | datetimeoffset,保留每个值的偏移量类型和值都相同。 | |
| JSON (21c), and CLOB holding JSON | nvarchar(max)JSON 值相同;导出写为 'null' 的 SQL NULL 仍为 NULL。 | |
| BLOB, RAW(n), LONG RAW | binary(n)、varbinary(n) 和 varbinary(max)字节仍然是字节。 | |
| ROWID, UROWID, XMLTYPE, SDO_GEOMETRY | nvarchar(max),XMLTYPE 为 xmlOracle 自有的类型没有对应类型;其值保留文本,包括 ROWID 值,尽管它们离开 Oracle 就没有意义。 | |
| collations that ignore case (BINARY_CI) | 每列使用相同的排序规则列的比较和排序方式不变,唯一键也区分相同的行。 | |
| identity columns, and a sequence a BEFORE INSERT trigger assigns | IDENTITY,从最大的 id 之后继续复制的 id 按原样导入,计数器从最大的 id 之后继续。由触发器从序列取值填充的列(12c 之前的做法)也会成为标识列。 | |
| identity columns the target can't count | 普通整数SQL Server 每个表只有一个 IDENTITY 列计数;其他列保留其值,不带计数器。 | counter_dropped |
| CREATE SEQUENCE | CREATE SEQUENCE,选项相同导出会把序列当前到达的位置写为其 START WITH,因此序列从那里继续。 | |
| virtual columns (GENERATED ALWAYS AS ... VIRTUAL) | 计算列SQL Server 用相同的公式计算它们;导出为它们写出的值会被省略。 | |
| virtual columns with functions IntoSQL doesn't write, and the || operator | 普通列无法为 SQL Server 写出的公式会留下一个普通列和一条报告记录;导出为虚拟列写出的值会被省略,SQL Server 也没有可计算的值。 | generated_not_translated |
| defaults with functions IntoSQL doesn't write (SYS_GUID(), ||) | 无默认值无法写出的默认值会被省略,并在报告中列出。 | default_not_translated |
| CHECK constraints | 已转换检查约束相同,在导入每一行时检查。 | |
| CHECK constraints with functions IntoSQL doesn't write | 不创建无法写出的检查约束会被省略,并在报告中列出。 | check_skipped |
| CHECK constraints that are DISABLE or ENABLE NOVALIDATE | 不创建Oracle 不对已有的行执行这些检查约束,因此行可能违反它们。 | check_not_enforced |
| function-based, bitmap and domain indexes | 不创建IntoSQL 在整列上写出 b-tree 索引;其他索引请在导入后重新创建。 | index_skipped |
| foreign keys | 在数据之后以 WITH NOCHECK 添加,然后检查,有孤立行时给出警告外键在行之后创建,最后检查;有孤立行时外键保持未检查状态并给出警告,而不是中止导入。 | |
| ON DELETE CASCADE that cascades in a cycle or along a second path | NO ACTIONSQL Server 拒绝形成循环或经由第二条路径的级联操作,因此创建该键时不带此操作,并在报告中列出。 | foreign_key_action_dropped |
| foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE ones | 不创建外键无法引用不存在的表,而 Oracle 不检查的外键可能存在没有父行的行。 | foreign_key_skipped |
| partitioned tables | 单个表,包含每个分区的行行保持不变;如有需要,请在 SQL Server 中重新对该表分区。 | partitions_dropped |
| NOT NULL columns with values that become NULL | 去除 NOT NULL值必须变为 NULL 的列无法保留 NOT NULL,否则导入会中止。 | not_null_relaxed |
| views, synonyms, types, packages, procedures, functions and other triggers | 在报告中列出它们的代码用 PL/SQL 编写,因此会列出来,由你重新创建。 | not_converted_objects |
| exports without CREATE TABLE (data only) | 根据值推断类型只含数据的导出只有行,因此类型来自值。 | no_schema |
| expressions in INSERT values other than the ones exports write | NULL不运行数据库的转换器无法对它们求值;导出写出的 to_date、to_timestamp、TO_CLOB、HEXTORAW 等会被读取。 | expression_value |
| types IntoSQL doesn't know, and object types | nvarchar(max)文本可以容纳任何值;请在导入后更改类型。 | unknown_type |
| dates before year 1 and time zones given as region names | NULL不是日期的值变为 NULL,报告会统计其数量。 | date_out_of_range |
| NaN and Infinity in BINARY_FLOAT and BINARY_DOUBLE | NULLSQL Server 的浮点类型没有 NaN 或 infinity,因此这些值变为 NULL,报告会统计其数量。 | invalid_value_null |
| NUL characters in text | 保留nvarchar 可以包含 NUL 字符,因此值按原样复制。 | |
| text that isn't valid in its encoding | 错误的字节变为替换字符脚本是 UTF-8,每个值都必须是有效文本。 | invalid_utf8_replaced |
| text decoded twice (ñ) | 按原样复制脚本中存储的正是这些字符,因此按原样复制;报告会指出它们。 | mojibake_suspected |
将脚本导入 SQL Server
脚本先创建表、复制行,再添加键、索引和外键,因此导入速度快,并会在遇到第一个错误时停止。
sqlcmd -b -f 65001 -S localhost -d mydb -i oracle.sql_server.sql外键先以 WITH NOCHECK 添加,然后再检查。如果某行指向不存在的行,脚本会说明是哪个约束,并让它保持未检查状态,而不是中途失败。
对于较大的转储,请在服务器所在的机器上运行 sqlcmd。每批最多 1000 行单独提交,因此事务日志保持较小。
如何转储 Oracle 以获得最佳结果
转换器读取转储中的内容。以下选项能让转储保留更多数据:
- 在 SQL Developer 中选择 Tools、Database Export,并选择 DDL 和 Insert 数据格式。SQLcl 使用 SET SQLFORMAT INSERT 也会写出相同内容。
- 导出前请执行 ALTER SESSION SET NLS_DATE_FORMAT = 'SYYYY-MM-DD HH24:MI:SS':默认格式写出的日期不含时间和世纪。
- exp 或 expdp 生成的 .dmp 文件是二进制的:请将其导入 Oracle 数据库(例如 Docker 中的 Oracle Database Free),再用 SQL Developer 导出。
哪些内容不会转换
视图、包、存储过程、函数、触发器、类型和同义词都是用 PL/SQL 编写的。它们会按名称在报告中列出,绝不会被悄悄丢弃,以便你为 SQL Server 重写。
关于 Oracle 转 SQL Server 的问题
这里没有你要的答案?请通过联系页面给我们留言,会有专人回复。 联系页面
我的转储会被上传吗?
不会。转换器在你的浏览器中运行,分块从磁盘读取转储,因此转储不会离开你的电脑。桌面应用和命令行也在你的电脑上转换。
转储可以多大?
转换器从不把整个转储放进内存:它按范围读取文件两次,边读边写出脚本。几 GB 的转储可以在浏览器、桌面应用和命令行中转换。
可以转换 exp 或 expdp 生成的 .dmp 文件吗?
不能直接转换:Data Pump 和原始的 Export 写出的是只有 Oracle 能读取的二进制格式。请将其导入任意 Oracle 数据库(Oracle Database Free 可在 Docker 中运行),再用 SQL Developer 的 Database Export 或 SQLcl 导出。转换器收到 .dmp 文件时会给出提示。
导出可以来自哪些 Oracle 版本?
由 SQL Developer 和 SQLcl 从 Oracle 11g 及更高版本写出、带有 DBMS_METADATA 表定义的导出。每个测试导出都来自 Oracle 21c 和 23ai,涵盖美式、德式和 ISO 会话,并与其源数据逐个单元格比较。
NUMBER、DATE 和其他 Oracle 类型会怎样?
NUMBER 变为 decimal,整数为 bigint;DATE 变为 datetime2(0),保留其时间;文本变为 nvarchar,与 Oracle 中一样精确比较;CLOB 变为 nvarchar(max),BLOB 变为 varbinary(max)。NaN 和 Infinity 变为 NULL,报告会统计其数量。
标识列和序列会保留吗?
是的。标识列变为 IDENTITY 列,并从最大的 id 之后继续;序列变为 SQL Server 序列,从序列取值的默认值也会继续从中取值。
PL/SQL 包、存储过程和触发器怎么处理?
它们会按名称在报告中列出,但不会转换,因为每种数据库都用自己的语言编写它们。
脚本可以导入哪些 SQL Server 版本?
SQL Server 2017 到 2025。同一个脚本在每个版本上都能导入,如有问题会在第一个错误处停止。