The DIRECT parameter for SQLLoader Command-Line enables Direct Path Load which uses a Direct Path API instead of a regular INSERT statement to load data into the target table. 
However, one needs to know how it handles a Unique Index on the target table.
It actually leaves the UNUSABLE if, duplicate values are loaded. The Index rebuild fails but the duplicate values remain in the table.
Here is a quick demo (this in 12.1.0.2 MultiTenant). I first setup the target table with a Unique Index.
Next, I setup the datafile with a duplicate record and the controlfile.
I am now ready to run a Direct Path Load.
What is that ? 3 rows loaded successfully ? So, the duplicate row also did get loaded ? Let's check the log file.
Did you notice the section in the log file that says :
Apparently, the Index is left UNUSABLE.
We can see the duplicated row for ID_COLUMN=3 and find that the Index cannot be rebuilt. Oracle has allowed duplicate rows to load and left the Index UNUSABLE.
So, if you are planning to use DIRECT=TRUE and have a Unique Index, make sure you check the status of the Index and/or check the Log file before you proceed with processing the data.
Conversely, here is how the data is handled without DIRECT=TRUE :.
The duplicate row was rejected and went to the BAD file and was REJECTED.
.
.
.
So, the next time you chose to use SQLLDR DIRECT=TRUE for its performance benefits, make sure you know how to validate the status of Unique Indexes and check the log file ! Else, you might allow duplicates if the incoming data has not been cleansed before loading into the database.
.
.
.
If you are running 11g, the behaviour is the same in 11g/
.
.
.
.
However, one needs to know how it handles a Unique Index on the target table.
It actually leaves the UNUSABLE if, duplicate values are loaded. The Index rebuild fails but the duplicate values remain in the table.
Here is a quick demo (this in 12.1.0.2 MultiTenant). I first setup the target table with a Unique Index.
[oracle@ora12102 Desktop]$ sqlplus hemant/hemant@PDB1 SQL*Plus: Release 12.1.0.2.0 Production on Mon Sep 26 22:36:51 2016 Copyright (c) 1982, 2014, Oracle. All rights reserved. Last Successful login time: Mon Sep 26 2016 22:26:16 +08:00 Connected to: Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production With the Partitioning, Automatic Storage Management, OLAP, Advanced Analytics and Real Application Testing options SQL> show con_id CON_ID ------------------------------ 3 SQL> create table test_sqlldr_direct 2 (id_column number, 3 data_column varchar2(15)) 4 / Table created. SQL> create unique index test_sqlldr_direct_u1 on test_sqlldr_direct(id_column); Index created. SQL> insert into test_sqlldr_direct values (1, 'First Row'); 1 row created. SQL> commit; Commit complete. SQL> exit Disconnected from Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production With the Partitioning, Automatic Storage Management, OLAP, Advanced Analytics and Real Application Testing options [oracle@ora12102 Desktop]$
Next, I setup the datafile with a duplicate record and the controlfile.
[oracle@ora12102 Desktop]$ ls -l total 8 -rw-r--r-- 1 oracle oinstall 40 Sep 26 22:40 load_data.dat -rw-r--r-- 1 oracle oinstall 165 Sep 26 22:45 load_control.ctl [oracle@ora12102 Desktop]$ cat load_data.dat 2,'Second Row' 3,'Third Row' 3,'Oops !' [oracle@ora12102 Desktop]$ cat load_control.ctl LOAD DATA INFILE load_data.dat APPEND INTO TABLE TEST_SQLLDR_DIRECT FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' TRAILING NULLCOLS ( id_column, data_column) [oracle@ora12102 Desktop]$
I am now ready to run a Direct Path Load.
[oracle@ora12102 Desktop]$ sqlldr hemant/hemant@pdb1 control=load_control.ctl direct=TRUE SQL*Loader: Release 12.1.0.2.0 - Production on Mon Sep 26 22:47:09 2016 Copyright (c) 1982, 2014, Oracle and/or its affiliates. All rights reserved. Path used: Direct Load completed - logical record count 3. Table TEST_SQLLDR_DIRECT: 3 Rows successfully loaded. Check the log file: load_control.log for more information about the load. [oracle@ora12102 Desktop]$
What is that ? 3 rows loaded successfully ? So, the duplicate row also did get loaded ? Let's check the log file.
[oracle@ora12102 Desktop]$ ls -ltr
total 12
-rw-r--r-- 1 oracle oinstall   40 Sep 26 22:40 load_data.dat
-rw-r--r-- 1 oracle oinstall  165 Sep 26 22:45 load_control.ctl
-rw-r--r-- 1 oracle oinstall 1833 Sep 26 22:47 load_control.log
[oracle@ora12102 Desktop]$ cat load_control.log
SQL*Loader: Release 12.1.0.2.0 - Production on Mon Sep 26 22:47:09 2016
Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.
Control File:   load_control.ctl
Data File:      load_data.dat
  Bad File:     load_data.bad
  Discard File:  none specified
 
 (Allow all discards)
