Yesterday a comment was made on a video on our YouTube channel. The question was, what happens when you try to transport a tablespace that is not self-contained. Let’s find out.
First, some test data:
create tablespace a; create tablespace b; create tablespace c; create user daniel identified by oracle; grant dba to daniel; create table daniel.sales (c1 number) tablespace a; create table daniel.orders (c1 number) tablespace c; create index daniel.i_orders on daniel.orders (c1) tablespace b;
Then, let’s run the Data Pump command to start the process
alter tablespace a read only; alter tablespace b read only; host expdp daniel/oracle transport_tablespaces=a,b Export: Release 188.8.131.52.0 - Production on Fri Feb 4 12:32:14 2022 Version 184.108.40.206.0 ... Processing object type TRANSPORTABLE_EXPORT/STATISTICS/TABLE_STATISTICS ORA-39123: Data Pump transportable tablespace job aborted ORA-39187: The transportable set is not self-contained, violation list is ORA-39907: Index DANIEL.I_ORDERS in tablespace B points to table DANIEL.ORDERS in tablespace C. Job "DANIEL"."SYS_EXPORT_TRANSPORTABLE_01" stopped due to fatal error at Fri Feb 4 12:32:33 2022 elapsed 0 00:00:17
The answer is: Data Pump will check the set of tablespaces and ensure they are self-contained, when you perform the export.
In the documentation it lists the following as one of the tasks for transporting tablespaces:
- Pick a self-contained set of tablespaces.
What does that mean exactly? Later on, in the documentation there are some examples of violations:
- An index inside the set of tablespaces is for a table outside of the set of tablespaces.
- A partitioned table is partially contained in the set of tablespaces.
- A referential integrity constraint points to a table across a set boundary.
Let me illustrate it. Imagine I want to transport tablespaces A and B.
This first illustration shows a self-contained tablespace set. Table SALES is in tablespace A and a corresponding index I_SALES in tablespace B. Both tablespaces are part of the set that I want to transport. All good!
The next example is no good. Tablespace B now contains an index I_ORDERS, which refers to a table ORDERS which is not placed in the tablespaces that I want to transport. Either I have to drop the index I_ORDERS or move the table ORDERS into one of the tablespaces I am transporting.
This example is also no good. Table SALES has three partitions. One of the partitions is in a tablespace that I am not transporting. I need to either drop the partition in tablespace C or move it to one of the other tablespaces.
This last example is also no good. Table SALES has a foreign key constraint that refers table ORDERS, but ORDERS is located outside the tablespace set. There is a solution to this which will be discussed shortly.
You can use
DBMS_TTS to check whether a given set of tablespaces are self-contained. Using my previous example. I would run:
Next, I could check the result:
SQL> select * from transport_set_violations;
By default, the procedure transport_set_check doesn’t check for foreign keys that refer to something outside the tablespace set. This means that my last illustration above would not be found by transport_set_check, and you would end up with an error when you try to transport the tablespaces.
To also check for bad foreign key constraints:
execute dbms_tts.transport_set_check('A,B', true);
Foreign Key Constraints
If you have foreign key constraints that refer to a table outside of the tablespace set, it is one of the issues which can be ignored. You can instruct Data Pump to exclude constraints, and this issue will be ignored:
$ expdp ... transport_tablespaces=A,B exclude=constraint
When you import the tablespaces into another database, there won’t be any constraints. Even those foreign key constraints that were valid are gone. And even check constaints. All of them! You should consider whether you want to create them again manually.
If you can exclude constraints, can you also exclude indexes? If possible, you could avoid dropping or moving an offending index. Let’s try! I am using the same test data as the first example.
Check for violations:
execute dbms_tts.transport_set_check('A,B'); select * from transport_set_violations; VIOLATIONS ----------------------------------------------------------------- ORA-39907: Index DANIEL.I_ORDERS in tablespace B points to table DANIEL.ORDERS in tablespace C.
As expected, index I_ORDERS is a problem. Let’s try to export and exclude indexes (
alter tablespace a read only; alter tablespace b read only; host expdp daniel/oracle transport_tablespaces=a,b exclude=index Export: Release 220.127.116.11.0 - Production on Fri Feb 4 12:32:14 2022 Version 18.104.22.168.0 Copyright (c) 1982, 2019, Oracle and/or its affiliates. All rights reserved. Connected to: Oracle Database 19c Enterprise Edition Release 22.214.171.124.0 - Production Starting "DANIEL"."SYS_EXPORT_TRANSPORTABLE_01": daniel/******** transport_tablespaces=a,b exclude=index Processing object type TRANSPORTABLE_EXPORT/STATISTICS/TABLE_STATISTICS ORA-39123: Data Pump transportable tablespace job aborted ORA-39187: The transportable set is not self-contained, violation list is ORA-39907: Index DANIEL.I_ORDERS in tablespace B points to table DANIEL.ORDERS in tablespace C. Job "DANIEL"."SYS_EXPORT_TRANSPORTABLE_01" stopped due to fatal error at Fri Feb 4 12:32:33 2022 elapsed 0 00:00:17
It is not possible to exclude indexes in the same way as constraints. However, I think it makes sense. A constraint is just metadata; something that is recorded in the dictionary. We can easily ignore that when doing the Data Pump export. An index is an object with segments; data in the tablespace. Imagine we could somehow exclude the index. All those blocks belonging to the index would now be zombie blocks belonging to nothing.
If you need to move data out of a running database and your tablespaces are not self-contained, you need to fix the issues. Some of the solutions involve dropping or moving data, but you might not want to do that in a running database.
Imagine the example above where there is an index that refers to a table outside of the tablespace set (I_ORDERS). You probably need that index in your running database, so not a good idea to drop the index.
But you can use your standby database if you have one.
- Convert your physical standby into a snapshot standby.
- On your snapshot standby, do the required changes to make your tablespace set self-contained.
- Still, on the snapshot standby, copy the data files and perform the Data Pump export to generate your transportable tablespace set and corresponding Data Pump dump file.
- Revert the snapshot standby back into a physical standby. This will automatically revert all the temporary changes you made and re-sync with the primary database.
If you don’t have a standby database, you could achieve the same with Flashback Database, but that would require an outage on the running database.
If you try to transport tablespaces that are not self-contained, you will get an error. There is no way around the issues except for foreign key constraints. If you need to make changes to your database to have self-contained tablespaces, you can do it on a snapshot standby database to avoid interfering with a running database.
Other Blog Posts in This Series
- Full Transportable Export/Import
- What Is a Self-contained Transportable Tablespace Set
- How to Migrate a Database Using Full Transportable Export/Import and Incremental Backups
- Make It Fast
- Backup on Standby Database
- Slow Network Between Source and Target
- Bigfile Tablespaces
- Testing the Procedure On Activated Standby Database
- Target Database and Data Guard
- ASM Aliases and Why You Should Get Rid of Them
- Pro Tips