跳到正文
IntoSQL 首页

将 SQL Server 转换为 Oracle

拖入 SSMS 的"生成脚本"写出的包含架构和数据的脚本(.sql 或 .sql.gz,UTF-16 或 UTF-8),即可下载包含相同表、键、索引和数据的 Oracle 脚本。Oracle 无法原样存储的每个值,以及它不支持的每个对象,都会在报告中列出。

你的文件
sqlshop_2024_sqlserver.sql示例文件,11 kB
你的数据库

已转换 18 行,2 处修正,无拒绝

显示

shop_2024_sqlserver.sql 中的表SQL Server 转储 · 由 SQL Server Management Objects 写出 · utf-8

  • orders写作 ORDERS10 行1
    11 列
    IDNUMBER(10) not null
    CUSTOMER_IDNUMBER(10) not null
    STATUSVARCHAR2(10 CHAR) not null
    TOTALNUMBER(19,4) not null
    ITEMSNUMBER(3) not null
    ORDERED_ATTIMESTAMP(0) not null
    SHIPPED_ATTIMESTAMP(7)
    NOTECLOB
    DETAILSCLOB
    ORDER_REFVARCHAR2(36 CHAR) not null
    TOTAL_WITH_TAXNUMBER(12,2)
  • customers写作 CUSTOMERS8 行1
    8 列
    IDNUMBER(10) not null
    NAMEVARCHAR2(100 CHAR) not null
    EMAILVARCHAR2(200 CHAR) not null
    COUNTRYVARCHAR2(2 CHAR) not null
    IS_ACTIVENUMBER(1) not null
    SIGNED_UPDATE
    CREATED_ATTIMESTAMP(0) WITH TIME ZONE not null
    ROW_VERSIONRAW(8)

未转换

  • 视图 paid_orders
  • 触发器 orders_no_negative

每种数据库都用自己的语言编写这些对象。请为 Oracle 重写它们。

-- Written by IntoSQL from a SQL Server dump. Load it with: NLS_LANG=.AL32UTF8 sqlplus -S -L <user>/<password>@<database> @<file>
WHENEVER SQLERROR EXIT FAILURE ROLLBACK;
SET DEFINE OFF;
SET SQLBLANKLINES ON;
SET FEEDBACK OFF;
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION "INTOSQL_CLOB" (p SYS.ODCIVARCHAR2LIST) RETURN CLOB IS
    r CLOB;
BEGIN
    DBMS_LOB.CREATETEMPORARY(r, TRUE);
    FOR i IN 1 .. p.COUNT LOOP
        IF p(i) IS NOT NULL THEN
            DBMS_LOB.WRITEAPPEND(r, LENGTH2(p(i)), p(i));
        END IF;
    END LOOP;
    RETURN r;
END;
/
CREATE OR REPLACE FUNCTION "INTOSQL_BLOB" (p SYS.ODCIVARCHAR2LIST) RETURN BLOB IS
    r BLOB;
    piece RAW(2000);
BEGIN
    DBMS_LOB.CREATETEMPORARY(r, TRUE);
    FOR i IN 1 .. p.COUNT LOOP
        piece := HEXTORAW(p(i));
        DBMS_LOB.WRITEAPPEND(r, UTL_RAW.LENGTH(piece), piece);
    END LOOP;
    RETURN r;
END;
/

CREATE TABLE "ORDERS" (
    "ID" NUMBER(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 1),
    "CUSTOMER_ID" NUMBER(10) NOT NULL,
    "STATUS" VARCHAR2(10 CHAR) DEFAULT 'pending' NOT NULL,
    "TOTAL" NUMBER(19,4) NOT NULL,
    "ITEMS" NUMBER(3) DEFAULT 1 NOT NULL,
    "ORDERED_AT" TIMESTAMP(0) NOT NULL,
    "SHIPPED_AT" TIMESTAMP(7),
    "NOTE" CLOB,
    "DETAILS" CLOB,
    "ORDER_REF" VARCHAR2(36 CHAR) NOT NULL,
    "TOTAL_WITH_TAX" NUMBER(12,2) GENERATED ALWAYS AS (CAST(CAST("TOTAL" * (1.16) AS NUMBER(12,2)) AS NUMBER(12,2))) VIRTUAL,
    CONSTRAINT "CK_ORDERS_ITEMS" CHECK ("ITEMS" >= 0),
    CONSTRAINT "CHECK2" CHECK ("STATUS" = 'cancelled' OR "STATUS" = 'shipped' OR "STATUS" = 'paid' OR "STATUS" = 'pending'),
    CONSTRAINT "CHECK3" CHECK ("TOTAL" >= (0))
);