Number to load: ALL
Number to skip: 0
Errors allowed: 50
Continuation:    none specified
Path used:      Direct
Table TEST_SQLLDR_DIRECT, loaded from every logical record.
Insert option in effect for this table: APPEND
TRAILING NULLCOLS option in effect
   Column Name                  Position   Len  Term Encl Datatype
------------------------------ ---------- ----- ---- ---- ---------------------
ID_COLUMN                           FIRST     *   ,  O(") CHARACTER            
DATA_COLUMN                          NEXT     *   ,  O(") CHARACTER            
The following index(es) on table TEST_SQLLDR_DIRECT were processed:
index HEMANT.TEST_SQLLDR_DIRECT_U1 was made unusable due to:
ORA-01452: cannot CREATE UNIQUE INDEX; duplicate keys found
Table TEST_SQLLDR_DIRECT:
  3 Rows successfully loaded.
  0 Rows not loaded due to data errors.
  0 Rows not loaded because all WHEN clauses were failed.
  0 Rows not loaded because all fields were null.
Bind array size not used in direct path.
Column array  rows :    5000
Stream buffer bytes:  256000
Read   buffer bytes: 1048576
Total logical records skipped:          0
Total logical records read:             3
Total logical records rejected:         0
Total logical records discarded:        0
Total stream buffers loaded by SQL*Loader main thread:        1
Total stream buffers loaded by SQL*Loader load thread:        0
Run began on Mon Sep 26 22:47:09 2016
Run ended on Mon Sep 26 22:47:11 2016
Elapsed time was:     00:00:01.88
CPU time was:         00:00:00.01
[oracle@ora12102 Desktop]$ 
Did you notice the section in the log file that says :
The following index(es) on table TEST_SQLLDR_DIRECT were processed: index HEMANT.TEST_SQLLDR_DIRECT_U1 was made unusable due to: ORA-01452: cannot CREATE UNIQUE INDEX; duplicate keys found
Apparently, the Index is left UNUSABLE.
[oracle@ora12102 Desktop]$ sqlplus hemant/hemant@pdb1 SQL*Plus: Release 12.1.0.2.0 Production on Mon Sep 26 22:50:51 2016 Copyright (c) 1982, 2014, Oracle. All rights reserved. Last Successful login time: Mon Sep 26 2016 22:47:09 +08:00 Connected to: Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production With the Partitioning, Automatic Storage Management, OLAP, Advanced Analytics and Real Application Testing options SQL> select status from user_indexes 2 where index_name = 'TEST_SQLLDR_DIRECT_U1' 3 / STATUS -------- UNUSABLE SQL> select * from test_sqlldr_direct order by 1; ID_COLUMN DATA_COLUMN ---------- --------------- 1 First Row 2 'Second Row' 3 'Third Row' 3 'Oops !' SQL> alter index test_sqlldr_direct_u1 rebuild; alter index test_sqlldr_direct_u1 rebuild * ERROR at line 1: ORA-01452: cannot CREATE UNIQUE INDEX; duplicate keys found SQL>
We can see the duplicated row for ID_COLUMN=3 and find that the Index cannot be rebuilt. Oracle has allowed duplicate rows to load and left the Index UNUSABLE.
So, if you are planning to use DIRECT=TRUE and have a Unique Index, make sure you check the status of the Index and/or check the Log file before you proceed with processing the data.
Conversely, here is how the data is handled without DIRECT=TRUE :.
SQL> truncate table test_sqlldr_direct;
Table truncated.
SQL> insert into test_sqlldr_direct values (1,'First Row');
1 row created.
SQL> select status from user_indexes
  2  where index_name = 'TEST_SQLLDR_DIRECT_U1'
  3  /
STATUS
--------
VALID
SQL> 
[oracle@ora12102 Desktop]$ rm load_control.log
[oracle@ora12102 Desktop]$ sqlldr hemant/hemant@PDB1 control=load_control.ctl
SQL*Loader: Release 12.1.0.2.0 - Production on Mon Sep 26 22:59:58 2016
Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.
Path used:      Conventional
Commit point reached - logical record count 3
Table TEST_SQLLDR_DIRECT:
  2 Rows successfully loaded.
Check the log file:
  load_control.log
for more information about the load.
[oracle@ora12102 Desktop]$ 
[oracle@ora12102 Desktop]$ ls -ltr
total 16
-rw-r--r-- 1 oracle oinstall   40 Sep 26 22:40 load_data.dat
-rw-r--r-- 1 oracle oinstall  165 Sep 26 22:45 load_control.ctl
-rw-r--r-- 1 oracle oinstall   11 Sep 26 22:59 load_data.bad
-rw-r--r-- 1 oracle oinstall 1668 Sep 26 22:59 load_control.log
[oracle@ora12102 Desktop]$ cat load_data.bad
3,'Oops !'
[oracle@ora12102 Desktop]$ cat load_control.log
SQL*Loader: Release 12.1.0.2.0 - Production on Mon Sep 26 22:59:58 2016
Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.
Control File:   load_control.ctl
Data File:      load_data.dat
  Bad File:     load_data.bad
  Discard File:  none specified
 
 (Allow all discards)
Number to load: ALL
Number to skip: 0
Errors allowed: 50
Bind array:     64 rows, maximum of 256000 bytes
Continuation:    none specified
Path used:      Conventional
Table TEST_SQLLDR_DIRECT, loaded from every logical record.
Insert option in effect for this table: APPEND
TRAILING NULLCOLS option in effect
   Column Name                  Position   Len  Term Encl Datatype
------------------------------ ---------- ----- ---- ---- ---------------------
ID_COLUMN                           FIRST     *   ,  O(") CHARACTER            
DATA_COLUMN                          NEXT     *   ,  O(") CHARACTER            
Record 3: Rejected - Error on table TEST_SQLLDR_DIRECT.
ORA-00001: unique constraint (HEMANT.TEST_SQLLDR_DIRECT_U1) violated
Table TEST_SQLLDR_DIRECT:
  2 Rows successfully loaded.
  1 Row not loaded due to data errors.
  0 Rows not loaded because all WHEN clauses were failed.
  0 Rows not loaded because all fields were null.
Space allocated for bind array:                  33024 bytes(64 rows)
Read   buffer bytes: 1048576
Total logical records skipped:          0
Total logical records read:             3
Total logical records rejected:         1
Total logical records discarded:        0
Run began on Mon Sep 26 22:59:58 2016
Run ended on Mon Sep 26 22:59:58 2016
Elapsed time was:     00:00:00.07
CPU time was:         00:00:00.00
[oracle@ora12102 Desktop]$ 
SQL> select * from test_sqlldr_direct 
  2  order by id_column
  3  /
 ID_COLUMN DATA_COLUMN
---------- ---------------
  1 First Row
  2 'Second Row'
  3 'Third Row'
SQL> 
SQL> select status from user_indexes
  2  where index_name = 'TEST_SQLLDR_DIRECT_U1'
  3  /
STATUS
--------
VALID
SQL> 
The duplicate row was rejected and went to the BAD file and was REJECTED.
.
.
.
So, the next time you chose to use SQLLDR DIRECT=TRUE for its performance benefits, make sure you know how to validate the status of Unique Indexes and check the log file ! Else, you might allow duplicates if the incoming data has not been cleansed before loading into the database.
.
.
.
If you are running 11g, the behaviour is the same in 11g/
.
.
.
.
No comments:
Post a Comment