Guia de Migração de Oracle para SQL Server e SQL Server para Oracle - Pt. 2
Publicados: 2022-03-11A primeira parte desta série discutiu as diferenças entre Oracle Database e Microsoft SQL Server em sua implementação de transações, com foco nas armadilhas que podem ser encontradas durante as migrações de Oracle para SQL Server e vice-versa. Esta próxima parte abordará vários elementos de sintaxe SQL comumente usados que não têm correspondência ou têm um significado ou uso completamente diferente na divisão Oracle–SQL Server.
Sequências no Oracle e Colunas de Identidade no SQL Server
Há uma divisão de longa data na comunidade de banco de dados entre dois campos: os patriotas das chaves naturais e os defensores das chaves artificiais (ou “substitutas”).
Eu mesmo defendo as chaves naturais, mas muitas vezes me pego criando substitutos por uma razão ou outra. Mas deixando de lado a substância desse debate, vamos examinar os mecanismos padrão para gerar chaves artificiais: sequências Oracle e colunas de identidade do SQL Server.
Uma sequência Oracle é um objeto de nível de banco de dados de primeira classe. Por outro lado, uma coluna de identidade do SQL Server é um tipo de coluna, não um objeto.
Quando uma sequência Oracle é usada para gerar uma chave de tabela - geralmente uma chave primária - é garantido que ela será incrementada e, portanto, exclusiva. Mas não é garantido que seja consecutivo. Na verdade, mesmo em implementações bem projetadas, é mais provável que haja algumas lacunas. Portanto, nenhuma implementação do Oracle deve depender de valores gerados por sequência para serem consecutivos.
Além disso, uma sequência é gerenciada por meio de um dicionário de dados do banco de dados Oracle, portanto, seria muito demorado (e complicado) criar uma sequência dedicada para suportar cada chave substituta. Um único objeto de sequência pode suportar várias ou até mesmo todas as chaves substitutas.
Por outro lado, quando vários processos precisam acessar NEXTVAL
(o próximo valor incremental) de uma sequência, a sequência se tornará um recurso crítico de acesso único. Ele efetivamente tornará todos os processos que o acessam estritamente sequenciais, transformando qualquer implementação multi-thread (single ou multi-server) em um processo single-thread, com longos tempos de espera e alto uso de memória/baixo uso de CPU.
Tais implementações realmente acontecem. A solução para esse problema é definir o objeto de sequência em questão com um valor de cache razoável - o que significa que um intervalo definido de valores (seja 100 ou 100 mil) é selecionado em um cache para um processo de chamada, registrado no dicionário de dados conforme usado , e ficam disponíveis para esse processo específico sem a necessidade de acessar o dicionário de dados toda vez que o NEXTVAL
é chamado.
Mas é exatamente por isso que as lacunas serão criadas, pois nem todos os valores armazenados em cache provavelmente serão usados. Isso também significa que em vários processos em sessões paralelas, alguns valores de sequência registrados podem ser invertidos cronologicamente. Essa inversão não pode ocorrer em um único processo, a menos que um valor de sequência tenha sido redefinido ou retrocedido. Mas este último cenário equivale a procurar problemas: deve ser desnecessário e, se implementado incorretamente, pode resultar na geração de valores duplicados.
Portanto, a única maneira correta de usar sequências do Oracle é para a geração de chaves substitutas: chaves que são exclusivas, mas que não devem conter nenhuma outra informação confiável.
Colunas de identidade no SQL Server
E o SQL Server? Embora sequências com funcionalidade e implementação muito semelhantes à sua contraparte Oracle tenham sido introduzidas no SQL Server 2012, elas não são uma técnica de primeira linha. Como outras adições de recursos, eles fazem sentido para conversão do Oracle, mas ao implementar chaves substitutas do zero no SQL Server, IDENTITY
é uma opção muito melhor.
IDENTITY
é um objeto “filho” de uma tabela. Ele não acessa nenhum recurso fora de uma tabela e é garantido que seja sequencial, a menos que seja manipulado deliberadamente. E foi projetado especificamente para essa tarefa, e não para compatibilidade semântica com o Oracle.
Com a Oracle implementando a funcionalidade IDENTITY
na versão 12.1, é natural se perguntar como ela passou sem ela antes, por que a implementou agora e por que o SQL Server precisava dela desde o início (desde suas origens Sybase SQL Server).
O motivo é que o Oracle sempre teve um recurso de chave de identidade: a pseudocoluna ROWID
, tendo um tipo de dados ROWID
ou UROWID
. Esse valor não é numérico ( ROWID
e UROWID
são tipos de dados proprietários da Oracle) e identifica exclusivamente um registro de dados.
Ao contrário do IDENTITY
do SQL Server, o ROWID
do Oracle não pode ser facilmente manipulado (pode ser consultado, mas não inserido ou modificado), e é criado em segundo plano para cada linha em cada tabela do Oracle. Além disso, a maneira mais eficiente de acessar qualquer linha de dados em um banco de dados Oracle é por seu ROWID
, portanto, é usado como uma técnica de otimização de desempenho. Por fim, ele define a ordem de classificação de saída de consulta padrão, pois indexa efetivamente o armazenamento de baixo nível de dados de linha.
Se o ROWID
da Oracle é tão importante, como o SQL Server sobreviveu a todos esses anos e lançamentos sem ele? Usando colunas IDENTITY
como chaves primárias (substitutas).
É importante observar a diferença na implementação da estrutura de índice entre Oracle e SQL Server.
No SQL Server, o primeiro índice — a chave primária, na maioria das vezes — é agrupado; isso significa que, mais comumente, os dados no arquivo de dados primários são ordenados por essa chave. No lado do Oracle, o equivalente a um índice clusterizado é uma tabela organizada por índice. Essa é uma construção opcional no Oracle que é usada esporadicamente, apenas quando necessário — para tabelas de pesquisa somente leitura, por exemplo.
Todos os padrões de design no Oracle baseados no uso de ROWID
(como desduplicação de dados) devem ser implementados com base nas colunas IDENTITY
ao migrar para o SQL Server.
Embora a migração do uso de IDENTITY
no SQL Server para o uso de IDENTITY
no Oracle possa produzir um código funcionalmente correto, não é o ideal, porque no lado do Oracle, o ROWID
terá um desempenho muito mais eficiente.
O mesmo é verdadeiro ao fazer uma conversão simples de sintaxe SQL para mover sequências Oracle para o SQL Server: O código será executado, mas usar IDENTITY
é a opção mais preferida em termos de simplicidade de código e desempenho.