CREATE TABLE "CUSTOMERS" (
    "ID" NUMBER(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 1),
    "NAME" VARCHAR2(100 CHAR) NOT NULL,
    "EMAIL" VARCHAR2(200 CHAR) NOT NULL,
    "COUNTRY" VARCHAR2(2 CHAR) DEFAULT 'US' NOT NULL,
    "IS_ACTIVE" NUMBER(1) DEFAULT 1 NOT NULL,
    "SIGNED_UP" DATE,
    "CREATED_AT" TIMESTAMP(0) WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
    "ROW_VERSION" RAW(8),
    CONSTRAINT "CK_CUSTOMERS_IS_ACTIVE" CHECK ("IS_ACTIVE" IN (0, 1))
);

BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
1,
'Ana Martínez',
'ana@example.com',
'MX',
1,
DATE '2023-01-15',
TIMESTAMP '2023-01-15 04:24:00.0000000 -06:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
2,
'Zoë Müller',
'zoe@example.de',
'DE',
1,
DATE '2023-02-03',
TIMESTAMP '2023-02-03 09:11:42.0000000 +01:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
3,
'José Peña',
'jose@example.es',
'ES',
0,
DATE '2023-03-21',
TIMESTAMP '2023-03-21 20:05:17.0000000 +01:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
4,
'李雷',
'lilei@example.cn',
'CN',
1,
DATE '2023-05-09',
TIMESTAMP '2023-05-09 10:40:00.0000000 +08:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
5,
'Noah O''Brien',
'noah@example.ie',
'IE',
1,
NULL,
TIMESTAMP '2023-06-30 15:00:00.0000000 +01:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
6,
'Amélie Laurent',
'amelie@example.fr',
'FR',
1,
DATE '2024-01-02',
TIMESTAMP '2024-01-02 10:30:00.0000000 +01:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
7,
'Ravi Patel',
'ravi@example.in',
'IN',
0,
DATE '2024-02-29',
TIMESTAMP '2024-03-01 05:29:59.0000000 +05:30'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
8,
'Sofia Rossi 🌻',
'sofia@example.it',
'IT',
1,
DATE '2024-04-12',
TIMESTAMP '2024-04-12 14:12:12.0000000 +02:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"ORDER_REF"
) VALUES (
1,
1,
'shipped',
129.9000,
3,
TIMESTAMP '2024-03-01 10:00:00.0000000',
TIMESTAMP '2024-03-02 16:30:00.1234567',
NULL,
'{"gift": false}',
'0b7a4c2e-3f1d-4e8a-9c55-1a2b3c4d5e6f'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"ORDER_REF"
) VALUES (
2,
2,
'paid',
54.0000,
1,
TIMESTAMP '2024-03-02 11:15:00.0000000',
NULL,
'Gift wrap 🎁',
'{"gift": true, "wrap": "red"}',
'1c8b5d3f-4a2e-4f9b-8d66-2b3c4d5e6f70'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"ORDER_REF"
) VALUES (
3,
1,
'cancelled',
12.5000,
1,
TIMESTAMP '2024-03-03 09:45:00.0000000',
NULL,
'Customer changed their mind',
NULL,
'2d9c6e4a-5b3f-4a0c-9e77-3c4d5e6f7081'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"ORDER_REF"
) VALUES (
4,
4,
'shipped',
980.0000,
12,
TIMESTAMP '2024-03-05 02:10:00.0000000',
TIMESTAMP '2024-03-07 08:00:00.0000000',
'请小心轻放',
'{"fragile": true}',
'3e0d7f5b-6c4a-4b1d-8f88-4d5e6f708192'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"ORDER_REF"
) VALUES (
5,
5,
'pending',
45.7500,
2,
TIMESTAMP '2024-03-08 17:20:00.0000000',
NULL,
'Leave at the door; ring twice',
NULL,
'4f1e806c-7d5b-4c2e-9a99-5e6f708192a3'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"ORDER_REF"
) VALUES (
6,
6,
'shipped',
310.4000,
5,
TIMESTAMP '2024-03-10 13:00:00.0000000',
TIMESTAMP '2024-03-11 09:00:00.0000000',
'Fragile: "glass"',
'{"fragile": true}',
'50f2917d-8e6c-4d3f-8baa-6f708192a3b4'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"ORDER_REF"
) VALUES (
7,
8,
'paid',
76.2000,
2,
TIMESTAMP '2024-03-11 18:45:00.0000000',
NULL,
NULL,
NULL,
'6103a28e-9f7d-4e40-9cbb-708192a3b4c5'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"ORDER_REF"
) VALUES (
8,
2,
'shipped',
19.9900,
1,
TIMESTAMP '2024-03-12 08:00:00.0000000',
TIMESTAMP '9999-12-31 23:59:59.9999999',
'Shipped date set to the maximum by an old import',
NULL,
'7214b39f-a08e-4f51-8dcc-8192a3b4c5d6'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"ORDER_REF"
) VALUES (
9,
3,
'shipped',
64.0000,
2,
TIMESTAMP '2024-03-14 15:30:00.0000000',
TIMESTAMP '2024-03-15 10:00:00.0000000',
'Two parcels',
'{"parcels": [1, 2]}',
'8325c4a0-b19f-4062-9edd-92a3b4c5d6e7'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"ORDER_REF"
) VALUES (
10,
7,
'paid',
1499.0000,
1,
TIMESTAMP '2024-03-15 21:05:00.0000000',
NULL,
'C:\Users\ravi\invoice.pdf',
NULL,
'9436d5b1-c2a0-4173-8fee-a3b4c5d6e7f8'
);
COMMIT;
END;
/

