Separando tabelas e índices em tablespaces diferentes no impdp
REMAP_TABLESPACE pensa em tablespace de origem e não sabe o que é índice. Quem separa tabela de índice na importação é o TRANSFORM, junto com a default tablespace do usuário.

Peguei uma migração dessas em que a origem tinha tudo jogado na mesma tablespace. Tabela, índice, LOB, tudo no mesmo lugar. No destino eu queria a organização que deveria existir desde sempre: dados numa tablespace, índices em outra.
O reflexo é usar REMAP_TABLESPACE. Só que ele não resolve isso, e vale entender por quê antes de sair batendo cabeça.
Por que o REMAP_TABLESPACE não serve aqui
O REMAP_TABLESPACE trabalha por tablespace de origem. Você diz "tudo que estava em A vai para B". Ele não sabe, e não quer saber, se o objeto é tabela ou índice.
Então quando eu escrevo isso:
REMAP_TABLESPACE=TS_LEGADO:DADOS_01
tabela e índice vão os dois para DADOS_01. Não existe sintaxe do tipo "de TS_LEGADO, tabela vai para X e índice vai para Y". O parâmetro simplesmente não tem essa dimensão.
Se a origem já tivesse tablespaces separadas, seria trivial: dois remaps e pronto. O problema aparece justamente quando a origem é bagunçada e você quer arrumar na chegada.
O pulo do gato
A saída está no TRANSFORM, que aceita tipo de objeto. Diferente do remap, ele consegue mirar só nos índices.
A ideia é remover a cláusula de tablespace dos índices e deixar que eles caiam na default tablespace do usuário. Aí é só apontar a default para a tablespace de índices antes de importar.
Primeiro, ajuste o usuário:
ALTER USER ERP_PROD DEFAULT TABLESPACE INDICES_01;
ALTER USER ERP_PROD QUOTA UNLIMITED ON INDICES_01;
ALTER USER ERP_PROD QUOTA UNLIMITED ON DADOS_01;
Depois rode o import com o transform aplicado apenas a INDEX:
impdp labdba/senha@localhost:1521/lab_impdp \
DIRECTORY=DATA_PUMP_DIR DUMPFILE=EXPDP_ERP_PROD.dmp \
LOGFILE=imp_erp_prod.log LOGTIME=ALL METRICS=Y \
SCHEMAS=ERP_PROD PARALLEL=4 \
TRANSFORM=OID:N,SEGMENT_ATTRIBUTES:N:INDEX \
REMAP_TABLESPACE=TS_LEGADO:DADOS_01
Rodei num PDB, por isso a string de conexão. Em base non-CDB o '"/ as sysdba"'
de sempre resolve, e o resto do comando é idêntico.
Um erro vai aparecer no fim, e ele é esperado:
ORA-31684: Object type USER:"ERP_PROD" already exists
O dump traz o CREATE USER, e você criou o usuário antes justamente para
apontar a default tablespace. O Data Pump reclama e segue. Se esse erro não
aparecer, é sinal de que o usuário não existia, e aí a default que você queria
nunca foi aplicada.
O que acontece na prática: as tabelas seguem o REMAP_TABLESPACE normalmente e vão para DADOS_01. Os índices perdem a cláusula TABLESPACE no DDL e nascem na default do usuário, que agora é INDICES_01.
Terminado o import, devolva a default para o que ela deveria ser:
ALTER USER ERP_PROD DEFAULT TABLESPACE DADOS_01;
Esse último passo é fácil de esquecer e chato de descobrir meses depois, quando a aplicação criar uma tabela nova e ela for parar na tablespace de índices.
O que o SEGMENT_ATTRIBUTES leva junto
Aqui entra a ressalva honesta. O SEGMENT_ATTRIBUTES:N não remove só o tablespace. Ele tira também os atributos de storage e o logging.
Na maioria dos casos isso é bom. O índice nasce com os defaults da tablespace nova, sem arrastar INITIAL e NEXT que alguém definiu na origem em 2015 e ninguém nunca revisou. Mas se o seu ambiente depende de storage customizado em índices específicos, você vai perder isso e precisa saber disso antes, não depois.
Se quiser preservar o storage e mexer só no destino, não tem jeito pelo transform. Aí o caminho é importar e depois mover com REBUILD.
Os índices de constraint podem escapar
Outro detalhe que vale conferir. Índices que dão suporte a chave primária ou única não vêm como objeto INDEX no Data Pump. Eles vêm junto do CONSTRAINT, na cláusula USING INDEX TABLESPACE.
Ou seja, o transform mirado em INDEX pode não pegar esses. Depois do import, confira onde eles foram parar:
set verify off
set feedback off
set linesize 200
col "Tablespace" form a22
col "Qtd Indices" form 999G999G990
SELECT NVL(tablespace_name, '(sem segmento)') AS "Tablespace",
COUNT(*) AS "Qtd Indices"
FROM dba_indexes
WHERE owner = 'ERP_PROD'
GROUP BY tablespace_name
ORDER BY COUNT(*) DESC;
Se sobrar alguma coisa na tablespace de dados, resolve com rebuild pontual:
ALTER INDEX ERP_PROD.<indice> REBUILD TABLESPACE INDICES_01 ONLINE;
São poucos, então o custo é baixo.
Conferindo o resultado
O retrato que eu uso para validar sai do DBA_SEGMENTS:
set verify off
set feedback off
set linesize 200
alter session set nls_numeric_characters = ',.';
col "Tipo" form a12
col "Tablespace" form a22
col "Qtd" form 999G999G990
col "Tamanho (MB)" form 999G999G990D00
SELECT segment_type AS "Tipo",
tablespace_name AS "Tablespace",
COUNT(*) AS "Qtd",
ROUND(SUM(bytes)/1024/1024, 2) AS "Tamanho (MB)"
FROM dba_segments
WHERE owner = 'ERP_PROD'
GROUP BY segment_type, tablespace_name
ORDER BY segment_type, tablespace_name;
Tipo Tablespace Qtd Tamanho (MB)
------------ ---------------------- ------------ ---------------
INDEX DADOS_01 1 4,00
INDEX INDICES_01 3 12,00
LOBINDEX DADOS_01 1 0,06
LOBSEGMENT DADOS_01 1 0,13
TABLE DADOS_01 1 24,00
Três índices foram para INDICES_01, como eu queria. Mas repare no que ficou para trás: um índice continuou em DADOS_01, junto com a tabela e os dois segmentos de LOB.
O índice teimoso é o da chave primária, e é exatamente a ressalva da seção anterior. Olhando um por um:
Indice Tablespace Origem
-------------------------- -------------- --------------
PK_PEDIDOS DADOS_01 de constraint
SYS_IL0000073153C00005$$ DADOS_01 comum
IX_PEDIDOS_CLIENTE INDICES_01 comum
IX_PEDIDOS_STATUS INDICES_01 comum
UK_PEDIDOS_NOTA INDICES_01 comum
O PK_PEDIDOS veio pelo CONSTRAINT, não pelo INDEX, e por isso o transform não o alcançou. O SYS_IL... é o índice do LOB, que é segmento preso à tabela.
O rebuild resolve o da constraint:
ALTER INDEX ERP_PROD.PK_PEDIDOS REBUILD TABLESPACE INDICES_01 ONLINE;
Indice Tablespace Origem
-------------------------- -------------- --------------
SYS_IL0000073153C00005$$ DADOS_01 comum
IX_PEDIDOS_CLIENTE INDICES_01 comum
IX_PEDIDOS_STATUS INDICES_01 comum
PK_PEDIDOS INDICES_01 de constraint
UK_PEDIDOS_NOTA INDICES_01 comum
O LOB continua com a tabela, e faz sentido: ele é segmento preso a ela, e o transform não foi aplicado a esse tipo. Se você quiser LOB em outra tablespace, é outro problema e outro parâmetro.
E se o import já estiver rodando
Não dá para mudar no meio. O TRANSFORM precisa ser informado na largada.
Se você percebeu tarde, tem duas opções. Matar o job, dropar o usuário e recomeçar, ou deixar terminar e mover os índices depois com REBUILD. A escolha depende de onde você está.
Na migração de cliente que originou este artigo eram mais de treze mil índices. Rebuild disso tudo gera bastante redo e leva um tempo comparável ao próprio import, então matei o job e recomecei. Se fosse um schema pequeno, provavelmente teria deixado rolar.
Fechando
O truque em si é uma linha de parâmetro. O que demora é entender que REMAP_TABLESPACE e TRANSFORM resolvem problemas diferentes: um pensa em tablespace de origem, o outro pensa em tipo de objeto. Quando você precisa cruzar as duas coisas, é o transform mais a default tablespace que fecha a conta.
Vale como oportunidade também. Migração é uma das poucas horas em que você pode arrumar a organização física de um schema sem parar nada além do que já ia parar.
Testado em Oracle Database 19c (19.0.0.0.0) sobre Oracle Linux 8.10, num PDB de laboratório criado só para isto, em agosto de 2026. A técnica veio de uma migração real de cliente; o cenário foi remontado no laboratório com nomes neutros, e todas as saídas acima são as que apareceram na tela.
Marcio Mandarino é DBA Oracle e SQL Server há mais de 20 anos, com passagens por ambientes críticos de varejo, saúde, educação e locação de equipamentos.
Marcio Mandarino
DBA Oracle e SQL Server há mais de 20 anos. Atendo plantão, recuperação de incidente e gestão contínua de bancos de dados. Este artigo saiu de um laboratório que rodou antes de virar texto.