Índices filtrados no Microsoft SQL Server
Anos atrás, o Microsoft SQL Server 2008 introduziu uma série de recursos significativos que o transformaram em um banco de dados corporativo de primeira linha. Um que salvou meu dia mais de uma vez foi índices filtrados.
Um índice filtrado é um índice não agrupado (ou seja, um que existe como seu próprio arquivo de dados) que possui uma cláusula WHERE
. Isso significa que o arquivo de índice contém apenas registros de dados relevantes para a cláusula. Para aproveitar ao máximo os índices filtrados, ele também deve ter uma cláusula INCLUDE
que liste todas as colunas necessárias ao retornar um conjunto de dados. Quando sua consulta é otimizada para usar um índice filtrado específico que inclui todos os pontos de dados necessários, o mecanismo de banco de dados só precisa acessar um arquivo de índice (pequeno) sem nem mesmo examinar o arquivo de dados da tabela primária.
Isso foi particularmente valioso para mim alguns anos atrás, ao trabalhar com uma tabela de terabyte. O cliente em questão frequentemente precisava acessar apenas uma fração de um por cento dos registros ativos a qualquer momento. A implementação inicial desse acesso (acionada por ações de UI do usuário final) não foi apenas dolorosamente lenta — era simplesmente inutilizável. Quando adicionei um índice filtrado com os INCLUDE
s necessários, tornou-se uma pesquisa de menos de um milissegundo. O tempo que gastei nessa tarefa de otimização foi de apenas uma hora.
Claro, os índices filtrados têm algumas limitações. Eles não podem incluir colunas LOB, há limites sobre quais condições as cláusulas WHERE
que os próprios índices podem incluir e eles adicionam ao espaço de armazenamento de um banco de dados. Mas, desde que um caso de uso se encaixe nesses parâmetros, as compensações de armazenamento geralmente são bem menores em comparação com o aumento significativo de desempenho que os índices filtrados podem fornecer.
E os índices filtrados no banco de dados Oracle?
Mais tarde, encontrei-me em uma grande equipe em uma empresa da Fortune 500 como desenvolvedor/DBA em um projeto de migração do SQL Server para Oracle. O código em torno do banco de dados de origem — SQL Server 2008 — foi mal implementado, com desempenho fraco que tornou a conversão imperativa: o trabalho diário de sincronização de back-end estava sendo executado por mais de 23 horas. Ele não tinha índices filtrados, mas no novo sistema Oracle 11g, vi vários casos em que índices filtrados seriam muito benéficos. Mas o Oracle 11g não tem índices filtrados!
Os índices filtrados também não são implementados no Oracle 18c mais recente.
Mas nossa tarefa como profissionais técnicos é fazer o melhor uso do que temos. Então eu implementei o equivalente de índices filtrados em meu sistema Oracle 11g (e a mesma técnica que usei posteriormente em 12c). A ideia é baseada em como o Oracle trata NULL
s, e pode ser usado em qualquer versão do Oracle.
O Oracle não trata um valor NULL
da mesma forma que os dados regulares. Um NULL
no Oracle não é nada — não existe. Como resultado, se você definir sua coluna indexada como NULLABLE
e estiver pesquisando por valores não NULL
, seu arquivo de dados de índice conterá apenas registros de interesse. Como uma definição de índice Oracle não tem cláusula INCLUDE
, você precisará criar um índice composto com todas as colunas que precisam ser incluídas em um conjunto de resultados. (Esta técnica tem alguma sobrecarga em comparação com a cláusula INCLUDE
do SQL Server, mas é razoavelmente insignificante.)
Essa implementação de solução alternativa adiciona uma limitação: a coluna de índice à esquerda deve permitir NULL
s e, portanto, não pode ser a chave primária da tabela. No entanto, pode ser uma coluna derivada ou calculada criada especificamente para oferecer suporte a esse método de otimização de desempenho. De certa forma, a coluna inicial do índice é logicamente binária: valores não NULL
para dados incluídos em sua pesquisa e NULL
para quaisquer dados que deveriam ser “invisíveis”.
A outra opção possível na migração da lógica de índice filtrado do SQL Server para o Oracle é implementar um índice (ou a tabela completa) como particionado. Nesse caso, apenas a partição de índice relevante será acessada pelo mecanismo de banco de dados - desde que as consultas sejam implementadas corretamente usando a condição de particionamento exata em suas cláusulas WHERE
.
Isso funcionará bem, mesmo em escala, em dados relativamente estáticos, mas pode colocar uma alta carga de manutenção em uma equipe de DBA se aplicado a dados que mudam com frequência. Um exemplo seria ao otimizar o acesso aos dados atuais em um aplicativo centrado no tempo: a equipe de DBA precisará redefinir as partições diariamente. Embora essa redefinição possa ser roteirizada em um trabalho de manutenção noturna, ela torna seu sistema mais complexo e introduz novos pontos de falha sistêmica em potencial.
Portanto, é preciso ser muito específico e cuidadoso sempre que a lógica de índice filtrada do SQL Server precisar ser migrada para o Oracle.
Como lidar com conversões
Com uma migração de Oracle para SQL Server, procure oportunidades de otimização usando índices filtrados. Você não verá índices filtrados no Oracle, mas poderá ver índices que incluem valores NULL
. Não os copie como estão: pode ser o melhor lugar onde você pode obter um aumento de desempenho e melhoria de design em sua conversão.
Para migrações do SQL Server para Oracle, se você vir índices filtrados, procure como evitar um gargalo de desempenho em seu código Oracle correspondente. Veja como você pode reprojetar o fluxo de dados para compensar o aumento de desempenho ausente que os índices filtrados forneceram na implementação de origem.
Desafios de migração do SQL Server para Oracle / Oracle para SQL Server desmistificados
Para projetos de migração entre Oracle e SQL Server em qualquer direção, é importante ter uma compreensão mais profunda da mecânica envolvida. Quando as versões atuais dos respectivos bancos de dados (Oracle 18c e Microsoft SQL Server 2017*) contêm equivalentes léxicos da funcionalidade um do outro, por exemplo, em sequências e identidade, pode parecer uma vitória fácil. Mas copiar um bom design em um RDBMS diretamente para o outro pode resultar em um código desnecessariamente complicado e com baixo desempenho.
Na próxima e última parte desta série, abordo a consistência de leitura e o uso de ferramentas de migração. Fique ligado!
* O SQL Server 2019 (ou “15.x”) não foi lançado há tempo suficiente para ampla adoção corporativa.