-- Keys, indexes, foreign keys, counters and triggers, after the rows.
ALTER TABLE "ORDERS" ADD CONSTRAINT "PK_ORDERS" PRIMARY KEY ("ID");
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "PK_CUSTOMERS" PRIMARY KEY ("ID");
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "UQ_CUSTOMERS_EMAIL" UNIQUE ("EMAIL");
CREATE INDEX "IX_ORDERS_IDX_ORDERS_CUSTOMER" ON "ORDERS" ("CUSTOMER_ID");
CREATE INDEX "IX_ORDERS_IDX_ORDERS_STATUS_DATE" ON "ORDERS" ("STATUS", "ORDERED_AT");
ALTER TABLE "ORDERS" ADD CONSTRAINT "FK_ORDERS" FOREIGN KEY ("CUSTOMER_ID") REFERENCES "CUSTOMERS" ("ID") ENABLE NOVALIDATE;
BEGIN
    EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" ENABLE VALIDATE CONSTRAINT "FK_ORDERS"';
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Foreign key ' || '"FK_ORDERS"' || ' has rows without a parent: it stays unvalidated, and new rows are checked.');
END;
/
DECLARE
    next_id NUMBER;
BEGIN
    SELECT NVL(MAX("ID"), 0) + 1 INTO next_id FROM "ORDERS";
    IF next_id < 1 THEN
        next_id := 1;
    END IF;
    EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
DECLARE
    next_id NUMBER;
BEGIN
    SELECT NVL(MAX("ID"), 0) + 1 INTO next_id FROM "CUSTOMERS";
    IF next_id < 1 THEN
        next_id := 1;
    END IF;
    EXECUTE IMMEDIATE 'ALTER TABLE "CUSTOMERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
COMMENT ON TABLE "CUSTOMERS" IS 'People who bought at least once';
DROP FUNCTION "INTOSQL_CLOB";
DROP FUNCTION "INTOSQL_BLOB";
COMMIT;
EXIT;

修正

  1. orders 的 "total_with_tax" 在源数据中是存储列;Oracle 在读取时计算它。
  2. customers 的 "row_version" 是 SQL Server 自动设置的行版本;转储中没有它的值,因此改为 RAW(8),并保持为空。

需要检查

  • 源数据中有 6 个文本列比较时不区分大小写;Oracle 精确比较,因此唯一键和搜索的行为可能不同。 在需要的列上使用不区分大小写的排序规则或列类型。
  • 2 个视图、触发器、例程或事件未转换;报告逐一列出了它们。 请在 Oracle 中手动重新创建它们。
  • orders 上的检查约束 check1 无法为 Oracle 写出(function isjson),因此未创建。 如果它重要,请手动编写该检查约束。
  • orders 上的索引 idx_orders_pending 使用了无法为 Oracle 写出的表达式(filtered index),因此未创建。 如果查询需要,请手动创建该索引。
  • orders 中 "order_ref" 的默认值无法为 Oracle 写出(function uuid),因此该列没有默认值。 如果新行需要,请手动添加默认值。

