Oracle 내보내기를 이전 또는 최신 Oracle로 옮기기
SQL Developer 데이터베이스 내보내기나 SQLcl이 쓴 테이블과 Insert 문의 .sql 파일을 놓으면 Oracle 19c 이상에서 불러와지는 스크립트를 받아요. NUMBER, DATE, TIMESTAMP, LOB, IDENTITY, 시퀀스는 그대로이고, XMLTYPE과 JSON은 텍스트를 CLOB에 담으며, 모든 변경은 보고서에 나와요.
18개 행 변환, 수정 없음, 거부 없음
shop_2024_oracle.sql의 테이블Oracle 덤프 · SQL Developer(으)로 작성됨 · utf-8
customers새 이름: CUSTOMERS8개 행
7개 열 ID NUMBER(10,0) not null NAME VARCHAR2(100 CHAR) not null EMAIL VARCHAR2(150 CHAR) not null COUNTRY CHAR(2 BYTE) not null IS_ACTIVE NUMBER(1,0) not null SIGNED_UP DATE CREATED_AT TIMESTAMP(0) not null orders새 이름: ORDERS10개 행
9개 열 ID NUMBER(10,0) not null CUSTOMER_ID NUMBER(10,0) not null INVOICE NUMBER(10,0) STATUS VARCHAR2(10 CHAR) not null TOTAL NUMBER(10,2) not null ITEMS NUMBER(5,0) not null ORDERED_AT DATE not null SHIPPED_AT DATE NOTE VARCHAR2(255 CHAR)
-- Written by IntoSQL from a Oracle 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 SEQUENCE "INVOICE_NUMBERS" START WITH 1020 INCREMENT BY 1 MINVALUE 1;
CREATE TABLE "CUSTOMERS" (
"ID" NUMBER(10,0) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 1),
"NAME" VARCHAR2(100 CHAR) NOT NULL,
"EMAIL" VARCHAR2(150 CHAR) NOT NULL,
"COUNTRY" CHAR(2 BYTE) DEFAULT 'US' NOT NULL,
"IS_ACTIVE" NUMBER(1,0) DEFAULT 1 NOT NULL,
"SIGNED_UP" DATE,
"CREATED_AT" TIMESTAMP(0) DEFAULT LOCALTIMESTAMP NOT NULL,
CONSTRAINT "CHECK1" CHECK ("IS_ACTIVE" IN (0, 1))
);
CREATE TABLE "ORDERS" (
"ID" NUMBER(10,0) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 1),
"CUSTOMER_ID" NUMBER(10,0) NOT NULL,
"INVOICE" NUMBER(10,0) DEFAULT ("INVOICE_NUMBERS".NEXTVAL),
"STATUS" VARCHAR2(10 CHAR) DEFAULT 'pending' NOT NULL,
"TOTAL" NUMBER(10,2) NOT NULL,
"ITEMS" NUMBER(5,0) DEFAULT 1 NOT NULL,
"ORDERED_AT" DATE NOT NULL,
"SHIPPED_AT" DATE,
"NOTE" VARCHAR2(255 CHAR),
CONSTRAINT "CHECK1_2" CHECK ("STATUS" IN ('pending', 'paid', 'shipped', 'cancelled'))
);
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
1,
'Ana Martínez',
'ana@example.com',
'MX',
1,
TIMESTAMP '2023-01-15 00:00:00',
TIMESTAMP '2023-01-15 10:24:00.000000000'
);
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,
TIMESTAMP '2023-02-03 00:00:00',
TIMESTAMP '2023-02-03 08:11:42.000000000'
);
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,
TIMESTAMP '2023-03-21 00:00:00',
TIMESTAMP '2023-03-21 19:05:17.000000000'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
4,
'李雷',
'lilei@example.cn',
'CN',
1,
TIMESTAMP '2023-05-09 00:00:00',
TIMESTAMP '2023-05-09 02:40:00.000000000'
);
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 14:00:00.000000000'
);
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,
TIMESTAMP '2024-01-02 00:00:00',
TIMESTAMP '2024-01-02 09:30:00.000000000'
);
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,
TIMESTAMP '2024-02-29 00:00:00',
TIMESTAMP '2024-02-29 23:59:59.000000000'
);
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,
TIMESTAMP '2024-04-12 00:00:00',
TIMESTAMP '2024-04-12 12:12:12.000000000'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
1,
1,
1000,
'shipped',
129.9,
3,
TIMESTAMP '2024-03-01 10:00:00',
TIMESTAMP '2024-03-02 16:30:00',
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
2,
2,
1001,
'paid',
54,
1,
TIMESTAMP '2024-03-02 11:15:00',
NULL,
'Gift wrap 🎁'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
3,
1,
1002,
'cancelled',
12.5,
1,
TIMESTAMP '2024-03-03 09:45:00',
NULL,
'Customer changed their mind'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
4,
4,
1003,
'shipped',
980,
12,
TIMESTAMP '2024-03-05 02:10:00',
TIMESTAMP '2024-03-07 08:00:00',
'请小心轻放'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
5,
5,
1004,
'pending',
45.75,
2,
TIMESTAMP '2024-03-08 17:20:00',
NULL,
'Leave at the door; ring twice'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
6,
6,
1005,
'shipped',
310.4,
5,
TIMESTAMP '2024-03-10 13:00:00',
TIMESTAMP '2024-03-11 09:00:00',
'Fragile: "glass"'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
7,
8,
1006,
'paid',
76.2,
2,
TIMESTAMP '2024-03-11 18:45:00',
NULL,
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
8,
2,
1007,
'paid',
19.99,
1,
TIMESTAMP '2024-03-12 08:00:00',
NULL,
'Paid at the counter'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
9,
3,
1008,
'shipped',
64,
2,
TIMESTAMP '2024-03-14 15:30:00',
NULL,
'Shipping date never recorded'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
10,
7,
1009,
'paid',
1499,
1,
TIMESTAMP '2024-03-15 21:05:00',
NULL,
'C:\Users\ravi\invoice.pdf'
);
COMMIT;
END;
/
-- Keys, indexes, foreign keys, counters and triggers, after the rows.
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "PK_CUSTOMERS" PRIMARY KEY ("ID");
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "UQ_CUSTOMERS_CUSTOMERS_EMAIL_UK" UNIQUE ("EMAIL");
ALTER TABLE "ORDERS" ADD CONSTRAINT "PK_ORDERS" PRIMARY KEY ("ID");
CREATE INDEX "IX_ORDERS_ORDERS_STATUS_DATE_IX" ON "ORDERS" ("STATUS", "ORDERED_AT");
ALTER TABLE "ORDERS" ADD CONSTRAINT "ORDERS_CUSTOMER_FK" FOREIGN KEY ("CUSTOMER_ID") REFERENCES "CUSTOMERS" ("ID") ENABLE NOVALIDATE;
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" ENABLE VALIDATE CONSTRAINT "ORDERS_CUSTOMER_FK"';
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Foreign key ' || '"ORDERS_CUSTOMER_FK"' || ' 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 < 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';
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;
/
DROP FUNCTION "INTOSQL_CLOB";
DROP FUNCTION "INTOSQL_BLOB";
COMMIT;
EXIT;
수정 사항
수정할 부분이 없었어요.
파일은 이 컴퓨터에서 변환되고 업로드되지 않아요.
다른 샘플:
파일과 스크립트에서 바뀌는 점
각 행은 변환기가 적용하는 규칙이며, 규칙이 데이터를 바꿀 때 보고서에 쓰는 줄이 함께 나와요.
BOOLEAN (23ai)
23ai에서는 BOOLEAN, 아니면 0과 1의 CHECK가 붙은 NUMBER(1)
TRUE와 FALSE는 true와 false 그대로예요.
VARCHAR2(n BYTE|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONG
같은 타입, BYTE 또는 CHAR 길이 유지
같은 텍스트 타입이에요. LONG은 버전 8 이후 모든 버전이 권장하는 CLOB이 돼요.
VARCHAR2 and NVARCHAR2 past what the target's own types hold
모든 값이 4000바이트에 들어가면 VARCHAR2(4000 CHAR), 아니면 CLOB
VARCHAR2는 기본으로 4000바이트까지 담고 Oracle은 CLOB을 인덱싱할 수 없어요. 그래서 텍스트는 값이 들어가면 VARCHAR2, 아니면 CLOB이 돼요.
보고서 줄: text_as_lob
NUMBER, NUMBER(p,s), FLOAT(b)
같은 NUMBER 타입
같은 NUMBER와 FLOAT 타입으로, 모든 자릿수를 유지해요.
INTERVAL YEAR TO MONTH, INTERVAL DAY TO SECOND
같은 INTERVAL 타입
같은 interval이에요.
DATE, TIMESTAMP(p)
선언한 대로 DATE와 TIMESTAMP(p)
같은 타입이라 모든 날짜가 초 단위까지 시각을 유지해요.
TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE
같은 타입
Oracle은 -12:00부터 +14:00까지의 오프셋을 유지해요. -12:00을 넘는 오프셋의 값은 UTC로 써서 같은 시점을 유지해요.
JSON (21c), and CLOB holding JSON
21c 이상은 JSON, 아니면 CLOB
같은 JSON 값이에요. 내보내기가 'null'로 쓴 SQL NULL은 NULL 그대로예요.
BLOB, RAW(n), LONG RAW
같은 타입, LONG RAW는 BLOB
바이트는 바이트 그대로 남아요.
ROWID, UROWID, XMLTYPE, SDO_GEOMETRY
텍스트를 담은 CLOB
XMLTYPE, ROWID, UROWID는 텍스트를 CLOB에 유지하므로 스크립트를 19c 이상의 모든 버전에서 불러올 수 있어요.
보고서 줄: type_replaced
collations that ignore case (BINARY_CI)
정확한 비교
열 콜레이션은 쓰지 않으므로, 대소문자를 무시하던 열(BINARY_CI)도 이제 Ana와 ana를 구분해요.
보고서 줄: case_insensitive_collation
identity columns, and a sequence a BEFORE INSERT trigger assigns
GENERATED BY DEFAULT ON NULL AS IDENTITY, 가장 큰 id 다음부터 이어짐
복사한 id는 그대로 불러오고, 카운터는 가장 큰 id 다음부터 이어져요. 트리거가 시퀀스로 값을 채우는 열(12c 이전 방식)도 identity 열이 돼요.
identity columns the target can't count
일반 정수
Oracle은 테이블마다 identity 열 하나만 번호를 매겨요. 다른 열은 카운터 없이 값을 유지해요.
보고서 줄: counter_dropped
CREATE SEQUENCE
같은 옵션의 CREATE SEQUENCE
내보내기는 시퀀스가 도달한 위치를 START WITH로 쓰므로, 시퀀스는 거기서부터 이어져요.
virtual columns (GENERATED ALWAYS AS ... VIRTUAL)
가상 열
같은 가상 열이에요.
보고서 줄: stored_as_virtual
virtual columns with functions IntoSQL doesn't write, and the || operator
일반 열
Oracle용으로 쓸 수 없는 수식은 일반 열로 남고 보고서에 한 줄 기록돼요. 내보내기가 가상 열에 쓴 값은 제외되고, Oracle이 계산할 값도 없어요.
보고서 줄: 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
데이터 뒤에 ENABLE NOVALIDATE로 만들고, 고아 행은 경고와 함께 검증
키는 새 행을 바로 검사하고 마지막에 검증해요. 부모 행이 없는 행이 있으면 불러오기를 멈추지 않고 경고와 함께 검증하지 않은 상태로 남겨요.
ON DELETE CASCADE that cascades in a cycle or along a second path
제거
Oracle의 외래 키는 삭제할 때만, CASCADE 또는 SET NULL로 동작해요.
보고서 줄: foreign_key_action_unsupported
foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE ones
만들지 않음
키는 없는 테이블을 가리킬 수 없고, Oracle이 검사하지 않는 키에는 부모 행이 없는 행이 있을 수 있어요.
보고서 줄: foreign_key_skipped
names longer than Oracle 12.1 allows
줄임
Oracle 12.2부터 이름은 128바이트까지예요. 더 긴 이름은 줄여요.
보고서 줄: name_shortened
partitioned tables
모든 파티션의 행을 담은 하나의 테이블
행은 같아요. 필요하면 Oracle에서 테이블을 다시 파티션으로 나누세요.
보고서 줄: partitions_dropped
several schemas in one export
하나의 스키마, 테이블 이름 앞에 스키마 이름
스크립트는 실행하는 사용자의 스키마에 불러와지므로, 다른 스키마의 테이블은 이름 앞에 스키마 이름이 붙어요. 스키마 하나만 내보낸 경우 테이블 이름을 유지해요.
보고서 줄: schemas_merged
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
CLOB
텍스트는 어떤 값이든 담아요. 불러온 뒤 타입을 바꾸세요.
보고서 줄: 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
NaN과 Infinity 유지
Oracle의 부동소수점 타입은 NaN과 infinity를 담아요.
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
| 파일에서 | 스크립트에서 | 보고서 줄 |
|---|---|---|
| BOOLEAN (23ai) | 23ai에서는 BOOLEAN, 아니면 0과 1의 CHECK가 붙은 NUMBER(1)TRUE와 FALSE는 true와 false 그대로예요. | |
| VARCHAR2(n BYTE|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONG | 같은 타입, BYTE 또는 CHAR 길이 유지같은 텍스트 타입이에요. LONG은 버전 8 이후 모든 버전이 권장하는 CLOB이 돼요. | |
| VARCHAR2 and NVARCHAR2 past what the target's own types hold | 모든 값이 4000바이트에 들어가면 VARCHAR2(4000 CHAR), 아니면 CLOBVARCHAR2는 기본으로 4000바이트까지 담고 Oracle은 CLOB을 인덱싱할 수 없어요. 그래서 텍스트는 값이 들어가면 VARCHAR2, 아니면 CLOB이 돼요. | text_as_lob |
| NUMBER, NUMBER(p,s), FLOAT(b) | 같은 NUMBER 타입같은 NUMBER와 FLOAT 타입으로, 모든 자릿수를 유지해요. | |
| INTERVAL YEAR TO MONTH, INTERVAL DAY TO SECOND | 같은 INTERVAL 타입같은 interval이에요. | |
| DATE, TIMESTAMP(p) | 선언한 대로 DATE와 TIMESTAMP(p)같은 타입이라 모든 날짜가 초 단위까지 시각을 유지해요. | |
| TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE | 같은 타입Oracle은 -12:00부터 +14:00까지의 오프셋을 유지해요. -12:00을 넘는 오프셋의 값은 UTC로 써서 같은 시점을 유지해요. | |
| JSON (21c), and CLOB holding JSON | 21c 이상은 JSON, 아니면 CLOB같은 JSON 값이에요. 내보내기가 'null'로 쓴 SQL NULL은 NULL 그대로예요. | |
| BLOB, RAW(n), LONG RAW | 같은 타입, LONG RAW는 BLOB바이트는 바이트 그대로 남아요. | |
| ROWID, UROWID, XMLTYPE, SDO_GEOMETRY | 텍스트를 담은 CLOBXMLTYPE, ROWID, UROWID는 텍스트를 CLOB에 유지하므로 스크립트를 19c 이상의 모든 버전에서 불러올 수 있어요. | type_replaced |
| collations that ignore case (BINARY_CI) | 정확한 비교열 콜레이션은 쓰지 않으므로, 대소문자를 무시하던 열(BINARY_CI)도 이제 Ana와 ana를 구분해요. | case_insensitive_collation |
| identity columns, and a sequence a BEFORE INSERT trigger assigns | GENERATED BY DEFAULT ON NULL AS IDENTITY, 가장 큰 id 다음부터 이어짐복사한 id는 그대로 불러오고, 카운터는 가장 큰 id 다음부터 이어져요. 트리거가 시퀀스로 값을 채우는 열(12c 이전 방식)도 identity 열이 돼요. | |
| identity columns the target can't count | 일반 정수Oracle은 테이블마다 identity 열 하나만 번호를 매겨요. 다른 열은 카운터 없이 값을 유지해요. | counter_dropped |
| CREATE SEQUENCE | 같은 옵션의 CREATE SEQUENCE내보내기는 시퀀스가 도달한 위치를 START WITH로 쓰므로, 시퀀스는 거기서부터 이어져요. | |
| virtual columns (GENERATED ALWAYS AS ... VIRTUAL) | 가상 열같은 가상 열이에요. | stored_as_virtual |
| virtual columns with functions IntoSQL doesn't write, and the || operator | 일반 열Oracle용으로 쓸 수 없는 수식은 일반 열로 남고 보고서에 한 줄 기록돼요. 내보내기가 가상 열에 쓴 값은 제외되고, Oracle이 계산할 값도 없어요. | 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 | 데이터 뒤에 ENABLE NOVALIDATE로 만들고, 고아 행은 경고와 함께 검증키는 새 행을 바로 검사하고 마지막에 검증해요. 부모 행이 없는 행이 있으면 불러오기를 멈추지 않고 경고와 함께 검증하지 않은 상태로 남겨요. | |
| ON DELETE CASCADE that cascades in a cycle or along a second path | 제거Oracle의 외래 키는 삭제할 때만, CASCADE 또는 SET NULL로 동작해요. | foreign_key_action_unsupported |
| foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE ones | 만들지 않음키는 없는 테이블을 가리킬 수 없고, Oracle이 검사하지 않는 키에는 부모 행이 없는 행이 있을 수 있어요. | foreign_key_skipped |
| names longer than Oracle 12.1 allows | 줄임Oracle 12.2부터 이름은 128바이트까지예요. 더 긴 이름은 줄여요. | name_shortened |
| partitioned tables | 모든 파티션의 행을 담은 하나의 테이블행은 같아요. 필요하면 Oracle에서 테이블을 다시 파티션으로 나누세요. | partitions_dropped |
| several schemas in one export | 하나의 스키마, 테이블 이름 앞에 스키마 이름스크립트는 실행하는 사용자의 스키마에 불러와지므로, 다른 스키마의 테이블은 이름 앞에 스키마 이름이 붙어요. 스키마 하나만 내보낸 경우 테이블 이름을 유지해요. | schemas_merged |
| 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 | CLOB텍스트는 어떤 값이든 담아요. 불러온 뒤 타입을 바꾸세요. | 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 | NaN과 Infinity 유지Oracle의 부동소수점 타입은 NaN과 infinity를 담아요. | |
| 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 @oracle.oracle.sql외래 키는 ENABLE NOVALIDATE로 추가해서 새 행은 바로 검사하고, 그다음 검증해요. 기존 행이 존재하지 않는 행을 가리키면, 중간에 실패하지 않고 어떤 제약인지 알려 주고 그 제약은 검증하지 않은 채로 남겨요.
큰 덤프는 데이터베이스와 같은 컴퓨터에서 SQL*Plus를 실행하세요. 스크립트는 100행마다 커밋해요. 실행하는 사용자에게는 CREATE TABLE, CREATE SEQUENCE, CREATE TRIGGER, CREATE PROCEDURE 권한과 테이블스페이스 할당량이 필요해요.
좋은 결과를 위한 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로 내보내세요.
변환되지 않는 것
뷰, 트리거, 프로시저, 함수 등 Oracle 자체 언어로 작성된 객체는 몰래 빠지지 않고 보고서에 이름으로 나와요. 불러올 Oracle 버전에서 확인한 뒤 다시 만들 수 있어요.
Oracle 버전 간 이동 질문
여기에 답이 없나요? 문의 페이지로 연락하시면 담당자가 답장해요. 문의 페이지
덤프가 업로드되나요?
아니요. 변환기는 브라우저에서 실행되고 디스크의 덤프를 조각조각 읽으므로, 덤프가 컴퓨터 밖으로 나가지 않아요. 데스크톱 앱과 명령줄도 컴퓨터에서 변환해요.
덤프는 얼마나 커도 되나요?
변환기는 덤프 전체를 메모리에 올리지 않아요. 파일을 범위별로 두 번 읽고, 읽는 대로 스크립트를 써요. 수 GB 덤프도 브라우저, 데스크톱 앱, 명령줄에서 변환돼요.
exp나 expdp의 .dmp 파일을 변환할 수 있나요?
직접은 안 돼요. Data Pump와 기존 Export는 Oracle만 읽을 수 있는 바이너리 형식으로 써요. 아무 Oracle 데이터베이스(Oracle Database Free는 Docker에서 실행돼요)로 가져온 뒤 SQL Developer의 Database Export나 SQLcl로 내보내세요. 변환기는 .dmp 파일을 받으면 이를 알려 줘요.
어떤 Oracle 버전의 내보내기를 읽나요?
Oracle 11g 이상에서 SQL Developer와 SQLcl로 만든, DBMS_METADATA의 테이블 정의를 담은 내보내기예요. 모든 테스트 내보내기는 Oracle 21c와 23ai에서 미국식, 독일식, ISO 세션으로 만들고 원본과 셀 단위로 비교해요.
NUMBER, DATE 등 Oracle 타입은 어떻게 되나요?
그대로 유지돼요. 정밀도를 유지한 NUMBER, DATE, TIMESTAMP, interval, 길이를 유지한 CHAR와 VARCHAR2, RAW와 LOB 모두 그대로예요. XMLTYPE과 JSON은 텍스트를 CLOB에 유지하므로 스크립트를 19c에서 불러올 수 있어요.
identity 열과 시퀀스는 유지되나요?
네. identity 열과 시퀀스는 그대로 유지되고, 내보내기에 적힌 위치부터 이어져요.
PL/SQL 패키지, 프로시저, 트리거는 어떻게 되나요?
변환하지 않고 보고서에 이름으로 나열하므로, 테이블을 불러온 뒤 다시 만들 수 있어요.
스크립트는 어떤 Oracle 버전에서 불러올 수 있나요?
Oracle 19c 이상이에요. 모든 테스트 내보내기를 21c와 23ai에서 불러오며, 문제가 생기면 첫 오류에서 멈춰요.