Oracle 19c Data Pump (expdp / impdp)
A Step-by-Step Field Guide to Schema and Tablespace Migration in a RAC Multitenant Environment.
Based on a real-world implementation. This blog post turns that hands-on guide into a structured, reusable playbook for DBAs working with Oracle 19c CDB/PDB on RAC.
Table of Contents
- Why Data Pump Still Matters
- Architecture Overview
- Objectives of the Implementation
- Prerequisites — Don't Skip These
- Schema-Level Migration, End to End
- Tablespace-Level Migration, End to End
- Risks and Mitigation
- Rollback Strategy
- Lessons Learned & Best Practices
- Future Enhancements
- Conclusion
1. Why Data Pump Still Matters
Oracle Data Pump is Oracle's high-performance, logical backup and migration utility used for data movement across databases, environments, and platforms. Despite the popularity of newer cloud-era tools (GoldenGate, ZDM, RMAN duplication), expdp/impdp remains the workhorse of the practical DBA's toolbox because it offers:
- Reliability — preserves data integrity, metadata, and object dependencies.
- Portability — moves data across versions, platforms, and PDBs.
- Granularity — full DB, schema, tablespace, table, or query-driven subsets.
- Auditability — every job writes a structured log with object counts and row counts.
In enterprise environments, Data Pump is widely used for:
- Production-to-Test/Dev refresh
- Schema-level migrations
- Tablespace-level data movement
- Cross-PDB data transfer
- Controlled and auditable database changes
This post documents a complete, step-by-step implementation of schema-level and tablespace-level migration using Oracle Data Pump in an Oracle 19c Multitenant RAC environment. The implementation ensures a clean, controlled, and validated migration between source and target PDBs with proper tablespace remapping and post-migration verification.
2. Architecture Overview
The implementation runs across two CDBs, each with its own PDB on a 19c RAC cluster, sharing a Data Pump filesystem accessible from every node.
2.1 Schema-Level Migration Architecture
| Component | Source Environment | Target Environment |
|---|---|---|
| CDB | PROD | TEST |
| PDB | PRODPDB | TESTPDB |
| Schema | APPUSER | APPUSER |
| Tablespace | PROD_TS | TEST_TS |
| Database Type | Oracle 19c RAC | Oracle 19c RAC |
2.2 Tablespace-Level Migration Architecture
| Component | Source Environment | Target Environment |
|---|---|---|
| CDB | TEST | PROD |
| PDB | TESTPDB | PRODPDB |
| Schema Owner | CMSUSER | CMSUSER |
| Tablespace | TEST_TS | PROD_TS |
| Database Type | Oracle 19c RAC | Oracle 19c RAC |
2.3 Shared Storage
- Shared directory:
/u01/dpump - Accessible from all RAC nodes (key requirement when running multi-instance Data Pump jobs)
3. Objectives of the Implementation
The implementation is engineered around six concrete goals:
- Perform schema-level migration using Oracle Data Pump.
- Perform tablespace-level migration using Oracle Data Pump.
- Remap tablespaces between source and target environments.
- Ensure data integrity and object consistency.
- Validate migration results through post-import checks.
- Establish a reusable migration procedure.
These objectives map directly onto the rest of this article — every section below contributes to one or more of them.
4. Prerequisites — Don't Skip These
A surprising number of failed migrations trace back to a missed prerequisite. Validate the following before you type your first expdp.
4.1 Database & OS Requirements
- Oracle Database 19c installed on source and target
- Multitenant architecture (CDB/PDB)
- RAC configuration on both environments
- Archive logging enabled
- Adequate disk space available (for dump files and target tablespaces)
4.2 Configuration Requirements
- Source and target PDB services configured
- Oracle Data Pump directory object created
- Shared filesystem accessible across RAC nodes
- Required users and tablespaces created on target
- Oracle OS user privileges available
5. Schema-Level Migration, End to End
This is the most common Data Pump scenario: lift the entire APPUSER schema from PRODPDB and land it cleanly in TESTPDB, remapping the storage along the way.
5.1 Pre-Migration Checks on the Source Database
Before exporting anything, confirm the source schema and its tablespace footprint.
[oracle@node1 ~]$ . oraenv
ORACLE_SID = [oracle] ? prod1
The Oracle base has been set to /u01/app/oracle
[oracle@node1 ~]$ sqlplus / as sysdba
SQL> ALTER SESSION SET CONTAINER = prodpdb;
Session altered.
SQL> show con_name;
CON_NAME
PRODPDB
Verify schema details:
SQL> SELECT username,
account_status,
default_tablespace,
temporary_tablespace
FROM dba_users
WHERE username = 'APPUSER';
| USERNAME | STATUS | DEFAULT_TS | TEMP_TS |
|---|---|---|---|
| APPUSER | OPEN | PROD_TS | TEMP |
Verify tables owned by the schema and check tablespace usage:
SQL> SELECT table_name, tablespace_name
FROM dba_tables
WHERE owner = 'APPUSER'
ORDER BY tablespace_name, table_name;
The query confirms 20 tables, all sitting in PROD_TS:
APP_LOGS PROD_TS
APP_USERS PROD_TS
ATTENDANCE PROD_TS
AUDIT_TRAIL PROD_TS
BANK_ACCOUNTS PROD_TS
CONFIGURATION PROD_TS
EMPLOYEES PROD_TS
INVENTORY PROD_TS
NOTIFICATIONS PROD_TS
ORDERS PROD_TS
ORDER_ITEMS PROD_TS
PAYMENTS PROD_TS
PAYROLL PROD_TS
PRODUCTS PROD_TS
PRODUCT_CATEGORY PROD_TS
SHIPPING PROD_TS
SUPPORT_TICKETS PROD_TS
TRANSACTIONS PROD_TS
USER_LOGIN PROD_TS
USER_PROFILE PROD_TS
20 rows selected.
These checks ensure the schema exists, is accessible, and all objects live in the expected tablespace.
5.2 Pre-Migration Checks on the Target Database
Switch to the target CDB, jump into the destination PDB, and confirm the user is ready.
[oracle@node1 ~]$ . oraenv
ORACLE_SID = [oracle] ? test1
[oracle@node1 ~]$ sqlplus / as sysdba
SQL> alter session set container=testpdb;
Session altered.
SQL> show con_name;
CON_NAME
TESTPDB
Verify schema existence:
SQL> SELECT username, account_status, created
FROM dba_users
WHERE username = 'APPUSER';
If the schema does not exist, create it and grant the minimum required privileges:
SQL> CREATE USER appuser IDENTIFIED BY appuser
DEFAULT TABLESPACE TEST_TS
TEMPORARY TABLESPACE TEMP;
SQL> GRANT connect, resource TO appuser;
Tip: Create the user before import. Letting
impdpcreate the user via theSCHEMAexport pulls in the source's quotas, default tablespace, and password hash — usually not what you want when remapping to a different environment.
5.3 Directory Object Configuration for Data Pump
The shared OS directory must exist on every RAC node, owned by oracle:oinstall:
[root@node1 ~]# mkdir -p /u01/dpump
[root@node1 ~]# chown oracle:oinstall /u01/dpump
[root@node1 ~]# chmod 750 /u01/dpump
[root@node2 ~]# mkdir -p /u01/dpump
[root@node2 ~]# chown oracle:oinstall /u01/dpump
[root@node2 ~]# chmod 750 /u01/dpump
Then create the Oracle directory object in both PDBs and grant access to the schema owner:
On source (PRODPDB):
SQL> CREATE OR REPLACE DIRECTORY dpump_dir AS '/u01/dpump';
Directory created.
SQL> GRANT READ, WRITE ON DIRECTORY dpump_dir TO appuser;
Grant succeeded.
On target (TESTPDB): identical commands.
5.4 Schema Export from the Source Database (expdp)
Now the actual export — parallel, multi-file, and explicitly non-clustered.
[oracle@node1 ~]$ expdp appuser/appuser@PRODPDB \
> DIRECTORY=dpump_dir \
> DUMPFILE=appuser_%U.dmp \
> LOGFILE=appuser_exp.log \
> SCHEMAS=APPUSER \
> PARALLEL=4 \
> FILESIZE=5G \
> CLUSTER=NO
What each parameter does:
| Parameter | Purpose |
|---|---|
DIRECTORY=dpump_dir | Logical directory pointing to the shared /u01/dpump path |
DUMPFILE=appuser_%U.dmp | %U is a 2-digit substitution variable so parallel workers can write distinct files |
LOGFILE | Plain-text job log — review every time |
SCHEMAS=APPUSER | Defines the export mode: schema-level |
PARALLEL=4 | Four worker processes; combine with %U for true parallel I/O |
FILESIZE=5G | Each dump file is capped at 5 GB — easier to ship, archive, and re-import |
CLUSTER=NO | Forces the job onto a single instance — avoids RAC cross-node file conflicts |
Sample log output:
Starting "APPUSER"."SYS_EXPORT_SCHEMA_01": appuser/*****@PRODPDB DIRECTORY=dpump_dir
DUMPFILE=appuser_%U.dmp LOGFILE=appuser_exp.log SCHEMAS=APPUSER PARALLEL=4 FILESIZE=5G
Processing object type SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA
Processing object type SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type SCHEMA_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type SCHEMA_EXPORT/TABLE_DATA
Processing object type SCHEMA_EXPORT/TABLE/COMMENT
Processing object type SCHEMA_EXPORT/TABLE/TABLE
Processing object type SCHEMA_EXPORT/TABLE/INDEX
Processing object type SCHEMA_EXPORT/TABLE/CONSTRAINT/CONSTRAINT
Processing object type SCHEMA_EXPORT/STATISTICS/MARKER
. . exported "APPUSER"."APP_LOGS" 8.335 KB 50 rows
. . exported "APPUSER"."APP_USERS" 7.312 KB 10 rows
. . exported "APPUSER"."ATTENDANCE" 6.593 KB 10 rows
. . exported "APPUSER"."AUDIT_TRAIL" 8.250 KB 50 rows
. . exported "APPUSER"."BANK_ACCOUNTS" 6.117 KB 10 rows
. . exported "APPUSER"."CONFIGURATION" 7.429 KB 50 rows
. . exported "APPUSER"."EMPLOYEES" 6.656 KB 10 rows
. . exported "APPUSER"."INVENTORY" 6.039 KB 10 rows
. . exported "APPUSER"."NOTIFICATIONS" 8.328 KB 50 rows
. . exported "APPUSER"."ORDERS" 6.578 KB 10 rows
. . exported "APPUSER"."ORDER_ITEMS" 0 KB 0 rows
. . exported "APPUSER"."PAYMENTS" 6.609 KB 10 rows
. . exported "APPUSER"."PAYROLL" 6.585 KB 10 rows
. . exported "APPUSER"."PRODUCTS" 6.695 KB 10 rows
. . exported "APPUSER"."PRODUCT_CATEGORY" 0 KB 0 rows
. . exported "APPUSER"."SHIPPING" 0 KB 0 rows
. . exported "APPUSER"."SUPPORT_TICKETS" 6.773 KB 10 rows
. . exported "APPUSER"."TRANSACTIONS" 6.601 KB 10 rows
. . exported "APPUSER"."USER_LOGIN" 0 KB 0 rows
. . exported "APPUSER"."USER_PROFILE" 7.132 KB 10 rows
ORA-39173: Encrypted data has been stored unencrypted in dump file set.
Master table "APPUSER"."SYS_EXPORT_SCHEMA_01" successfully loaded/unloaded
*****
Dump file set for APPUSER.SYS_EXPORT_SCHEMA_01 is:
/u01/dpump/appuser_01.dmp
/u01/dpump/appuser_02.dmp
/u01/dpump/appuser_03.dmp
Job "APPUSER"."SYS_EXPORT_SCHEMA_01" successfully completed at Mon Dec 29 16:39:59 2025
Note on
ORA-39173: This is an advisory, not an error. It tells you that columns encrypted at rest were written to the dump in cleartext (because noENCRYPTION=option was specified). Treat the dump file as sensitive material; ship it over secure channels and remove it from/u01/dpumponce import is verified.
Verify dump files:
[oracle@node1 ~]$ cd /u01/dpump/
[oracle@node1 dpump]$ ls
appuser_01.dmp appuser_02.dmp appuser_03.dmp appuser_exp.log
5.5 Schema Import into the Target Database (impdp)
The import is where the magic of REMAP_TABLESPACE happens — PROD_TS objects are silently relocated into TEST_TS.
[oracle@node1 ~]$ impdp appuser/appuser@TESTPDB \
> DIRECTORY=dpump_dir \
> DUMPFILE=appuser_%U.dmp \
> LOGFILE=appuser_imp.log \
> SCHEMAS=APPUSER \
> REMAP_TABLESPACE=PROD_TS:TEST_TS \
> PARALLEL=4
The log mirrors the export log line-for-line, except every entry now reads imported instead of exported:
Master table "APPUSER"."SYS_IMPORT_SCHEMA_01" successfully loaded/unloaded
Starting "APPUSER"."SYS_IMPORT_SCHEMA_01": appuser/*****@TESTPDB DIRECTORY=dpump_dir
DUMPFILE=appuser_%U.dmp LOGFILE=appuser_imp.log SCHEMAS=APPUSER REMAP_TABLESPACE=PROD_TS:TEST_TS
Processing object type SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA
Processing object type SCHEMA_EXPORT/TABLE/TABLE
Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA
. . imported "APPUSER"."APP_LOGS" 8.335 KB 50 rows
...
. . imported "APPUSER"."USER_PROFILE" 7.132 KB 10 rows
Processing object type SCHEMA_EXPORT/TABLE/CONSTRAINT/CONSTRAINT
Processing object type SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type SCHEMA_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type SCHEMA_EXPORT/STATISTICS/MARKER
Job "APPUSER"."SYS_IMPORT_SCHEMA_01" successfully completed at Mon Dec 29 16:58:29 2025
This single command recreates every schema object in the target PDB and quietly redirects each one to TEST_TS.
5.6 Post-Migration Verification
Connect as the application user and audit the result.
[oracle@node1 ~]$ sqlplus appuser/appuser@TESTPDB
SQL> show con_name;
CON_NAME
TESTPDB
SQL> show user;
USER is "APPUSER"
SQL> SELECT COUNT(*) AS table_count FROM user_tables;
SQL> SELECT table_name, tablespace_name
FROM user_tables
ORDER BY tablespace_name, table_name;
Expected result — every one of the 20 tables now sits in TEST_TS:
| TABLE_NAME | TABLESPACE |
|---|---|
| APP_LOGS | TEST_TS |
| APP_USERS | TEST_TS |
| ATTENDANCE | TEST_TS |
| AUDIT_TRAIL | TEST_TS |
| BANK_ACCOUNTS | TEST_TS |
| CONFIGURATION | TEST_TS |
| EMPLOYEES | TEST_TS |
| INVENTORY | TEST_TS |
| NOTIFICATIONS | TEST_TS |
| ORDERS | TEST_TS |
| ORDER_ITEMS | TEST_TS |
| PAYMENTS | TEST_TS |
| PAYROLL | TEST_TS |
| PRODUCTS | TEST_TS |
| PRODUCT_CATEGORY | TEST_TS |
| SHIPPING | TEST_TS |
| SUPPORT_TICKETS | TEST_TS |
| TRANSACTIONS | TEST_TS |
| USER_LOGIN | TEST_TS |
| USER_PROFILE | TEST_TS |
Migration scoreboard:
- ✅ Schema
APPUSERmigrated fromPRODPDBtoTESTPDB. - ✅ All objects imported without errors.
- ✅ Tablespace remapping completed successfully.
- ✅ Data integrity validated.
6. Tablespace-Level Migration, End to End
The second pattern flips perspective: instead of "move this user", the question becomes "move everything that lives in this tablespace". Useful for storage-tier moves, character-set migrations of a single tablespace, or carving out a subset of a system.
In this run we move tablespace TEST_TS (in TESTPDB) into PROD_TS (in PRODPDB), with CMSUSER as the owning schema on both sides.
6.1 Source Tablespace Ownership Verification (TESTPDB)
You must know exactly which schemas own segments inside the source tablespace before exporting. Otherwise, a multi-owner tablespace will surprise you mid-import.
[oracle@node1 ~]$ . oraenv
ORACLE_SID = [oracle] ? test1
[oracle@node1 ~]$ sqlplus / as sysdba
SQL> ALTER SESSION SET CONTAINER = testpdb;
SQL> SELECT owner, COUNT(*) AS segment_count
FROM dba_segments
WHERE tablespace_name = 'TEST_TS'
GROUP BY owner
ORDER BY owner;
OWNER SEGMENT_COUNT
--------- -------------
CMSUSER 39
Drill into segment types:
SQL> SELECT owner, segment_type, COUNT(*) AS cnt
FROM dba_segments
WHERE tablespace_name = 'TEST_TS'
GROUP BY owner, segment_type
ORDER BY owner, segment_type;
OWNER SEGMENT_TYPE CNT
--------- ------------ -----
CMSUSER INDEX 19
CMSUSER TABLE 20
This confirms CMSUSER is the only schema using TEST_TS — green light to proceed.
6.2 Target Environment Validation (PRODPDB)
Verify that the destination user exists and is mapped to the right tablespaces:
SQL> ALTER SESSION SET CONTAINER = PRODPDB;
Session altered.
SQL> SELECT username, account_status, created
FROM dba_users
WHERE username = 'CMSUSER';
SQL> SELECT username, default_tablespace, temporary_tablespace
FROM dba_users
WHERE username = 'CMSUSER';
This ensures CMSUSER is present in the target PDB and its default tablespace is PROD_TS.
6.3 Data Pump Directory Configuration
Identical pattern to the schema-level migration — create the OS directory on every RAC node, then create the Oracle directory object inside both source and target PDBs and grant READ, WRITE to CMSUSER:
[root@node1 ~]# mkdir -p /u01/dpump
[root@node1 ~]# chown oracle:oinstall /u01/dpump
[root@node1 ~]# chmod 750 /u01/dpump
-- On source (TESTPDB):
SQL> CREATE OR REPLACE DIRECTORY dpump_dir AS '/u01/dpump';
SQL> GRANT READ, WRITE ON DIRECTORY dpump_dir TO cmsuser;
-- On target (PRODPDB):
SQL> CREATE OR REPLACE DIRECTORY dpump_dir AS '/u01/dpump';
SQL> GRANT READ, WRITE ON DIRECTORY dpump_dir TO cmsuser;
6.4 Tablespace-Level Export from the Source PDB
The key parameter is TABLESPACES= instead of SCHEMAS=:
[oracle@node1 ~]$ expdp cmsuser/cmsuser@TESTPDB \
> DIRECTORY=dpump_dir \
> DUMPFILE=test_ts_%U.dmp \
> LOGFILE=test_ts_exp.log \
> TABLESPACES=test_ts \
> PARALLEL=4 \
> FILESIZE=5G \
> CLUSTER=NO
Sample output (abridged):
Starting "CMSUSER"."SYS_EXPORT_TABLESPACE_01": cmsuser/*****@TESTPDB
DIRECTORY=dpump_dir DUMPFILE=test_ts_%U.dmp LOGFILE=test_ts_exp.log TABLESPACES=test_ts
PARALLEL=4 FILESIZE=5G CLUSTER=NO
Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type TABLE_EXPORT/TABLE/COMMENT
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
Processing object type TABLE_EXPORT/TABLE/INDEX/INDEX
Processing object type TABLE_EXPORT/TABLE/CONSTRAINT/REF_CONSTRAINT
. . exported "CMSUSER"."ACCOUNT" 9.460 KB 100 rows
. . exported "CMSUSER"."FLIGHTS" 7.828 KB 28 rows
. . exported "CMSUSER"."PRODUCTS" 7.671 KB 28 rows
. . exported "CMSUSER"."BANK_TRANSACTIONS" 7.625 KB 28 rows
. . exported "CMSUSER"."STUDENTS" 7.554 KB 28 rows
. . exported "CMSUSER"."STOCK_MOVEMENTS" 7.414 KB 28 rows
. . exported "CMSUSER"."ECOMMERCE_CUSTOMERS" 7.187 KB 28 rows
. . exported "CMSUSER"."BANK_ACCOUNTS" 7.140 KB 28 rows
. . exported "CMSUSER"."ATTENDANCE" 7.023 KB 28 rows
. . exported "CMSUSER"."PATIENTS" 7.023 KB 28 rows
. . exported "CMSUSER"."FLIGHT_BOOKINGS" 7.023 KB 28 rows
. . exported "CMSUSER"."APPOINTMENTS" 7.000 KB 28 rows
. . exported "CMSUSER"."ECOMMERCE_ORDERS" 7.007 KB 28 rows
. . exported "CMSUSER"."PAYROLL" 7.000 KB 28 rows
. . exported "CMSUSER"."PROJECTS" 6.898 KB 4 rows
. . exported "CMSUSER"."HR_EMPLOYEES" 6.976 KB 28 rows
. . exported "CMSUSER"."EMPLOYEES" 6.921 KB 4 rows
. . exported "CMSUSER"."SALARY_HISTORY" 6.937 KB 4 rows
. . exported "CMSUSER"."ORDERS" 6.484 KB 4 rows
. . exported "CMSUSER"."DEPARTMENTS" 5.992 KB 4 rows
Dump file set for CMSUSER.SYS_EXPORT_TABLESPACE_01 is:
/u01/dpump/test_ts_01.dmp
/u01/dpump/test_ts_02.dmp
/u01/dpump/test_ts_03.dmp
/u01/dpump/test_ts_04.dmp
Job "CMSUSER"."SYS_EXPORT_TABLESPACE_01" successfully completed at Mon Dec 29 13:39:24 2025
Confirm the files exist on shared storage from a different RAC node — proof that the shared mount is wired correctly:
[oracle@node2 ~]$ cd /u01/dpump/
[oracle@node2 dpump]$ ls
test_ts_01.dmp test_ts_03.dmp test_ts_exp.log
test_ts_02.dmp test_ts_04.dmp test_write.txt
6.5 Tablespace-Level Import into the Target PDB
[oracle@node1 ~]$ impdp cmsuser/cmsuser@PRODPDB \
> DIRECTORY=dpump_dir \
> DUMPFILE=test_ts_%U.dmp \
> LOGFILE=prod_ts_imp.log \
> REMAP_TABLESPACE=test_ts:prod_ts \
> PARALLEL=4
Note an interesting nuance: even though the export job was SYS_EXPORT_TABLESPACE_01, the import job becomes SYS_IMPORT_FULL_01 because impdp reads the tablespace dump as a generic dump file:
Master table "CMSUSER"."SYS_IMPORT_FULL_01" successfully loaded/unloaded
Starting "CMSUSER"."SYS_IMPORT_FULL_01": cmsuser/*****@PRODPDB ...
Processing object type TABLE_EXPORT/TABLE/TABLE
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
. . imported "CMSUSER"."ACCOUNT" 9.460 KB 100 rows
...
. . imported "CMSUSER"."DEPARTMENTS" 5.992 KB 4 rows
Processing object type TABLE_EXPORT/TABLE/CONSTRAINT/CONSTRAINT
Processing object type TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type TABLE_EXPORT/TABLE/CONSTRAINT/REF_CONSTRAINT
Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type TABLE_EXPORT/TABLE/STATISTICS/MARKER
The REMAP_TABLESPACE=test_ts:prod_ts parameter is what lets the source tablespace be silently rebadged as the target tablespace at object-creation time.
6.6 Post-Migration Verification (PRODPDB)
Connect as the schema owner and confirm every table now lives in PROD_TS:
[oracle@node1 ~]$ sqlplus cmsuser/cmsuser@PRODPDB
SQL> show con_name;
CON_NAME
PRODPDB
SQL> show user;
USER is "CMSUSER"
SQL> SELECT COUNT(*) AS table_count FROM user_tables;
SQL> SELECT table_name, tablespace_name
FROM user_tables
ORDER BY tablespace_name, table_name;
| TABLE_NAME | TABLESPACE |
|---|---|
| ACCOUNT | PROD_TS |
| APPOINTMENTS | PROD_TS |
| ATTENDANCE | PROD_TS |
| BANK_ACCOUNTS | PROD_TS |
| BANK_TRANSACTIONS | PROD_TS |
| DEPARTMENTS | PROD_TS |
| ECOMMERCE_CUSTOMERS | PROD_TS |
| ECOMMERCE_ORDERS | PROD_TS |
| EMPLOYEES | PROD_TS |
| FLIGHTS | PROD_TS |
| FLIGHT_BOOKINGS | PROD_TS |
| HR_EMPLOYEES | PROD_TS |
| ORDERS | PROD_TS |
| PATIENTS | PROD_TS |
| PAYROLL | PROD_TS |
| PRODUCTS | PROD_TS |
| PROJECTS | PROD_TS |
| SALARY_HISTORY | PROD_TS |
| STOCK_MOVEMENTS | PROD_TS |
| STUDENTS | PROD_TS |
20 rows. Migration complete:
- ✅ Tablespace
TEST_TS→PROD_TSmigrated successfully. - ✅ All
CMSUSERobjects imported. - ✅ Tablespace remapping verified.
- ✅ No data loss.
7. Risks and Mitigation
A migration plan is only as good as its risk register. Here are the seven risks the implementation explicitly accounted for, with the mitigation actually applied.
7.1 Insufficient Disk Space
Risk: Insufficient disk space in the shared Data Pump directory or target tablespace causes export/import failure.
Mitigation:
- Verified filesystem space availability before export.
- Used
FILESIZEto split dump files. - Confirmed adequate free space in target tablespaces.
7.2 Missing or Invalid Schema/User on Target
Risk: Import fails if the target schema does not exist or is locked/expired.
Mitigation:
- Verified schema existence and account status before import.
- Ensured correct default and temporary tablespaces.
- Confirmed required privileges were in place.
7.3 Tablespace Mapping Errors
Risk: Objects land in incorrect tablespaces if remapping is misconfigured.
Mitigation:
- Explicitly used
REMAP_TABLESPACEduring import. - Validated tablespace mapping after import.
- Performed object-level verification using DBA views.
7.4 RAC-Related Conflicts
Risk: Parallel jobs across RAC nodes cause unexpected behavior or file-access issues.
Mitigation:
- Used a shared filesystem accessible across all RAC nodes.
- Set
CLUSTER=NOduring export to avoid cross-node conflicts. - Controlled parallelism to balance performance and stability.
7.5 Performance Impact
Risk: Export or import operations impact database performance.
Mitigation:
- Executed migration during a low-usage window.
- Used parallel execution for faster completion.
- Monitored database and OS performance during migration.
7.6 Data Consistency Issues
Risk: Inconsistent data if changes happen during export.
Mitigation:
- Performed migration during a controlled maintenance window.
- Ensured application inactivity during export.
- Used Data Pump's transactional consistency mechanisms.
7.7 Object or Data Loss
Risk: Tables, indexes, or constraints fail to import.
Mitigation:
- Reviewed export and import logs thoroughly.
- Verified object counts and row counts post-migration.
- Recompiled invalid objects where required.
8. Rollback Strategy
A migration without a rollback plan is an outage waiting to happen. Below is the rollback playbook used in this implementation.
8.1 Rollback Criteria
Roll back if any of the following occur:
- Import job fails with unrecoverable errors.
- Data validation does not match source.
- Application testing fails post-migration.
- Incorrect tablespace or schema mapping detected.
8.2 Schema-Level Rollback Procedure
If schema-level migration fails:
Connect to the target PDB as
SYSDBA.Identify imported objects.
Drop the affected schema/user:
SQL> DROP USER appuser CASCADE;Recreate the schema with correct settings.
Re-run the import after resolving the issue.
8.3 Tablespace-Level Rollback Procedure
If tablespace-level migration fails:
Connect to the target PDB as
SYSDBA.Identify objects imported into the target tablespace.
Drop affected objects or the tablespace itself:
SQL> DROP TABLESPACE prod_ts INCLUDING CONTENTS AND DATAFILES;Recreate the tablespace.
Re-run the import after correcting the issue.
8.4 Dump File Preservation
- Source dump files are retained until migration validation is complete.
- Dump files provide the ability to re-import data if needed.
- Source database remains unchanged throughout the process — the source is, by definition, your real rollback target.
8.5 Post-Rollback Validation
After rollback:
- Confirm schema and tablespace cleanup.
- Verify no residual objects remain.
- Ensure the target environment is restored to its original state.
9. Lessons Learned & Best Practices
Distilled from the implementation above — bookmark these for the next time you run Data Pump in anger.
- Always verify schema/tablespace ownership before exporting. A 5-second
dba_segmentsquery saves hours of cleanup. - Use
%UwithPARALLEL. Parallel without%Ucollapses to single-file output and gives no actual parallelism. CLUSTER=NOis your friend on RAC. Unless you have a specific reason to scatter Data Pump workers across instances, pin them locally.REMAP_TABLESPACElives only on import. It's the single most useful Data Pump parameter — and the one most often forgotten.- Read every line of the log.
ORA-39173is informational;ORA-39083(object-type creation failed) is not. Treat the log as the system of record. - Pre-create the target user with the target's defaults — don't let
impdpre-create it from the export. - Validate with DBA views, not gut feeling.
user_tables,dba_segments, anddba_constraintsare the truth. - Encrypted columns export unencrypted by default — handle dump files like sensitive data, or add
ENCRYPTION=andENCRYPTION_PASSWORD=. - Keep dump files until the new environment has been smoke-tested, then purge them — they are confidential snapshots of production data.
10. Future Enhancements
The same Data Pump foundation extends naturally to:
- Full database exports and imports.
- Transportable tablespace migrations (large-volume movement at near file-copy speed).
- Cross-PDB and cross-environment migrations.
- Automated environment refresh activities (refresh
TESTPDBfromPRODPDBon a schedule). - Database version upgrade support (export-from-older-version, import-into-newer-version).
11. Conclusion
This implementation successfully demonstrates schema-level and tablespace-level data migration using Oracle Data Pump in an Oracle 19c RAC Multitenant environment. The process followed best practices by:
- Validating prerequisites,
- Performing controlled exports and imports,
- Remapping tablespaces, and
- Executing post-migration verification.
The pattern is suitable for production-to-test refresh, environment cloning, database consolidation, and routine enterprise database administration tasks. The documented procedure can be reused as a standard operating guideline for future Oracle database migrations — bring the parameters, change the names, keep the discipline.
Until the next migration window — happy pumping. 🛢️
Thanks for reading.
BR,
Syed Zaheer
Comments