文件在这台电脑上转换,永远不会上传。

其他示例:

从 SQL Server 到 Oracle 的变化

每一行是转换器应用的一条规则,以及该规则更改你的数据时在报告中写入的记录。

  • bit

    NUMBER(1),并带有限定 0 和 1 的 CHECK

    Oracle 21c 没有 boolean 列类型;1 和 0 在每个版本上都能保留这些值。

  • char(n), varchar(n), nchar(n), nvarchar(n)

    VARCHAR2(n CHAR),按字符计数

    长度按字符计算,与 SQL Server 的 nvarchar 相同。

  • varchar(max), nvarchar(max), text, ntext

    每个值都不超过 4000 字节时为 VARCHAR2(4000 CHAR),否则为 CLOB

    VARCHAR2 默认容纳 4000 字节,而 Oracle 无法为 CLOB 建立索引,因此值能容纳时文本保持为 VARCHAR2,否则为 CLOB。

    报告记录: text_as_lob

  • empty strings and empty binary values

    NULL

    Oracle 将空字符串存储为 NULL,因此原为 ' 的值读回时为 NULL。报告会按列统计它们。

    报告记录: empty_string_null

  • decimal(p,s), money, smallmoney

    NUMBER(p,s);money 为 NUMBER(19,4)

    NUMBER 与 decimal 一样可容纳 38 位数字,因此每个值都是精确的。

  • datetime, datetime2, smalldatetime

    TIMESTAMP(p)

    datetime 是挂钟读数,与 Oracle 的 TIMESTAMP 相同,后者最多保留九位秒的小数。date 变为 DATE,它也包含时间,但始终为午夜。

  • datetimeoffset

    TIMESTAMP WITH TIME ZONE,保留每个值的偏移量

    Oracle 保留 -12:00 到 +14:00 的偏移量;偏移量超出 -12:00 的值以 UTC 写出,仍是同一时刻。

  • time

    从午夜起的 INTERVAL DAY TO SECOND

    Oracle 没有表示一天中时间的类型;从午夜起的 interval 可容纳每个值。

  • dates that aren't valid

    NULL

    不是日期的值变为 NULL,报告会统计其数量。

    报告记录: date_out_of_range

  • json (SQL Server 2025)

    CLOB

    JSON 值相同,Oracle 的 JSON 函数可以读取。

  • uniqueidentifier

    VARCHAR2(36)

    标识符相同,以小写写出。

  • binary, varbinary, image

    RAW(n) 和 BLOB

    字节仍然是字节。

  • geometry, geography

    BLOB,存放 SQL Server 自己的字节

    SSMS 以 SQL Server 的序列化格式写出空间值,这些字节会逐字节保留。

    报告记录: spatial_as_native

  • rowversion, timestamp

    BLOB,空

    行版本由 SQL Server 自动设置,SSMS 不会为其写出值。

    报告记录: rowversion_empty

  • xml, sql_variant, hierarchyid, vector

    VARCHAR2 或 CLOB,存放每个值的文本

    Oracle 没有对应的类型;xml 和 sql_variant 保留每个值的文本,hierarchyid 保留其路径,例如 /1/2/。

    报告记录: type_replaced

  • collations that ignore case (_CI_)

    精确比较

    除非另行指定,Oracle 精确比较文本,因此在 SQL Server 中不区分大小写的列现在会区分 Ana 和 ana。

    报告记录: case_insensitive_collation

  • IDENTITY

    GENERATED BY DEFAULT ON NULL AS IDENTITY,从最大的 id 之后继续

    复制的 id 按原样导入,计数器从最大的 id 之后继续。

  • CREATE SEQUENCE

    CREATE SEQUENCE,选项相同

    Oracle 序列的工作方式相同。SSMS 不会写出序列当前到达的位置,因此它从创建时的值开始。

  • computed columns

    虚拟列

    Oracle 只在读取时计算生成列;PERSISTED 列变为值相同的虚拟列。

    报告记录: stored_as_virtual

  • computed columns with functions Oracle lacks

    普通列

    无法写出的公式会留下一个普通列和一条报告记录;SSMS 不会为计算列写出值。

    报告记录: generated_not_translated

  • defaults with functions Oracle lacks, and NEXT VALUE FOR

    无默认值

    无法写出的默认值会被省略,并在报告中列出。

    报告记录: default_not_translated

  • CHECK constraints

    已转换

    检查约束相同,在导入每一行时检查。

  • CHECK constraints with functions Oracle lacks

    不创建

    无法写出的检查约束会被省略,并在报告中列出。

    报告记录: check_skipped

  • CHECK constraints added WITH NOCHECK or disabled

    不创建

    SQL Server 不执行这些检查约束,因此行可能违反它们。

    报告记录: check_not_enforced

  • filtered and columnstore indexes, and indexes on CLOB columns

    不创建

    IntoSQL 在整列上写出 b-tree 索引;其他索引请在导入后重新创建。

    报告记录: index_skipped

  • foreign keys

    在数据之后以 ENABLE NOVALIDATE 添加,然后验证,有孤立行时给出警告

    外键立即检查新行,最后再验证;有孤立行时外键保持未验证状态并给出警告,而不是中止导入。

  • ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT

    去除

    Oracle 的外键只在删除时执行操作,可用 CASCADE 或 SET NULL。

    报告记录: foreign_key_action_unsupported

  • names over 128 bytes

    缩短

    Oracle 12.2 及更高版本的名称最多 128 字节;SQL Server 的名称最多 128 个字符。

    报告记录: name_shortened

  • foreign keys to tables the dump doesn't create

    不创建

    外键无法引用不存在的表。

    报告记录: foreign_key_skipped

  • tables on a partition scheme

    单个表,包含每个分区的行

    行保持不变;如有需要,请在 Oracle 中重新对该表分区。

    报告记录: partitions_dropped

  • temporal tables (SYSTEM_VERSIONING)

    两个表:当前行和历史记录

    表及其历史表作为普通表复制;Oracle 不会自动保留历史记录。

    报告记录: system_versioning_dropped

  • schemas

    单个 schema,每个表名前加上其 schema 名

    脚本会导入到运行它的用户的 schema 中,因此其他 schema 的表名前会加上其 schema 名;dbo 中的表保留原名。

    报告记录: schemas_merged

  • NOT NULL columns with values that become NULL

    去除 NOT NULL

    值必须变为 NULL 的列无法保留 NOT NULL,否则导入会中止。

    报告记录: not_null_relaxed

  • views, functions, procedures, triggers, synonyms, types and partition functions

    在报告中列出

    它们的代码用 T-SQL 编写,因此会列出来,由你重新创建。

    报告记录: not_converted_objects

  • scripts without CREATE TABLE (data only)

    根据值推断类型

    只含数据的脚本只有行,因此类型来自值。

    报告记录: no_schema

  • expressions in INSERT values

    NULL

    不运行数据库的转换器无法对它们求值。

    报告记录: expression_value

  • types IntoSQL doesn't know

    CLOB

    文本可以容纳任何值;请在导入后更改类型。

    报告记录: unknown_type

  • NUL characters in text

    保留

    VARCHAR2 和 CLOB 可以包含 NUL 字符,因此值按原样复制。

  • text that isn't valid in its encoding

    错误的字节变为替换字符

    脚本是 UTF-8,每个值都必须是有效文本。

    报告记录: invalid_utf8_replaced

  • text decoded twice (ñ)

    按原样复制

    脚本中存储的正是这些字符,因此按原样复制;报告会指出它们。

    报告记录: mojibake_suspected

