[아이티윌 오라클 DBA 과정 91기] 260116 TIL

2026. 1. 20. 18:06Courses/아이티윌 오라클 DBA 과정

SQL*Loader(Cont.)

conventional path load

준비

drop table hr.test purge;

create table hr.test(
    id number constraint test_id_pk primary key,
    name varchar2(30) not null,
    phone varchar2(15) constraint test_phone_unique unique,
    sal number constraint test_sal_ck check(sal > 1000),
    mgr number constraint test_mgr_fk references hr.test(id)) tablespace users;

select * from dba_constraints where owner = 'HR' and table_name = 'TEST';
select index_name, uniqueness, status from dba_indexes where owner = 'HR' and table_name = 'TEST';
[oracle@ora19c data]$ vi insa.ctl
[oracle@ora19c data]$ cat insa.ctl
load data
infile insa.dat
truncate
into table hr.test
fields terminated by ',' optionally enclosed by '"'
(id,name,phone,sal,mgr)

[oracle@ora19c data]$ vi insa.dat
[oracle@ora19c data]$ cat insa.dat
1,"james","010-9999-0000",2000, -- 입력
2,"grace","010-7777-7777",500,1 -- bad, sal < 1000
3,"scott","010-7777-7777",3000,1 -- 입력
3,"lucas","010-1004-1004",4000,2 -- bad, 2번이 입력되지 않으므로 fk 위반
4,,"010-1234-1004",5000,1 -- bad, name은 not null
5,"oracle","010-0001-0001",6000,6 --bad, mgr에 해당하는 id가 없으므로 fk 위반

[oracle@ora19c data]$ ls
insa.ctl  insa.dat

데이터 로드

[oracle@ora19c data]$ sqlldr hr/hr control=insa.ctl

SQL*Loader: Release 19.0.0.0.0 - Production on Fri Jan 16 10:05:29 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Path used:      Conventional
Commit point reached - logical record count 6

Table HR.TEST:
  0 Rows successfully loaded.

Check the log file:
  insa.log
for more information about the load.
[oracle@ora19c data]$ ls
insa.bad  insa.ctl  insa.dat  insa.log
  • 1번 데이터의 mgr이 null로 인식이 안돼서 입력되지 않음에 따라 연쇄적으로 정상 데이터들이 입력되지 못함

[oracle@ora19c data]$ cat insa.log
...
   Column Name                  Position   Len  Term Encl Datatype
