Conventional Path Load
The default load mode. sqlldr reads rows, builds an array of bind values, and submits ordinary INSERT statements through the SQL layer — exactly as your application would.
What it gives you
- Triggers fire (BEFORE INSERT, AFTER INSERT, FOR EACH ROW)
- All constraints checked (PK, FK, CHECK, NOT NULL)
- Indexes maintained row-by-row
- Works on all table types (clustered, IOT, partitioned, materialised view containers)
- Online: other sessions keep reading and writing the table
What it costs
Conventional path is the slow lane. Each insert traverses the SQL parse → execute → buffer-cache → redo path. Expect 5–10× lower throughput than direct path.
Example
-- conventional.ctl
OPTIONS ( ROWS=10000, BINDSIZE=8388608, READSIZE=8388608 )
LOAD DATA
INFILE 'orders.csv'
APPEND
INTO TABLE sales.orders
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
TRAILING NULLCOLS
(
order_id INTEGER EXTERNAL,
customer_id INTEGER EXTERNAL,
order_date DATE "YYYY-MM-DD",
amount DECIMAL EXTERNAL,
status CHAR(1)
)
sqlldr userid=hr/hr control=conventional.ctl # DIRECT not specified → conventional
How rows turn into INSERTs
sqlldrallocates a bind array of sizeBINDSIZEbytes- Reads rows from the data file via a
READSIZE-byte buffer - Fills the bind array
- Issues
INSERT INTO sales.orders VALUES (:1,:2,:3,:4,:5)with the array - Commits every
ROWSrows - Repeats until EOF
The INSERT is path-by-path through Oracle: parse, optimise, execute, redo, undo, indexes, triggers — everything.
Tuning levers
| Lever | Effect |
|---|---|
BINDSIZE (bytes) |
Bigger array = fewer round-trips; Oracle caps it where row × array exceeds it |
READSIZE (bytes) |
Should match BINDSIZE; otherwise one of them throttles |
ROWS |
Commit interval. Fewer commits = less log; too few risks losing work on crash |
COLUMNARRAYROWS |
Override the array size (rows) when bytes math is awkward |
sqlldr userid=hr/hr control=orders.ctl bindsize=33554432 readsize=33554432 rows=20000
When to prefer conventional
- Triggers must fire (audit, sequence-from-trigger, derived columns)
- Foreign keys must be checked at load time
- Tables involved are clustered (direct doesn't support clusters)
- Loading a small amount (< ~100k rows) — direct's setup overhead isn't worth it
- Using
INSERTmode (notAPPEND) into an empty table you can't truncate
Best practices
- Disable secondary indexes during the load and rebuild after — biggest single win for conventional path
- Set
ROWShigh (10k–50k) so you commit infrequently but not so high that a crash loses too much work BINDSIZE = READSIZE; both should be at least 8 MB on modern systems- Watch redo generation in v$sysstat — conventional path generates a lot of it
- Drop disable-validate to disable-novalidate on FKs during load to skip validation, then re-enable