PostgreSQL を Oracle に変換
pg_dump または pg_dumpall で作成したプレーン形式のダンプ (.sql または .sql.gz) をドロップすると、同じテーブル、キー、インデックス、データを持つ Oracle スクリプトをダウンロードできます。Oracle がそのまま保持できない値と、引き継がないオブジェクトは、すべてレポートに記載されます。
18 行を変換、修正 2 件、却下なし
shop_2024_postgres.sql のテーブルPostgreSQL 18.6 のダンプ · pg_dump で作成 · utf8mb4
customersCUSTOMERS として書き出し8 行1
8 列 ID NUMBER(10) not null NAME VARCHAR2(4000 CHAR) not null EMAIL VARCHAR2(750 CHAR) not null COUNTRY VARCHAR2(2 CHAR) not null IS_ACTIVE NUMBER(1) not null TAGS CLOB not null SIGNED_UP DATE CREATED_AT TIMESTAMP(6) WITH TIME ZONE not null ordersORDERS として書き出し10 行1
10 列 ID NUMBER(10) not null CUSTOMER_ID NUMBER(10) not null STATUS VARCHAR2(9 CHAR) not null TOTAL NUMBER(10,2) not null ITEMS NUMBER(5) not null ORDERED_AT TIMESTAMP(6) not null SHIPPED_AT TIMESTAMP(6) NOTE VARCHAR2(4000 CHAR) DETAILS CLOB DELIVERY VARCHAR2(100 CHAR)
未変換
- ビュー paid_orders
これらはデータベースごとに独自の言語で書かれます。Oracle 用に書き直してください。
-- Written by IntoSQL from a PostgreSQL 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 "CUSTOMERS" (
"ID" NUMBER(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 1001),
"NAME" VARCHAR2(4000 CHAR) NOT NULL,
"EMAIL" VARCHAR2(750 CHAR) NOT NULL,
"COUNTRY" VARCHAR2(2 CHAR) DEFAULT 'US' NOT NULL,
"IS_ACTIVE" NUMBER(1) DEFAULT 1 NOT NULL,
"TAGS" CLOB NOT NULL,
"SIGNED_UP" DATE,
"CREATED_AT" TIMESTAMP(6) WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
CONSTRAINT "CK_CUSTOMERS_IS_ACTIVE" CHECK ("IS_ACTIVE" IN (0, 1))
);
CREATE TABLE "ORDERS" (
"ID" NUMBER(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 5001),
"CUSTOMER_ID" NUMBER(10) NOT NULL,
"STATUS" VARCHAR2(9 CHAR) DEFAULT 'pending' NOT NULL,
"TOTAL" NUMBER(10,2) NOT NULL,
"ITEMS" NUMBER(5) DEFAULT 1 NOT NULL,
"ORDERED_AT" TIMESTAMP(6) NOT NULL,
"SHIPPED_AT" TIMESTAMP(6),
"NOTE" VARCHAR2(4000 CHAR),
"DETAILS" CLOB,
"DELIVERY" VARCHAR2(100 CHAR),
CONSTRAINT "CK_ORDERS_STATUS" CHECK ("STATUS" IN ('pending', 'paid', 'shipped', 'cancelled')),
CONSTRAINT "ORDERS_TOTAL_CHECK" CHECK ("TOTAL" >= (0))
);
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
1,
'Ana Martínez',
'ana@example.com',
'MX',
1,
'["vip"]',
DATE '2023-01-15',
TIMESTAMP '2023-01-15 10:24:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
2,
'Zoë Müller',
'zoe@example.de',
'DE',
1,
'[]',
DATE '2023-02-03',
TIMESTAMP '2023-02-03 08:11:42 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
3,
'José Peña',
'jose@example.es',
'ES',
0,
'["wholesale","late payer"]',
DATE '2023-03-21',
TIMESTAMP '2023-03-21 19:05:17 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
4,
'李雷',
'lilei@example.cn',
'CN',
1,
'[]',
DATE '2023-05-09',
TIMESTAMP '2023-05-09 02:40:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
5,
'Noah O''Brien',
'noah@example.ie',
'IE',
1,
'[]',
NULL,
TIMESTAMP '2023-06-30 14:00:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
6,
'Amélie Laurent',
'amelie@example.fr',
'FR',
1,
'["vip"]',
DATE '2024-01-02',
TIMESTAMP '2024-01-02 09:30:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
7,
'Ravi Patel',
'ravi@example.in',
'IN',
0,
'[]',
DATE '2024-02-29',
TIMESTAMP '2024-02-29 23:59:59 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
8,
'Sofia Rossi 🌻',
'sofia@example.it',
'IT',
1,
'[]',
DATE '2024-04-12',
TIMESTAMP '2024-04-12 12:12:12 +00:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
1,
1,
'shipped',
129.90,
3,
TIMESTAMP '2024-03-01 10:00:00',
TIMESTAMP '2024-03-02 16:30:00',
NULL,
'{"gift": false}',
'1 day 06:30:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
2,
2,
'paid',
54.00,
1,
TIMESTAMP '2024-03-02 11:15:00',
NULL,
'Gift wrap 🎁',
'{"gift": true, "wrap": "red"}',
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
3,
1,
'cancelled',
12.50,
1,
TIMESTAMP '2024-03-03 09:45:00',
NULL,
'Customer changed their mind',
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
4,
4,
'shipped',
980.00,
12,
TIMESTAMP '2024-03-05 02:10:00',
TIMESTAMP '2024-03-07 08:00:00',
'请小心轻放',
'{"fragile": true}',
'2 days 05:50:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
5,
5,
'pending',
45.75,
2,
TIMESTAMP '2024-03-08 17:20:00',
NULL,
'Leave at the door; ring twice',
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
6,
6,
'shipped',
310.40,
5,
TIMESTAMP '2024-03-10 13:00:00',
TIMESTAMP '2024-03-11 09:00:00',
'Fragile: "glass"',
'{"fragile": true}',
'20:00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
7,
8,
'paid',
76.20,
2,
TIMESTAMP '2024-03-11 18:45:00',
NULL,
NULL,
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
8,
2,
'shipped',
19.99,
1,
TIMESTAMP '2024-03-12 08:00:00',
NULL,
'Shipped date set to infinity by an old import',
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
9,
3,
'shipped',
64.00,
2,
TIMESTAMP '2024-03-14 15:30:00',
TIMESTAMP '2024-03-15 10:00:00',
'Two parcels',
'{"parcels": [1, 2]}',
'18:30:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
10,
7,
'paid',
1499.00,
1,
TIMESTAMP '2024-03-15 21:05:00',
NULL,
'C:\Users\ravi\invoice.pdf',
NULL,
NULL
);
COMMIT;
END;
/
-- Keys, indexes, foreign keys, counters and triggers, after the rows.
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "UQ_CUSTOMERS_CUSTOMERS_EMAIL_KEY" UNIQUE ("EMAIL");
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "PK_CUSTOMERS" PRIMARY KEY ("ID");
ALTER TABLE "ORDERS" ADD CONSTRAINT "PK_ORDERS" PRIMARY KEY ("ID");
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 "ORDERS_CUSTOMER_ID_FKEY" FOREIGN KEY ("CUSTOMER_ID") REFERENCES "CUSTOMERS" ("ID") ENABLE NOVALIDATE;
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" ENABLE VALIDATE CONSTRAINT "ORDERS_CUSTOMER_ID_FKEY"';
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Foreign key ' || '"ORDERS_CUSTOMER_ID_FKEY"' || ' 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 "CUSTOMERS";
IF next_id < 1001 THEN
next_id := 1001;
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';
DECLARE
next_id NUMBER;
BEGIN
SELECT NVL(MAX("ID"), 0) + 1 INTO next_id FROM "ORDERS";
IF next_id < 5001 THEN
next_id := 5001;
END IF;
EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
DROP FUNCTION "INTOSQL_CLOB";
DROP FUNCTION "INTOSQL_BLOB";
COMMIT;
EXIT;
修正
- customers の "tags" は text[] ですが、Oracle にはないため、同じ値を持つ CLOB にしました。
- orders の "delivery" は interval ですが、Oracle にはないため、同じ値を持つ VARCHAR2(100 CHAR) にしました。
確認が必要
- 1 件のビュー、トリガー、ルーチン、イベントは変換されません。レポートにそれぞれ記載しています。 Oracle で手動で作り直してください。
- customers の "tags" のデフォルト値は Oracle 用に書き換えられないため (a value its type can't hold)、列にデフォルト値はありません。 新しい行に必要なら、手動でデフォルト値を追加してください。
- orders のインデックス idx_orders_details は Oracle 用に書き換えられない式を使っているため (a gin index)、作成しません。 クエリに必要なら、手動でインデックスを作成してください。
- orders の "shipped_at" にある 1 個の値は infinity のように Oracle が保存できない日付のため、NULL にしました。 Oracle が保存できるのは 1 年から 9999 年までで、infinity はありません。必要ならソースでその日付を置き換えてください。
ファイルはこのコンピューター上で変換され、アップロードされることはありません。
その他のサンプル:
PostgreSQL から Oracle で変わること
各行はコンバーターが適用するルールです。ルールによってデータが変わったときにレポートに書かれる行も示します。
boolean
0 と 1 の CHECK 付きの NUMBER(1)
Oracle 21c には boolean 列型がありません。1 と 0 ならどのバージョンでも true と false を保てます。
varchar(n), char(n)
VARCHAR2(n CHAR)、文字数で計算
長さは PostgreSQL と同じく文字数で数えます。
text
すべての値が 4000 バイト (キー内では 750 文字) に収まれば VARCHAR2(4000 CHAR)、それ以外は CLOB
VARCHAR2 はデフォルトで 4000 バイトまでで、Oracle は CLOB にインデックスを作れません。そのためテキストは値が収まれば VARCHAR2 のまま、収まらなければ CLOB になります。
レポートの行: text_as_lob
empty strings and empty bytea values
NULL
Oracle は空文字列を NULL として保存するため、' だった値は NULL として読み出されます。レポートで列ごとの件数を示します。
レポートの行: empty_string_null
citext
厳密な比較
Oracle は PostgreSQL のテキストと同じく、指定がない限りテキストを厳密に比較します。citext 列は大文字と小文字を区別しない動作を失います。
レポートの行: case_insensitive_collation
numeric(p,s) past 38 digits; numeric without a precision
値が 38 桁に収まれば NUMBER、それ以外は VARCHAR2
Oracle の NUMBER はどのスケールでも 38 桁までです。それより大きな数はすべての桁を正確に保つためテキストにします。
レポートの行: decimal_as_text
money
NUMBER(19,2)
Oracle には money 型がありません。金額は、サーバーのロケールが付けた通貨記号を除いて数値として保ちます。
レポートの行: type_replaced
interval
PostgreSQL のテキストを持つ VARCHAR2(100 CHAR)
interval は月と日・秒を組み合わせますが、Oracle ではこれらを 2 つの異なる型で扱います。テキストならすべての部分を保てます。
レポートの行: type_replaced
arrays (integer[], text[]...)
JSON 配列を持つ CLOB
Oracle には配列がありません。JSON 配列ならすべての要素と NULL を保ち、JSON_TABLE で読めます。
レポートの行: type_replaced
ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types
PostgreSQL のテキストを持つ VARCHAR2 または CLOB
Oracle には同等の型がないため、各値は PostgreSQL が書き出したテキストを保ちます。
レポートの行: type_replaced
json, jsonb
CLOB
Oracle の JSON 関数はどのバージョンでもテキストから JSON を読み取れ、CLOB ならどんなサイズの値も保てます。
timestamp
TIMESTAMP
タイムゾーンなしの timestamp は、Oracle の TIMESTAMP と同じく時計が示す時刻です。date は DATE になり、時刻も持ちますが午前 0 時のままです。
timestamp with time zone
UTC の TIMESTAMP WITH TIME ZONE
各値は UTC で書き出し、ダンプのオフセットにかかわらず同じ時点です。
time
午前0時からの INTERVAL DAY TO SECOND
Oracle には時刻の型がありません。午前 0 時からの interval なら 24:00:00 を含むすべての値を保持できます。
infinity, -infinity, BC dates and years past 9999
NULL
スクリプトが書き出すのは 1 年から 9999 年までで、Oracle には infinity がありません。それらの値は NULL になり、レポートで件数を示します。
レポートの行: date_out_of_range
NaN and Infinity in numeric
NULL
Oracle の NUMBER には NaN や Infinity がないため、それらの値は NULL になり、レポートで件数を示します。
レポートの行: invalid_value_null
NOT NULL columns with values that become NULL
NOT NULL を削除
値を NULL にする必要があった列は、NOT NULL を保てません。保つと読み込みが止まります。
レポートの行: not_null_relaxed
enum types
ラベルの CHECK 付きの VARCHAR2
チェック付きのテキストで同じ値を保ちます。
enum columns holding values outside their labels
チェックなしの VARCHAR2
ラベルにない値はチェックに違反するため、チェックは作成しません。
レポートの行: enum_check_skipped
uuid, inet, cidr, macaddr
VARCHAR2
Oracle にはこれらの型がありません。テキストならすべての値を保てます。
bytea
BLOB
バイト列はバイト列のままです。
bit(n), bit varying
63 ビットまではビットの値を持つ NUMBER、それを超えるとテキストの桁
ビット列は、そのビットが表す数値として保存します。
oid, xid, cid
負でないことを確かめる CHECK 付きの NUMBER(10)
NUMBER はすべての値を保持できます。
serial, identity
GENERATED BY DEFAULT ON NULL AS IDENTITY、最大の ID の後から継続
コピーした ID はそのまま読み込み、カウンターは最大の ID とシーケンスの次の値のうち大きい方の後から続きます。
a second serial or identity column in a table
普通の整数
Oracle が採番する IDENTITY 列はテーブルごとに 1 つだけです。それ以外の列はカウンターなしで値を保ちます。
レポートの行: counter_dropped
sequences not owned by a column
到達していた値から始まる CREATE SEQUENCE
Oracle のシーケンスも同じように動作します。
generated columns
仮想列
Oracle は生成列を読み取り時にしか計算しません。STORED の列は同じ値を持つ仮想列になります。
レポートの行: stored_as_virtual
generated columns with functions Oracle lacks
値を持つ普通の列
Oracle 用に書き換えられない式は、ダンプの値を持つ普通の列とレポートの 1 行になります。
レポートの行: generated_not_translated
defaults with functions Oracle lacks, and defaults from a sequence
デフォルト値なし
Oracle 用に書き換えられないデフォルト値は含めず、レポートに記載します。
レポートの行: default_not_translated
CHECK constraints
変換して CREATE TABLE 内に記述
インラインのチェックは各行の読み込み時にテストされます。
CHECK constraints with functions Oracle lacks
作成しない
Oracle 用に書き換えられないチェックは含めず、レポートに記載します。
レポートの行: check_skipped
CHECK ... NOT VALID
作成しない
PostgreSQL は既存の行をチェックしていないため、違反している可能性があります。
レポートの行: check_not_valid
gin, gist, brin and hash indexes, partial and expression indexes, and indexes on CLOB columns
作成しない
IntoSQL が Oracle 用に書き出すのは列に対する b-tree インデックスです。Oracle は CLOB にインデックスを作れません。その他は読み込み後に作り直してください。
レポートの行: 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
foreign keys to tables the dump doesn't create
作成しない
キーは存在しないテーブルを参照できません。
レポートの行: foreign_key_skipped
names over 128 bytes
短縮
Oracle 12.2 以降の名前は 128 バイトまでで、PostgreSQL の 63 より長くできます。
レポートの行: name_shortened
partitioned tables
すべてのパーティションの行を持つ 1 つのテーブル
行は同じです。必要なら Oracle でもう一度テーブルをパーティション分割してください。
レポートの行: partitions_dropped
schemas
1 つのスキーマ、テーブル名の先頭にスキーマ名
スクリプトは実行したユーザーのスキーマに読み込むため、異なるスキーマのテーブルには先頭にスキーマ名を付けます。public のテーブルは名前を保ちます。
レポートの行: schemas_merged
views, functions, procedures, triggers, rules, policies, extensions and types
レポートに記載
コードが PostgreSQL の言語で書かれているため、作り直せるよう一覧にします。
レポートの行: not_converted_objects
dumps without CREATE TABLE (--data-only)
値から読み取った型
データのみのダンプには行しかないため、型は値から決まります。
レポートの行: no_schema
expressions in INSERT values
NULL
データベースを実行しないコンバーターでは、これらを評価できません。
レポートの行: expression_value
types IntoSQL doesn't know
CLOB
テキストはどんな値も保持できます。読み込み後に型を変更してください。
レポートの行: unknown_type
text that isn't valid in its encoding
不正なバイトを置換文字に
スクリプトは UTF-8 で、NLS_LANG=.AL32UTF8 で読み込みます。
レポートの行: invalid_utf8_replaced
text decoded twice (ñ)
そのままコピー
ダンプにはまさにこの文字が含まれているため、そのままコピーし、レポートで指摘します。
レポートの行: mojibake_suspected
| PostgreSQL では | Oracle では | レポートの行 |
|---|---|---|
| boolean | 0 と 1 の CHECK 付きの NUMBER(1)Oracle 21c には boolean 列型がありません。1 と 0 ならどのバージョンでも true と false を保てます。 | |
| varchar(n), char(n) | VARCHAR2(n CHAR)、文字数で計算長さは PostgreSQL と同じく文字数で数えます。 | |
| text | すべての値が 4000 バイト (キー内では 750 文字) に収まれば VARCHAR2(4000 CHAR)、それ以外は CLOBVARCHAR2 はデフォルトで 4000 バイトまでで、Oracle は CLOB にインデックスを作れません。そのためテキストは値が収まれば VARCHAR2 のまま、収まらなければ CLOB になります。 | text_as_lob |
| empty strings and empty bytea values | NULLOracle は空文字列を NULL として保存するため、' だった値は NULL として読み出されます。レポートで列ごとの件数を示します。 | empty_string_null |
| citext | 厳密な比較Oracle は PostgreSQL のテキストと同じく、指定がない限りテキストを厳密に比較します。citext 列は大文字と小文字を区別しない動作を失います。 | case_insensitive_collation |
| numeric(p,s) past 38 digits; numeric without a precision | 値が 38 桁に収まれば NUMBER、それ以外は VARCHAR2Oracle の NUMBER はどのスケールでも 38 桁までです。それより大きな数はすべての桁を正確に保つためテキストにします。 | decimal_as_text |
| money | NUMBER(19,2)Oracle には money 型がありません。金額は、サーバーのロケールが付けた通貨記号を除いて数値として保ちます。 | type_replaced |
| interval | PostgreSQL のテキストを持つ VARCHAR2(100 CHAR)interval は月と日・秒を組み合わせますが、Oracle ではこれらを 2 つの異なる型で扱います。テキストならすべての部分を保てます。 | type_replaced |
| arrays (integer[], text[]...) | JSON 配列を持つ CLOBOracle には配列がありません。JSON 配列ならすべての要素と NULL を保ち、JSON_TABLE で読めます。 | type_replaced |
| ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types | PostgreSQL のテキストを持つ VARCHAR2 または CLOBOracle には同等の型がないため、各値は PostgreSQL が書き出したテキストを保ちます。 | type_replaced |
| json, jsonb | CLOBOracle の JSON 関数はどのバージョンでもテキストから JSON を読み取れ、CLOB ならどんなサイズの値も保てます。 | |
| timestamp | TIMESTAMPタイムゾーンなしの timestamp は、Oracle の TIMESTAMP と同じく時計が示す時刻です。date は DATE になり、時刻も持ちますが午前 0 時のままです。 | |
| timestamp with time zone | UTC の TIMESTAMP WITH TIME ZONE各値は UTC で書き出し、ダンプのオフセットにかかわらず同じ時点です。 | |
| time | 午前0時からの INTERVAL DAY TO SECONDOracle には時刻の型がありません。午前 0 時からの interval なら 24:00:00 を含むすべての値を保持できます。 | |
| infinity, -infinity, BC dates and years past 9999 | NULLスクリプトが書き出すのは 1 年から 9999 年までで、Oracle には infinity がありません。それらの値は NULL になり、レポートで件数を示します。 | date_out_of_range |
| NaN and Infinity in numeric | NULLOracle の NUMBER には NaN や Infinity がないため、それらの値は NULL になり、レポートで件数を示します。 | invalid_value_null |
| NOT NULL columns with values that become NULL | NOT NULL を削除値を NULL にする必要があった列は、NOT NULL を保てません。保つと読み込みが止まります。 | not_null_relaxed |
| enum types | ラベルの CHECK 付きの VARCHAR2チェック付きのテキストで同じ値を保ちます。 | |
| enum columns holding values outside their labels | チェックなしの VARCHAR2ラベルにない値はチェックに違反するため、チェックは作成しません。 | enum_check_skipped |
| uuid, inet, cidr, macaddr | VARCHAR2Oracle にはこれらの型がありません。テキストならすべての値を保てます。 | |
| bytea | BLOBバイト列はバイト列のままです。 | |
| bit(n), bit varying | 63 ビットまではビットの値を持つ NUMBER、それを超えるとテキストの桁ビット列は、そのビットが表す数値として保存します。 | |
| oid, xid, cid | 負でないことを確かめる CHECK 付きの NUMBER(10)NUMBER はすべての値を保持できます。 | |
| serial, identity | GENERATED BY DEFAULT ON NULL AS IDENTITY、最大の ID の後から継続コピーした ID はそのまま読み込み、カウンターは最大の ID とシーケンスの次の値のうち大きい方の後から続きます。 | |
| a second serial or identity column in a table | 普通の整数Oracle が採番する IDENTITY 列はテーブルごとに 1 つだけです。それ以外の列はカウンターなしで値を保ちます。 | counter_dropped |
| sequences not owned by a column | 到達していた値から始まる CREATE SEQUENCEOracle のシーケンスも同じように動作します。 | |
| generated columns | 仮想列Oracle は生成列を読み取り時にしか計算しません。STORED の列は同じ値を持つ仮想列になります。 | stored_as_virtual |
| generated columns with functions Oracle lacks | 値を持つ普通の列Oracle 用に書き換えられない式は、ダンプの値を持つ普通の列とレポートの 1 行になります。 | generated_not_translated |
| defaults with functions Oracle lacks, and defaults from a sequence | デフォルト値なしOracle 用に書き換えられないデフォルト値は含めず、レポートに記載します。 | default_not_translated |
| CHECK constraints | 変換して CREATE TABLE 内に記述インラインのチェックは各行の読み込み時にテストされます。 | |
| CHECK constraints with functions Oracle lacks | 作成しないOracle 用に書き換えられないチェックは含めず、レポートに記載します。 | check_skipped |
| CHECK ... NOT VALID | 作成しないPostgreSQL は既存の行をチェックしていないため、違反している可能性があります。 | check_not_valid |
| gin, gist, brin and hash indexes, partial and expression indexes, and indexes on CLOB columns | 作成しないIntoSQL が Oracle 用に書き出すのは列に対する b-tree インデックスです。Oracle は CLOB にインデックスを作れません。その他は読み込み後に作り直してください。 | 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 |
| foreign keys to tables the dump doesn't create | 作成しないキーは存在しないテーブルを参照できません。 | foreign_key_skipped |
| names over 128 bytes | 短縮Oracle 12.2 以降の名前は 128 バイトまでで、PostgreSQL の 63 より長くできます。 | name_shortened |
| partitioned tables | すべてのパーティションの行を持つ 1 つのテーブル行は同じです。必要なら Oracle でもう一度テーブルをパーティション分割してください。 | partitions_dropped |
| schemas | 1 つのスキーマ、テーブル名の先頭にスキーマ名スクリプトは実行したユーザーのスキーマに読み込むため、異なるスキーマのテーブルには先頭にスキーマ名を付けます。public のテーブルは名前を保ちます。 | schemas_merged |
| views, functions, procedures, triggers, rules, policies, extensions and types | レポートに記載コードが PostgreSQL の言語で書かれているため、作り直せるよう一覧にします。 | not_converted_objects |
| dumps without CREATE TABLE (--data-only) | 値から読み取った型データのみのダンプには行しかないため、型は値から決まります。 | no_schema |
| expressions in INSERT values | NULLデータベースを実行しないコンバーターでは、これらを評価できません。 | expression_value |
| types IntoSQL doesn't know | CLOBテキストはどんな値も保持できます。読み込み後に型を変更してください。 | unknown_type |
| text that isn't valid in its encoding | 不正なバイトを置換文字にスクリプトは UTF-8 で、NLS_LANG=.AL32UTF8 で読み込みます。 | invalid_utf8_replaced |
| text decoded twice (ñ) | そのままコピーダンプにはまさにこの文字が含まれているため、そのままコピーし、レポートで指摘します。 | mojibake_suspected |
スクリプトを Oracle に読み込む
スクリプトはテーブルを作成して行をコピーしてから、キー、インデックス、外部キーを追加します。そのため読み込みが速く、最初のエラーで止まります。
NLS_LANG=.AL32UTF8 sqlplus -S -L user@//localhost/mydb @postgresql.oracle.sql外部キーは ENABLE NOVALIDATE で追加するため新しい行はすぐにチェックされ、その後に検証します。古い行が存在しない行を参照していれば、途中で失敗する代わりに、どの制約かをスクリプトが示し、その制約は未検証のまま残します。
大きなダンプでは、データベースと同じマシンで SQL*Plus を実行してください。スクリプトは 100 行ごとにコミットします。実行するユーザーには CREATE TABLE、CREATE SEQUENCE、CREATE TRIGGER、CREATE PROCEDURE の権限と、表領域の割り当てが必要です。
PostgreSQL を最適な方法でダンプする
コンバーターはダンプの内容を読み取ります。次のオプションを使うと、より多くのデータがダンプに残ります。
- pg_dump のデフォルトである --format=plain は SQL を書き出します。custom または directory 形式のアーカイブ (-Fc, -Fd) は、pg_restore -f dump.sql でデータベースサーバーなしに SQL にできます。
- --no-owner --no-privileges を使うと、コンバーターがどのみちスキップするロールと権限付与が除かれ、ダンプが小さくなります。
- pg_dumpall はすべてのデータベースを 1 つのファイルにダンプします。各テーブルにはデータベース名を付けるため、名前は衝突しません。
変換されないもの
ビュー、マテリアライズドビュー、関数、プロシージャ、トリガー、ルール、行セキュリティポリシー、拡張機能、型は PostgreSQL 独自の言語で書かれています。黙って削除することはなく、レポートに名前で一覧するので、Oracle 向けに書き直せます。
PostgreSQL から Oracle への変換に関する質問
ここに答えがない場合は、お問い合わせページからご連絡ください。担当者が返信します。 お問い合わせページ
ダンプはアップロードされますか?
いいえ。コンバーターはブラウザ上で動作し、ダンプをディスクから少しずつ読み込むため、ダンプがコンピューターの外に出ることはありません。デスクトップアプリとコマンドラインも、お使いのコンピューター上で変換します。
ダンプはどのくらいの大きさまで扱えますか?
コンバーターはダンプ全体をメモリに保持しません。ファイルを範囲ごとに 2 回読み、読みながらスクリプトを書き出します。数 GB のダンプも、ブラウザ、デスクトップアプリ、コマンドラインで変換できます。
pg_dump -Fc のアーカイブを読めますか?
直接は読めません。custom 形式のアーカイブはバイナリ形式です。pg_restore -f dump.sql dump.backup を使えば、サーバーに接続せずにコンバーターが読めるプレーンな SQL にできます。アーカイブを受け取るとコンバーターがその旨を表示します。
どの PostgreSQL バージョンのダンプに対応していますか?
PostgreSQL 10 以降の pg_dump と pg_dumpall で作成した、COPY または INSERT の行を含むダンプです。すべてのテスト用ダンプは PostgreSQL 13 と 18 で作成し、ソースとセル単位で比較しています。
serial と identity の値は保持されますか?
はい。各テーブルの serial または identity の列は IDENTITY 列になり、すべての行が ID を保ち、次の ID は最大の ID の後から続きます。列に属さないシーケンスは、到達していた値から始まる Oracle のシーケンスになります。
配列、enum など PostgreSQL の型はどうなりますか?
配列は JSON 配列、boolean は NUMBER(1)、タイムゾーン付きの timestamp は TIMESTAMP WITH TIME ZONE、text は値が収まれば VARCHAR2、それ以外は CLOB になります。空文字列は Oracle の保存方法どおり NULL になり、レポートで件数を示します。
関数、トリガー、ビューはどうなりますか?
データベースごとに独自の言語で書かれるため、変換はせず、レポートに名前で一覧します。
スクリプトはどの Oracle バージョンで読み込めますか?
Oracle 19c 以降です。すべてのテスト用ダンプを 21c と 23ai で読み込み、問題があれば最初のエラーで止まります。