Choosing the Right Export Format in Oracle SQL Developer: When INSERT Statements Kill Performance
SQL Developer's Export Wizard defaults to INSERT statements, but that format fails catastrophically at scale. This post walks through format selection—INSERT vs Loader vs CSV—with a 10,000-row worked example, delimiter pitfalls, and a verification checklist you can run today.
01 Sept 2025, 22:29 UTC

The Problem: Export Format Determines Whether Your Migration Finishes or Hangs
You need to move 500,000 rows from a production Oracle table to a test environment. You right-click the table in SQL Developer, choose Export, and pick the default format. Twenty minutes later, you're staring at a 2.3 GB .sql file that crashes your text editor and times out when you try to run it. The export succeeded; the import will never finish.
The Export Wizard in Oracle SQL Developer supports multiple output formats, but the default choice—INSERT statements—is rarely the right one for anything beyond a few thousand rows. Understanding when to use each format separates a five-minute task from a weekend of troubleshooting.
Format Comparison: INSERT vs Loader vs CSV
SQL Developer's Export dialog offers three primary formats for data movement:
- INSERT — Generates one
INSERT INTO table (...) VALUES (...);statement per row. Human-readable, portable across Oracle versions, and works with any client that runs SQL scripts. - Loader — Produces a
.datdata file and a.ctlcontrol file for SQL*Loader (orsqlldr). This is Oracle's native high-speed bulk load utility. - CSV — Comma-separated values with optional enclosure characters. Portable beyond Oracle but requires careful delimiter handling.
For migrations staying within Oracle, Loader format is the engineering choice for volume. INSERT format is the convenience choice for small datasets and script-based workflows.
Worked Example: 10,000 Rows, Two Formats
Consider a table ORDERS with 10,000 rows and 15 columns (mixed NUMBER, VARCHAR2, DATE, TIMESTAMP).
Export as INSERT
- Right-click
ORDERSin the Connections navigator → Export. - Format: INSERT.
- Uncheck "Include CREATE TABLE" (you only want data).
- Save as
orders_insert.sql.
Result: ~4.2 MB file, 10,001 lines (one header comment + 10,000 statements). Running this in SQL*Plus or SQL Worksheet takes 18–25 seconds on a local network, but each statement incurs parse/execute overhead.
Export as Loader
- Same starting point, Format: Loader.
- Two files created:
orders.dat(~1.1 MB) andorders.ctl(~2 KB). - Load command:
sqlldr userid=scott/tiger@pdb1 control=orders.ctl.
Result: Load completes in 2–3 seconds. SQL*Loader uses direct-path or conventional-path inserts with array binding, bypassing per-statement parsing.
Verification step: Run SELECT COUNT(*) FROM orders; in the target schema after each method. Both should return 10,000. Spot-check a few rows with SELECT * FROM orders WHERE order_id IN (1, 5000, 10000); to confirm data fidelity.
Where INSERT Format Breaks Down
Three practical limits make INSERT format dangerous at scale:
- File size and editor crashes. A 500,000-row table with 20 columns easily produces a 1–2 GB
.sqlfile. Most editors (VS Code, Notepad++, SQL Developer itself) struggle to open or search files above 500 MB. - Statement buffer limits. SQL*Plus has a default
SQLBLKSIZEand some clients cap the statement batch size. A single monolithic script can exceed client-side memory. - Network round-trips. Each
INSERTis a separate network call unless you manually wrap batches inBEGIN ... END;blocks—which the wizard doesn't do.
If you must use INSERT format for compatibility (e.g., loading into a non-Oracle target via an ORM), split the export: add a WHERE clause in the Export dialog to chunk by date range or primary key buckets (WHERE order_date BETWEEN DATE '2024-01-01' AND DATE '2024-01-31'). This keeps individual files under 100 MB and lets you run them in parallel.
CSV Delimiter Collisions: The Silent Data Corruption
CSV seems safe until your VARCHAR2(4000) column contains commas, double quotes, or newlines. The Export dialog lets you set:
- Enclosure character (default: double quote)
- Delimiter (default: comma)
- Null string (default: empty)
If your data contains the enclosure character (e.g., a product description with "Premium"), you must either change the enclosure to a character not in your data (like |) or ensure the wizard escapes it correctly. SQL Developer doubles the enclosure character (""Premium""), which RFC 4180 parsers handle—but not all ETL tools do. Always test-load a CSV sample into your target system before committing to a full export.
Performance Depends on Your Machine, Not Just the Database
The Export Wizard runs on the client machine. It fetches rows over the network, formats them in local memory, and writes to local disk. Three factors dominate speed:
- Network latency/bandwidth between your workstation and the database server.
- Client heap memory (SQL Developer's
-Xmxsetting insqldeveloper.conf). Large exports can trigger GC pauses orOutOfMemoryError. - Disk I/O on the client—SSD vs HDD matters for multi-GB files.
For production-scale exports, run SQL Developer on a server co-located with the database (or use expdp/Data Pump server-side) rather than over a VPN from a laptop.
Column Mapping: Aligning Schema Without ALTER TABLE
The Export dialog's "Columns" tab lets you rename, reorder, or exclude columns in the output. This is useful when the target table has a different column order or renamed columns (e.g., CREATED_AT → CREATION_DATE). The mapping applies to all formats. For Loader format, the control file reflects the mapped names, so SQL*Loader inserts into the correct target columns without a staging table.
Decision Checklist
| Scenario | Recommended Format | Why |
|---|---|---|
| < 10,000 rows, one-time script | INSERT | Readable, runnable anywhere |
| > 10,000 rows, Oracle-to-Oracle | Loader | 10–50× faster load, smaller files |
| Non-Oracle target (PostgreSQL, MySQL, etc.) | CSV (with verified delimiters) | Universal parser support |
| Schema mismatch (column rename/reorder) | Any + Column Mapping tab | Avoids DDL on target |
| Massive table (> 1M rows) | Data Pump (expdp/impdp), not Wizard | Server-side, parallel, restartable |
Closing Action: Verify Before You Trust
Before relying on any export for a real migration:
- Export a representative subset (use WHERE clause) in your chosen format.
- Load it into a disposable test schema.
- Run
SELECT COUNT(*), MIN(pk), MAX(pk) FROM source_table MINUS SELECT COUNT(*), MIN(pk), MAX(pk) FROM target_table;— expect zero rows. - Compare a few wide rows with
DBMS_CRYPTO.HASHor a simple column-by-column diff query.
If the verification passes, scale up. If it fails, you've caught a delimiter, encoding, or mapping issue on 1,000 rows—not 1,000,000.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.