------------------------------ ---------- ----- ---- ---- ---------------------
ID                                  FIRST     *   ,  O(") CHARACTER
NAME                                 NEXT     *   ,  O(") CHARACTER
PHONE                                NEXT     *   ,  O(") CHARACTER
SAL                                  NEXT     *   ,  O(") CHARACTER
MGR                                  NEXT     *   ,  O(") CHARACTER

Record 1: Rejected - Error on table HR.TEST, column MGR.
Column not found before end of logical record (use TRAILING NULLCOLS)
Record 2: Rejected - Error on table HR.TEST.
ORA-02290: check constraint (HR.TEST_SAL_CK) violated

Record 3: Rejected - Error on table HR.TEST.
ORA-02291: integrity constraint (HR.TEST_MGR_FK) violated - parent key not found

Record 4: Rejected - Error on table HR.TEST.
ORA-02291: integrity constraint (HR.TEST_MGR_FK) violated - parent key not found

Record 5: Rejected - Error on table HR.TEST, column NAME.
ORA-01400: cannot insert NULL into ("HR"."TEST"."NAME")

Record 6: Rejected - Error on table HR.TEST.
ORA-02291: integrity constraint (HR.TEST_MGR_FK) violated - parent key not found
...
  • trailing nullcols 추가
[oracle@ora19c data]$ vi insa.ctl
[oracle@ora19c data]$ cat insa.ctl
load data
infile insa.dat
truncate
into table hr.test
fields terminated by ',' optionally enclosed by '"'
trailing nullcols
(id,name,phone,sal,mgr)
[oracle@ora19c data]$ sqlldr hr/hr control=insa.ctl

SQL*Loader: Release 19.0.0.0.0 - Production on Fri Jan 16 10:13:46 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Path used:      Conventional
Commit point reached - logical record count 6

Table HR.TEST:
  2 Rows successfully loaded.

Check the log file:
  insa.log
for more information about the load.
SYS@ora19c> select * from hr.test;

        ID NAME                                               PHONE                  SAL        MGR
---------- -------------------------------------------------- --------------- ---------- ----------
         1 james                                              010-9999-0000         2000
         3 scott                                              010-7777-7777         3000          1

direct path load

준비

[oracle@ora19c data]$ vi insa.ctl
[oracle@ora19c data]$ cat insa.ctl
unrecoverable load data
infile insa.dat
truncate
into table hr.test
fields terminated by ',' optionally enclosed by '"'
trailing nullcols
(id,name,phone,sal,mgr)

데이터 로드

[oracle@ora19c data]$ sqlldr hr/hr control=insa.ctl direct=true

SQL*Loader: Release 19.0.0.0.0 - Production on Fri Jan 16 10:22:33 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Path used:      Direct

Load completed - logical record count 6.

Table HR.TEST:
  5 Rows successfully loaded.

Check the log file:
  insa.log
for more information about the load.
  • not null 위반하는 4번 데이터를 제외하고 제약 조건 위반하는 데이터들이 모두 들어감
SYS@ora19c> select * from hr.test;

        ID NAME                                               PHONE                  SAL        MGR
---------- -------------------------------------------------- --------------- ---------- ----------
         1 james                                              010-9999-0000         2000
         2 grace                                              010-7777-7777          500          1
         3 scott                                              010-7777-7777         3000          1
         3 lucas                                              010-1004-1004         4000          2
         5 oracle                                             010-0001-0001         6000          6
  • not null 빼고 나머지 제약조건은 disable novalidate로 바뀜
  • 유니크 인덱스도 unusable로 바뀜
SYS@ora19c> select constraint_name, status, validated, deferrable,deferred from dba_constraints where owner = 'HR' and table_name = 'TEST';

CONSTRAINT_NAME                STATUS                         VALIDATED     DEFERRABLE     DEFERRED
------------------------------ ------------------------------ ------------- -------------- ----------
SYS_C007728                    ENABLED                        VALIDATED     NOT DEFERRABLE IMMEDIATE
TEST_MGR_FK                    DISABLED                       NOT VALIDATED NOT DEFERRABLE IMMEDIATE
TEST_SAL_CK                    DISABLED                       NOT VALIDATED NOT DEFERRABLE IMMEDIATE
TEST_ID_PK                     DISABLED                       NOT VALIDATED NOT DEFERRABLE IMMEDIATE
TEST_PHONE_UNIQUE              DISABLED                       NOT VALIDATED NOT DEFERRABLE IMMEDIATE

SYS@ora19c> select index_name, uniqueness, status from dba_indexes where owner = 'HR' and table_name = 'TEST';

INDEX_NAME                     UNIQUENES STATUS
------------------------------ --------- ----------
TEST_ID_PK                     UNIQUE    UNUSABLE
TEST_PHONE_UNIQUE              UNIQUE    UNUSABLE

SPOOL

  • SQL Developer가 없는 경우 spool을 통해 데이터 추출 가능
[oracle@ora19c data]$ vi emp.sql
[oracle@ora19c data]$ cat emp.sql
set pagesize 0
set linesize 200
set termout off
set feedback off
set echo off
set trimspool on

spool emp.dat
select * from hr.employees;
spool off

set pagesize 1000
set termout on
set feedback on
set echo off

[oracle@ora19c data]$ ls
emp.sql  insa.bad  insa.ctl  insa.dat  insa.log
  • set termout off : 결과물을 화면에 출력하지 않도록 설정
  • set feedback off : 107 rows selected 같이 쿼리 수행 결과를 출력하지 않도록 설정
  • set echo off : sql이 출력되지 않도록 설정
  • set trimspool on : 데이터 사이 공백 제거

SQL*Plus 에서 emp.sql 실행

[oracle@ora19c data]$ sqlplus / as sysdba

SQL*Plus: Release 19.0.0.0.0 - Production on Fri Jan 16 11:15:08 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SYS@ora19c> @emp.sql
SYS@ora19c> !

[oracle@ora19c data]$ ls
emp.dat  emp.sql  insa.bad  insa.ctl  insa.dat  insa.log

[oracle@ora19c data]$ tail emp.dat
        197 Kevin                Feeney                    KFEENEY                   650.507.9822         23-MAY-06 SH_CLERK         3000                       124            50
        198 Donald               OConnell                  DOCONNEL                  650.507.9833         21-JUN-07 SH_CLERK         2600                       124            50
        199 Douglas              Grant                     DGRANT                    650.507.9844         13-JAN-08 SH_CLERK         2600                       124            50
        200 Jennifer             Whalen                    JWHALEN                   515.123.4444         17-SEP-03 AD_ASST          4400                       101            10
        201 Michael              Hartstein                 MHARTSTE                  515.123.5555         17-FEB-04 MK_MAN          13000                       100            20
        202 Pat                  Fay                       PFAY                      603.123.6666         17-AUG-05 MK_REP           6000                       201            20
        203 Susan                Mavris                    SMAVRIS                   515.123.7777         07-JUN-02 HR_REP           6500                       101            40
        204 Hermann              Baer                      HBAER                     515.123.8888         07-JUN-02 PR_REP          10000                       101            70
        205 Shelley              Higgins                   SHIGGINS                  515.123.8080         07-JUN-02 AC_MGR          12008                       101           110
        206 William              Gietz                     WGIETZ                    515.123.8181         07-JUN-02 AC_ACCOUNT       8300                       205           110

CSV 형식으로 spool

[oracle@ora19c data]$ vi emp.sql
[oracle@ora19c data]$ cat emp.sql
set pagesize 0
set linesize 200
set termout off
set feedback off
set echo off
set trimspool on

spool emp.dat
select employee_id||','||last_name||','||salary||','||to_char(hire_date,'yyyy-mm-dd hh24:mi:ss')||','||department_id from hr.employees;
spool off

set pagesize 1000
set termout on
set feedback on
set echo on
[oracle@ora19c data]$ sqlplus hr/hr

SQL*Plus: Release 19.0.0.0.0 - Production on Fri Jan 16 11:32:25 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

Last Successful login time: Fri Jan 16 2026 11:28:11 +09:00

Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

HR@ora19c> @emp.sql
HR@ora19c> exit
Disconnected from Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
[oracle@ora19c data]$ ls
emp.dat  emp.sql  insa.bad  insa.ctl  insa.dat  insa.log
[oracle@ora19c data]$ head emp.dat
100,King,24000,2003-06-17 00:00:00,90
101,Kochhar,17000,2005-09-21 00:00:00,90
102,De Haan,17000,2001-01-13 00:00:00,90
103,Hunold,9000,2006-01-03 00:00:00,60
104,Ernst,6000,2007-05-21 00:00:00,60
105,Austin,4800,2005-06-25 00:00:00,60
106,Pataballa,4800,2006-02-05 00:00:00,60
107,Lorentz,4200,2007-02-07 00:00:00,60
108,Greenberg,12008,2002-08-17 00:00:00,100
109,Faviet,9000,2002-08-16 00:00:00,100

spool 로 받은 데이터를 sql*loader로 로드

테이블 준비

[oracle@ora19c data]$ sqlplus hr/hr

SQL*Plus: Release 19.0.0.0.0 - Production on Fri Jan 16 11:28:11 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

Last Successful login time: Fri Jan 16 2026 11:00:30 +09:00

Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

HR@ora19c> drop table hr.emp purge;
drop table hr.emp purge
              *
ERROR at line 1:
ORA-55610: Invalid DDL statement on history-tracked table

HR@ora19c> conn / as sysdba
Connected.
SYS@ora19c> alter table hr.emp no flashback archive;

Table altered.

SYS@ora19c> drop table hr.emp purge;

Table dropped.

SYS@ora19c> create table hr.emp tablespace users as select employee_id, last_name, salary, hire_date, department_id from hr.employees where 1 = 2;

Table created.

데이터 로드

[oracle@ora19c data]$ vi emp.ctl
[oracle@ora19c data]$ cat emp.ctl
unrecoverable load data
infile emp.dat
insert
into table hr.emp
fields terminated by ',' optionally enclosed by '"'
trailing nullcols
(employee_id, last_name, salary, hire_date date 'yyyy-mm-dd hh24:mi:ss', department_id)

[oracle@ora19c data]$ sqlldr hr/hr control=emp.ctl direct=true

SQL*Loader: Release 19.0.0.0.0 - Production on Fri Jan 16 12:04:25 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Path used:      Direct

Load completed - logical record count 107.

Table HR.EMP:
  107 Rows successfully loaded.

Check the log file:
  emp.log
for more information about the load.
  • date 자료형은 데이터 파일에 저장된 형식에 맞춰 형식 지정해줘야 함

EXTERNAL TABLE(9i)

  • 데이터는 데이터베이스 바깥쪽(OS)에 두고 마치 데이터베이스에 저장된 데이터처럼 다루는 것이 필요할 때 사용
  • read만 가능

논리적인 디렉터리 생성

SYS@ora19c> create directory emp_dir as '/home/oracle/data';

Directory created.

SYS@ora19c> col directory_name format a30
SYS@ora19c> col directory_path format a30
SYS@ora19c> select * from dba_directories where directory_name = 'EMP_DIR';

OWNER                          DIRECTORY_NAME                 DIRECTORY_PATH                 ORIGIN_CON_ID
------------------------------ ------------------------------ ------------------------------ -------------
SYS                            EMP_DIR                        /home/oracle/data                          0

논리적 디렉터리를 사용할 수 있는 객체 권한 부여

SYS@ora19c> grant read, write on directory emp_dir to hr;

Grant succeeded.

SYS@ora19c> select * from dba_tab_privs where grantee = 'HR';

GRANTEE                        OWNER                          TABLE_NAME                     GRANTOR                    PRIVILEGE                       GRA HIE COM TYPE                     INH
------------------------------ ------------------------------ ------------------------------ ------------------------------ ------------------------------ --- --- --- ------------------------ ---
HR                             SYS                            DBMS_STATS                     SYS                        EXECUTE                         NO  NO  NO  PACKAGE                  NO
HR                             SYS                            PRIV_MGR                       SYS                        EXECUTE                         NO  NO  NO  PROCEDURE                NO
HR                             SYS                            EMP_DIR                        SYS                        READ                    NO  NO  NO  DIRECTORY                NO
HR                             SYS                            EMP_DIR                        SYS                        WRITE                           NO  NO  NO  DIRECTORY                NO

external table 생성

SYS@ora19c> drop table hr.emp purge;

Table dropped.

SYS@ora19c> create table hr.emp ( 
    id number(4), 
    name varchar2(30), 
    sal number, 
    hire date, 
    dept_id number(4) ) 
    organization external ( 
        type oracle_loader 
        default directory emp_dir 
        access parameters ( 
            records delimited by newline 
            badfile 'emp.bad' 
            logfile 'emp.log' 
            fields terminated by ',' 
            missing field values are null 
            ( id, name, sal, hire char date_format date mask "yyyy-mm-dd hh24:mi:ss", dept_id ) 
        ) 
        location ('emp.dat', 'emp_new.dat') 
    );
select * from hr.emp;
select * from dba_objects where object_name = 'EMP' and owner = 'HR';
select * from dba_tables where table_name = 'EMP' and owner = 'HR';

external table 조회

select * from dba_external_tables;

external table 위치 조회

select * from dba_external_locations;

external table 을 사용하여 데이터 이관

create table hr.insa tablespace users as select * from hr.emp;
select * from hr.insa;
select * from dba_tables where table_name = 'INSA' and owner = 'HR';

덤프를 받으면서 external table로 사용하기

create table hr.emp_ext
(id, name, sal, hire, deptno)
organization external
(
    type oracle_datapump
    default directory emp_dir
    location('emp.dump')
)
as
select employee_id, last_name, salary, to_char(hire_date, 'yyyy-mm-dd'), department_id
from hr.employees;
  • 덤프는 바이너리 형식이기 때문에 파일 내용을 직접 볼 수 없음

CTAS 또는 insert 서브쿼리로 데이터 로드

create table hr.emp2026
as
select * from hr.emp_ext;

truncate table hr.emp2026;

insert into hr.emp2026 select * from hr.emp_ext;

Table Reorganization(REORG)

1. Data Pump

테이블 생성 및 데이터 입력

# 테이블 생성
create table hr.reorg_test(id number, name varchar2(100)) tablespace users;

# 데이터 10000건 입력
begin
    for i in 1..10000 loop
        insert into hr.reorg_test(id, name) values(i, 'table/index reorganization example');
    end loop;
end;
/

select count(*) from hr.reorg_test; -- 10000

# 테이블 세그먼트가 사용 중인 익스텐트 및 블록 수
select extents, blocks, bytes from dba_segments where segment_name = 'REORG_TEST';
select tablespace_name, extent_id, bytes from dba_extents where segment_name = 'REORG_TEST';

제약 조건 추가

alter table hr.reorg_test add constraint reort_id_pk primary key(id);

select * from dba_constraints where table_name = 'REORG_TEST';
select * from dba_indexes where table_name = 'REORG_TEST';

# 인덱스 세그먼트가 사용 중인 익스텐트 및 블록 수
select extents, blocks, bytes from dba_segments where segment_name = 'REORT_ID_PK';
select tablespace_name, extent_id, bytes from dba_extents where segment_name = 'REORT_ID_PK';

테이블 정보 조회

select num_rows, blocks, avg_row_len from dba_tables where table_name = 'REORG_TEST';

-- 통계 정보 수집
exec dbms_stats.gather_table_stats('hr', 'reorg_test')

select num_rows, blocks, avg_row_len, to_char(last_analyzed, 'yyyy-mm-dd hh24:mi:ss') last_analyzed from dba_tables where table_name = 'REORG_TEST';

-- 실행 계획 확인
select * from hr.reorg_test where id = 100;

데이터 삭제

delete from hr.reorg_test where id > 100;
commit;

select count(*) from hr.reorg_test; -- 100
select extents, blocks, bytes from dba_segments where segment_name = 'REORG_TEST';
select tablespace_name, extent_id, bytes from dba_extents where segment_name = 'REORG_TEST';
select extents, blocks, bytes from dba_segments where segment_name = 'REORT_ID_PK';

select num_rows, blocks, avg_row_len from dba_tables where table_name = 'REORG_TEST';

-- 실행 계획 확인
select * from hr.reorg_test where id = 100;

  • 데이터 수가 변했더라도 마지막 수집한 통계 정보를 기반으로 나옴
  • 통계 정보가 그대로이기 때문에 실행 계획도 바뀌지 않음
# row수 * row 평균 길이 / 블록 크기
select 10000 * 39 / 8192 from dual; -- 47.607421875

export

[oracle@ora19c data]$ expdp system/oracle directory=emp_dir dumpfile=reorg_test.dmp tables=hr.reorg_test

Export: Release 19.0.0.0.0 - Production on Fri Jan 16 17:02:09 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Starting "SYSTEM"."SYS_EXPORT_TABLE_01":  system/******** directory=emp_dir dumpfile=reorg_test.dmp tables=hr.reorg_test
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
Processing object type TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type TABLE_EXPORT/TABLE/STATISTICS/MARKER
Processing object type TABLE_EXPORT/TABLE/TABLE
Processing object type TABLE_EXPORT/TABLE/CONSTRAINT/CONSTRAINT
. . exported "HR"."REORG_TEST"                           9.570 KB     100 rows
Master table "SYSTEM"."SYS_EXPORT_TABLE_01" successfully loaded/unloaded
******************************************************************************
Dump file set for SYSTEM.SYS_EXPORT_TABLE_01 is:
  /home/oracle/data/reorg_test.dmp
Job "SYSTEM"."SYS_EXPORT_TABLE_01" successfully completed at Fri Jan 16 17:02:29 2026 elapsed 0 00:00:19

[oracle@ora19c data]$ ls reorg_test.dmp
reorg_test.dmp

truncate

truncate table hr.reorg_test;

import

[oracle@ora19c data]$ impdp system/oracle directory=emp_dir dumpfile=reorg_test.dmp tables=hr.reorg_test content=data_only

Import: Release 19.0.0.0.0 - Production on Fri Jan 16 17:03:45 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Master table "SYSTEM"."SYS_IMPORT_TABLE_01" successfully loaded/unloaded
Starting "SYSTEM"."SYS_IMPORT_TABLE_01":  system/******** directory=emp_dir dumpfile=reorg_test.dmp tables=hr.reorg_test content=data_only
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
. . imported "HR"."REORG_TEST"                           9.570 KB     100 rows
Job "SYSTEM"."SYS_IMPORT_TABLE_01" successfully completed at Fri Jan 16 17:03:51 2026 elapsed 0 00:00:05
select count(*) from hr.reorg_test; -- 100
select extents, blocks, bytes from dba_segments where segment_name = 'REORG_TEST';
select tablespace_name, extent_id, bytes from dba_extents where segment_name = 'REORG_TEST';
select extents, blocks, bytes from dba_segments where segment_name = 'REORT_ID_PK';
select tablespace_name, extent_id, bytes from dba_extents where segment_name = 'REORT_ID_PK';

# 통계 정보 수집
exec dbms_stats.gather_table_stats('hr', 'reorg_test')

select num_rows, blocks, avg_row_len, to_char(last_analyzed, 'yyyy-mm-dd hh24:mi:ss') last_analyzed from dba_tables where table_name = 'REORG_TEST';