在 SQL Server 中在 Oracle 中报告记录
bitNUMBER(1),并带有限定 0 和 1 的 CHECKOracle 21c 没有 boolean 列类型;1 和 0 在每个版本上都能保留这些值。
char(n), varchar(n), nchar(n), nvarchar(n)VARCHAR2(n CHAR),按字符计数长度按字符计算,与 SQL Server 的 nvarchar 相同。
varchar(max), nvarchar(max), text, ntext每个值都不超过 4000 字节时为 VARCHAR2(4000 CHAR),否则为 CLOBVARCHAR2 默认容纳 4000 字节,而 Oracle 无法为 CLOB 建立索引,因此值能容纳时文本保持为 VARCHAR2,否则为 CLOB。text_as_lob
empty strings and empty binary valuesNULLOracle 将空字符串存储为 NULL,因此原为 ' 的值读回时为 NULL。报告会按列统计它们。empty_string_null
decimal(p,s), money, smallmoneyNUMBER(p,s);money 为 NUMBER(19,4)NUMBER 与 decimal 一样可容纳 38 位数字,因此每个值都是精确的。
datetime, datetime2, smalldatetimeTIMESTAMP(p)datetime 是挂钟读数,与 Oracle 的 TIMESTAMP 相同,后者最多保留九位秒的小数。date 变为 DATE,它也包含时间,但始终为午夜。
datetimeoffsetTIMESTAMP WITH TIME ZONE,保留每个值的偏移量Oracle 保留 -12:00 到 +14:00 的偏移量;偏移量超出 -12:00 的值以 UTC 写出,仍是同一时刻。
time从午夜起的 INTERVAL DAY TO SECONDOracle 没有表示一天中时间的类型;从午夜起的 interval 可容纳每个值。
dates that aren't validNULL不是日期的值变为 NULL,报告会统计其数量。date_out_of_range
json (SQL Server 2025)CLOBJSON 值相同,Oracle 的 JSON 函数可以读取。
uniqueidentifierVARCHAR2(36)标识符相同,以小写写出。
binary, varbinary, imageRAW(n) 和 BLOB字节仍然是字节。
geometry, geographyBLOB,存放 SQL Server 自己的字节SSMS 以 SQL Server 的序列化格式写出空间值,这些字节会逐字节保留。spatial_as_native
rowversion, timestampBLOB,空行版本由 SQL Server 自动设置,SSMS 不会为其写出值。rowversion_empty
xml, sql_variant, hierarchyid, vectorVARCHAR2 或 CLOB,存放每个值的文本Oracle 没有对应的类型;xml 和 sql_variant 保留每个值的文本,hierarchyid 保留其路径,例如 /1/2/。type_replaced
collations that ignore case (_CI_)精确比较除非另行指定,Oracle 精确比较文本,因此在 SQL Server 中不区分大小写的列现在会区分 Ana 和 ana。case_insensitive_collation
IDENTITYGENERATED BY DEFAULT ON NULL AS IDENTITY,从最大的 id 之后继续复制的 id 按原样导入,计数器从最大的 id 之后继续。
CREATE SEQUENCECREATE SEQUENCE,选项相同Oracle 序列的工作方式相同。SSMS 不会写出序列当前到达的位置,因此它从创建时的值开始。
computed columns虚拟列Oracle 只在读取时计算生成列;PERSISTED 列变为值相同的虚拟列。stored_as_virtual
computed columns with functions Oracle lacks普通列无法写出的公式会留下一个普通列和一条报告记录;SSMS 不会为计算列写出值。generated_not_translated
defaults with functions Oracle lacks, and NEXT VALUE FOR无默认值无法写出的默认值会被省略,并在报告中列出。default_not_translated
CHECK constraints已转换检查约束相同,在导入每一行时检查。
CHECK constraints with functions Oracle lacks不创建无法写出的检查约束会被省略,并在报告中列出。check_skipped
CHECK constraints added WITH NOCHECK or disabled不创建SQL Server 不执行这些检查约束,因此行可能违反它们。check_not_enforced
filtered and columnstore indexes, and indexes on CLOB columns不创建IntoSQL 在整列上写出 b-tree 索引;其他索引请在导入后重新创建。index_skipped
foreign keys在数据之后以 ENABLE NOVALIDATE 添加,然后验证,有孤立行时给出警告外键立即检查新行,最后再验证;有孤立行时外键保持未验证状态并给出警告,而不是中止导入。
ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT去除Oracle 的外键只在删除时执行操作,可用 CASCADE 或 SET NULL。foreign_key_action_unsupported
names over 128 bytes缩短Oracle 12.2 及更高版本的名称最多 128 字节;SQL Server 的名称最多 128 个字符。name_shortened
foreign keys to tables the dump doesn't create不创建外键无法引用不存在的表。foreign_key_skipped
tables on a partition scheme单个表,包含每个分区的行行保持不变;如有需要,请在 Oracle 中重新对该表分区。partitions_dropped
temporal tables (SYSTEM_VERSIONING)两个表:当前行和历史记录表及其历史表作为普通表复制;Oracle 不会自动保留历史记录。system_versioning_dropped
schemas单个 schema,每个表名前加上其 schema 名脚本会导入到运行它的用户的 schema 中,因此其他 schema 的表名前会加上其 schema 名;dbo 中的表保留原名。schemas_merged
NOT NULL columns with values that become NULL去除 NOT NULL值必须变为 NULL 的列无法保留 NOT NULL,否则导入会中止。not_null_relaxed
views, functions, procedures, triggers, synonyms, types and partition functions在报告中列出它们的代码用 T-SQL 编写,因此会列出来,由你重新创建。not_converted_objects
scripts without CREATE TABLE (data only)根据值推断类型只含数据的脚本只有行,因此类型来自值。no_schema
expressions in INSERT valuesNULL不运行数据库的转换器无法对它们求值。expression_value
types IntoSQL doesn't knowCLOB文本可以容纳任何值;请在导入后更改类型。unknown_type
NUL characters in text保留VARCHAR2 和 CLOB 可以包含 NUL 字符,因此值按原样复制。
text that isn't valid in its encoding错误的字节变为替换字符脚本是 UTF-8,每个值都必须是有效文本。invalid_utf8_replaced
text decoded twice (ñ)按原样复制脚本中存储的正是这些字符,因此按原样复制;报告会指出它们。mojibake_suspected

