Migrate a PDB Using Unplug/Plug on the same server: In this article, we explain how to migrate a PDB using the Unplug/Plug method in Oracle 19c on the same server. In this case, the source and target CDBs are different, but both are hosted on the same server and use the same Oracle home.
Environment Details:
Same server: (same Oracle home/host)
Source CDB Name & Version: CDB1, 19.30
Target CDB Name & Version: CDB2, 19.30
PDB to migrate: orclpdb
Method: Unplug from CDB1 → Plug into CDB2Important Notes:
✔ Source and target CDB must be same version or target is higher version
✔ Check compatibility before plugSteps:
- Check Source PDB Status (CDB1):
- Verify Component Compatibility (CDB1 and CDB2):
- Take Full CDB RMAN Backup (Mandatory Best Practice):
- Close the PDB (Source CDB1):
- Unplug the PDB from Source CDB1:
- Drop PDB from Source CDB1 (Keep Datafiles):
- Verify XML Metadata File at OS Level:
- Check Target CDB Status (CDB2):
- Check Plug Compatibility Before Plugging into Target CDB (CDB2):
- Plug PDB into Target CDB (CDB2 using NOCOPY):
- Verify PDB in Target CDB2:
- Open the Plugged PDB:
- Save PDB State (Auto Open After Restart):
- Verify Database Components After Migration:
- Check Invalid Objects in PDB:
- Recompile Invalid Objects (Best Practice):
- Recheck Invalid Objects After Compile:
Check Source PDB Status (CDB1):
Check the ORCLPDB pluggable database status from CDB1.
[oracle@primary ~]$ ps -ef | grep pmon
oracle 28884 4649 0 00:57 ? 00:00:00 ora_pmon_CDB1
oracle 30100 4649 0 00:59 ? 00:00:00 ora_pmon_CDB2
oracle 32502 6363 0 01:32 pts/1 00:00:00 grep --color=auto pmon
[oracle@primary ~]$
[oracle@primary ~]$
[oracle@primary ~]$ export ORACLE_SID=CDB1
[oracle@primary ~]$
[oracle@primary ~]$ sqlplus / as sysdba
SQL*Plus: Release 19.0.0.0.0 - Production on Fri May 15 01:33:10 2026
Version 19.30.0.0.0
Copyright (c) 1982, 2025, Oracle. All rights reserved.
Connected to:
Oracle Database 19c Standard Edition 2 Release 19.0.0.0.0 - Production
Version 19.30.0.0.0
SQL> set lines 400 pages 400
SQL> select name,database_role,open_mode,cdb from v$database;
NAME DATABASE_ROLE OPEN_MODE CDB
--------- ---------------- -------------------- ---
CDB1 PRIMARY READ WRITE YES
SQL>
SQL>
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
3 ORCLPDB READ WRITE NO
SQL>Verify Component Compatibility (CDB1 and CDB2):
Run below query on both CDBs:
SQL> select name,database_role,open_mode,cdb from v$database;
NAME DATABASE_ROLE OPEN_MODE CDB
--------- ---------------- -------------------- ---
CDB1 PRIMARY READ WRITE YES
SQL>
SQL>
SQL> select comp_name, version, status from dba_registry;
COMP_NAME VERSION STATUS
---------------------------------------- ------------------------------ --------------------
Oracle Database Catalog Views 19.0.0.0.0 VALID
Oracle Database Packages and Types 19.0.0.0.0 VALID
Oracle Real Application Clusters 19.0.0.0.0 OPTION OFF
JServer JAVA Virtual Machine 19.0.0.0.0 VALID
Oracle XDK 19.0.0.0.0 VALID
Oracle Database Java Packages 19.0.0.0.0 VALID
OLAP Analytic Workspace 19.0.0.0.0 OPTION OFF
Oracle XML Database 19.0.0.0.0 VALID
Oracle Workspace Manager 19.0.0.0.0 VALID
Oracle Text 19.0.0.0.0 VALID
Oracle Multimedia 19.0.0.0.0 VALID
Spatial 19.0.0.0.0 VALID
Oracle OLAP API 19.0.0.0.0 OPTION OFF
Oracle Label Security 19.0.0.0.0 OPTION OFF
Oracle Database Vault 19.0.0.0.0 OPTION OFF
15 rows selected.
SQL>
===================================================================
SQL> select name,database_role,open_mode,cdb from v$database;
NAME DATABASE_ROLE OPEN_MODE CDB
--------- ---------------- -------------------- ---
CDB2 PRIMARY READ WRITE YES
SQL>
SQL>
SQL> select comp_name, version, status from dba_registry;
COMP_NAME VERSION STATUS
---------------------------------------- ------------------------------ --------------------
Oracle Database Catalog Views 19.0.0.0.0 VALID
Oracle Database Packages and Types 19.0.0.0.0 VALID
Oracle Real Application Clusters 19.0.0.0.0 OPTION OFF
JServer JAVA Virtual Machine 19.0.0.0.0 VALID
Oracle XDK 19.0.0.0.0 VALID
Oracle Database Java Packages 19.0.0.0.0 VALID
OLAP Analytic Workspace 19.0.0.0.0 OPTION OFF
Oracle XML Database 19.0.0.0.0 VALID
Oracle Workspace Manager 19.0.0.0.0 VALID
Oracle Text 19.0.0.0.0 VALID
Oracle Multimedia 19.0.0.0.0 VALID
Spatial 19.0.0.0.0 VALID
Oracle OLAP API 19.0.0.0.0 OPTION OFF
Oracle Label Security 19.0.0.0.0 OPTION OFF
Oracle Database Vault 19.0.0.0.0 OPTION OFF
15 rows selected.
SQL>Take Full CDB RMAN Backup (Mandatory Best Practice):
run
{
allocate channel c1 device type disk;
backup database format '/backup/full_db_%d_%T_%U.bkp';
backup archivelog all format '/backup/arch_%d_%T_%U.bkp';
backup current controlfile format '/backup/ctrl_%d_%T_%U.bkp';
backup spfile format '/backup/spfile_%d_%T_%U.bkp';
release channel c1;
}Close the PDB (Source CDB1):
SQL> alter pluggable database ORCLPDB close immediate;
Pluggable database altered.
SQL>
SQL>
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
3 ORCLPDB MOUNTED
SQL>Unplug the PDB from Source CDB1:
SQL> alter pluggable database ORCLPDB unplug into '/u01/app/oracle/oradata/orclpdb.xml';
Pluggable database altered.
SQL>Drop PDB from Source CDB1 (Keep Datafiles):
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
3 ORCLPDB MOUNTED
SQL>
SQL>
SQL> drop pluggable database ORCLPDB keep datafiles;
Pluggable database dropped.
SQL>
SQL>
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
SQL>Verify XML Metadata File at OS Level:
[oracle@primary ~]$
[oracle@primary ~]$ ls -ltrh /u01/app/oracle/oradata/orclpdb.xml
-rw-r--r--. 1 oracle oinstall 7.4K May 15 01:41 /u01/app/oracle/oradata/orclpdb.xml
[oracle@primary ~]$Check Target CDB Status (CDB2):
[oracle@primary ~]$ export ORACLE_SID=CDB2
[oracle@primary ~]$
[oracle@primary ~]$ sqlplus / as sysdba
SQL*Plus: Release 19.0.0.0.0 - Production on Fri May 15 01:43:32 2026
Version 19.30.0.0.0
Copyright (c) 1982, 2025, Oracle. All rights reserved.
Connected to:
Oracle Database 19c Standard Edition 2 Release 19.0.0.0.0 - Production
Version 19.30.0.0.0
SQL>
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
SQL>Check Plug Compatibility Before Plugging into Target CDB (CDB2):
SQL> set serveroutput on;
SQL> declare
2 compatible boolean;
3 begin
4 compatible := dbms_pdb.check_plug_compatibility(
5 pdb_descr_file => '/u01/app/oracle/oradata/orclpdb.xml'
6 );
7 if compatible then
8 dbms_output.put_line('PDB is COMPATIBLE');
9 else
10 dbms_output.put_line('PDB is NOT COMPATIBLE');
11 end if;
12 end;
13 /
PDB is COMPATIBLE
PL/SQL procedure successfully completed.
SQL>Plug PDB into Target CDB (CDB2 using NOCOPY):
- Same Server + Same Storage → Use
NOCOPYbecause Oracle reuses existing datafiles without creating duplicate copies. - Different Storage → Use
COPYbecause Oracle creates new copies of datafiles in the target storage location. - Cross Server Migration → Use
COPYbecause source and target servers do not share the same filesystem/storage. - Minimal Downtime → Use
NOCOPYbecause it is faster and avoids physical file copy operations. - Storage Optimization → Use
NOCOPYbecause it saves storage space by using existing datafiles directly.
In our case, since both CDBs are on the same server and using the same storage, we are using the NOCOPY option.
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
SQL>
SQL>
SQL> create pluggable database ORCLPDB using '/u01/app/oracle/oradata/orclpdb.xml' nocopy tempfile reuse;
Pluggable database created.
SQL>Verify PDB in Target CDB2:
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
4 ORCLPDB MOUNTED
SQL>Open the Plugged PDB:
SQL> alter pluggable database ORCLPDB open;
Pluggable database altered.
SQL>
SQL>
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
4 ORCLPDB READ WRITE NO
SQL>Save PDB State (Auto Open After Restart):
SQL> alter pluggable database ORCLPDB save state;
Pluggable database altered.
SQL>Verify Database Components After Migration:
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
4 ORCLPDB READ WRITE NO
SQL>Check Invalid Objects in PDB:
SQL> select owner, count(*) from dba_objects where status='INVALID' group by owner;
no rows selected
SQL>Recompile Invalid Objects (Best Practice):
In our case, there are no invalid objects observed after migration completion. However, if any invalid objects are found after the migration, then execute utlrp.sql to recompile and resolve them.
SQL> @?/rdbms/admin/utlrp.sql
Session altered.
TIMESTAMP
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
COMP_TIMESTAMP UTLRP_BGN 2026-05-15 02:26:45
DOC> The following PL/SQL block invokes UTL_RECOMP to recompile invalid
DOC> objects in the database. Recompilation time is proportional to the
DOC> number of invalid objects in the database, so this command may take
DOC> a long time to execute on a database with a large number of invalid
DOC> objects.
DOC>
DOC> Use the following queries to track recompilation progress:
DOC>
DOC> 1. Query returning the number of invalid objects remaining. This
DOC> number should decrease with time.
DOC> SELECT COUNT(*) FROM obj$ WHERE status IN (4, 5, 6);
DOC>
DOC> 2. Query returning the number of objects compiled so far. This number
DOC> should increase with time.
DOC> SELECT COUNT(*) FROM UTL_RECOMP_COMPILED;
DOC>
DOC> This script automatically chooses serial or parallel recompilation
DOC> based on the number of CPUs available (parameter cpu_count) multiplied
DOC> by the number of threads per CPU (parameter parallel_threads_per_cpu).
DOC> On RAC, this number is added across all RAC nodes.
DOC>
DOC> UTL_RECOMP uses DBMS_SCHEDULER to create jobs for parallel
DOC> recompilation. Jobs are created without instance affinity so that they
DOC> can migrate across RAC nodes. Use the following queries to verify
DOC> whether UTL_RECOMP jobs are being created and run correctly:
DOC>
DOC> 1. Query showing jobs created by UTL_RECOMP
DOC> SELECT job_name FROM dba_scheduler_jobs
DOC> WHERE job_name like 'UTL_RECOMP_SLAVE_%';
DOC>
DOC> 2. Query showing UTL_RECOMP jobs that are running
DOC> SELECT job_name FROM dba_scheduler_running_jobs
DOC> WHERE job_name like 'UTL_RECOMP_SLAVE_%';
DOC>#
PL/SQL procedure successfully completed.
TIMESTAMP
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
COMP_TIMESTAMP UTLRP_END 2026-05-15 02:26:47
DOC> The following query reports the number of invalid objects.
DOC>
DOC> If the number is higher than expected, please examine the error
DOC> messages reported with each object (using SHOW ERRORS) to see if they
DOC> point to system misconfiguration or resource constraints that must be
DOC> fixed before attempting to recompile these objects.
DOC>#
OBJECTS WITH ERRORS
-------------------
0
DOC> The following query reports the number of exceptions caught during
DOC> recompilation. If this number is non-zero, please query the error
DOC> messages in the table UTL_RECOMP_ERRORS to see if any of these errors
DOC> are due to misconfiguration or resource constraints that must be
DOC> fixed before objects can compile successfully.
DOC> Note: Typical compilation errors (due to coding errors) are not
DOC> logged into this table: they go into DBA_ERRORS instead.
DOC>#
ERRORS DURING RECOMPILATION
---------------------------
0
Function created.
PL/SQL procedure successfully completed.
Function dropped.
PL/SQL procedure successfully completed.
SQL>Recheck Invalid Objects After Compile:
SQL> select owner, count(*) from dba_objects where status='INVALID' group by owner;
no rows selected
SQL>I hope my written content will helped you. Your suggestions/feedback are most welcome from orakldba.com. Keep learning… Have a great day!!!
Please comment here for your any query related to above content. You can write to mail me through my Email.Email :- orakldba@gmail.com
LinkedIn :- https://linkedin.com/in/prasantakirtaniya
Twitter :- https://twitter.com/prasantadba
Facebook Page :- https://www.facebook.com/orakldba
Website :- https://orakldba.com
ACE Profile :- https://ace.oracle.com/apex/ace/profile/prasantakirtaniya