将脚本导入 Oracle

脚本先创建表、复制行,再添加键、索引和外键,因此导入速度快,并会在遇到第一个错误时停止。

NLS_LANG=.AL32UTF8 sqlplus -S -L user@//localhost/mydb @sql_server.oracle.sql

外键以 ENABLE NOVALIDATE 添加,因此新行会立即检查,之后再验证。如果某个旧行指向不存在的行,脚本会说明是哪个约束,并让它保持未验证状态,而不是中途失败。

对于较大的转储,请在数据库所在的机器上运行 SQL*Plus。脚本每 100 行提交一次。运行脚本的用户需要 CREATE TABLE、CREATE SEQUENCE、CREATE TRIGGER 和 CREATE PROCEDURE 权限,以及其表空间上的配额。

如何转储 SQL Server 以获得最佳结果

转换器读取转储中的内容。以下选项能让转储保留更多数据:

  • 在 SSMS 中右键单击数据库,依次选择"任务"、"生成脚本",然后选择表。dbatools、mssql-scripter 以及其他基于同一库的工具也会写出相同的脚本。
  • 在"高级"中,将"要编写脚本的数据的类型"设为"架构和数据"。只含架构的脚本中,表没有行。
  • 以 SSMS 默认的 Unicode 或 UTF-8 保存:两者的读取方式相同,也支持 gzip。

哪些内容不会转换

视图、函数、存储过程、触发器、同义词、规则和安全策略都是用 T-SQL 编写的。它们会按名称在报告中列出,绝不会被悄悄丢弃,以便你为 Oracle 重写。

关于 SQL Server 转 Oracle 的问题

这里没有你要的答案?请通过联系页面给我们留言,会有专人回复。 联系页面

我的转储会被上传吗?

不会。转换器在你的浏览器中运行,分块从磁盘读取转储,因此转储不会离开你的电脑。桌面应用和命令行也在你的电脑上转换。

转储可以多大?

转换器从不把整个转储放进内存:它按范围读取文件两次,边读边写出脚本。几 GB 的转储可以在浏览器、桌面应用和命令行中转换。

它能读取 SSMS 保存的 UTF-16 文件吗?

能。SSMS 默认以 UTF-16 保存脚本,而转换器同样能读取 UTF-16 和 UTF-8,无论是否经过 gzip 压缩,批处理之间的 GO 也能识别。

脚本可以来自哪些 SQL Server 版本?

由 SSMS、dbatools 或 mssql-scripter 从 SQL Server 2017 及更高版本写出的脚本。每个测试脚本都来自 SQL Server 2017、2022 和 2025,并与其源数据逐个单元格比较。

IDENTITY 值会保留吗?

会。IDENTITY 列变为标识列,每一行都保留其 id,下一个 id 从最大的 id 之后继续。序列以相同的选项创建。

money、datetimeoffset 和其他 SQL Server 类型会怎样处理?

bit 变为 NUMBER(1),datetime2 变为保留每一位的 TIMESTAMP,datetimeoffset 变为带偏移量的 TIMESTAMP WITH TIME ZONE,nvarchar(max) 在其值放不进 VARCHAR2 时变为 CLOB。空字符串按 Oracle 的存储方式变为 NULL,报告会统计其数量。

存储过程、触发器和视图怎么处理?

它们会按名称在报告中列出,但不会转换,因为每种数据库都用自己的语言编写它们。

脚本可以导入哪些 Oracle 版本?

Oracle 19c 及更高版本。每个测试脚本都能在 21c 和 23ai 上导入,如有问题会在第一个错误处停止。