Artigo SQL Magazine 68 - Desvendando o Oracle Data Integrator

Uso da ferramenta Oracle Data Integrator (ODI) para a construção de processos ETL (Extract, Transform, Load). Neste artigo, utilizaremos o ODI para integrar dados de diferentes origens (SGBD Oracle, Firebird e arquivo texto) para uma base de destino Oracle.

De que trata o artigo:

Uso da ferramenta Oracle Data Integrator (ODI) para a construção de processos ETL (Extract, Transform, Load). Neste artigo, utilizaremos o ODI para integrar dados de diferentes origens (SGBD Oracle, Firebird e arquivo texto) para uma base de destino Oracle.

Para que serve:

O ODI nos permite transformar o trabalho, muitas vezes maçante, da construção de processos ETLs, em interfaces e fluxos de fácil desenvolvimento, manutenção e visualização.

Em que situação o tema é útil:

Além de padronizar e otimizar processos de ETL, o ODI é capaz de fazer a integração de diferentes tecnologias e bancos de dados em um único lugar, facilitando o trabalho de qualquer projeto que necessite fazer integração de dados.

Desvendando o Oracle Data Integrator – Parte II - Resumo DevMan:

Por ser uma ferramenta visual, o ODI proporciona um ambiente de fácil desenvolvimento e manutenção. Os diagramas ETL das interfaces do ODI são de fácil entendimento, onde até pessoas sem um grande conhecimento técnico entendem o processo ETL que será efetuado. Os Módulos de Conhecimento (KMs) trazem uma padronização e facilidade de manutenção de código incrível.

Fora o ambiente de desenvolvimento, o ODI traz um ambiente completo de monitoramento de execuções (Módulo Operator) dos processos ETL, onde é possível ver todos os passos gerados pelo processo, assim como linhas inseridas, erros, tempo de execução, etc.

Para retomarmos a estrutura apresentada no artigo publicado na SQL Magazine 65, vamos relembrar de que maneira está estruturada e armazenada as tabelas envolvidas no processo de ETL. Como explicado, embora nosso modelo esteja em um DER único, nossas origens estão armazenadas em estruturas diferentes: as tabelas Cliente, TipoCliente, Venda e Vendedor estão alocadas no banco de dados ORACLE; as tabelas Grupo, Item e ItVenda estão no FIREBIRD; e ainda vamos utilizar uma fonte de dados oriunda de arquivo texto.

Para facilitar o entendimento e a leitura dos tópicos apresentados a seguir, vamos disponibilizar no contexto da estrutura relacional apresentada no primeiro artigo, todas as DDL´s e DML’s envolvidas nos processos descritos. Estes scripts podem ser obtidos no site da revista SQL Magazine.

DML (Linguagem de Manipulação de Dados):A DML é um subconjunto da linguagem usada para selecionar, inserir, atualizar e apagar dados.

  • SELECT: é o mais usado do DML, comanda e permite ao usuário especificar uma query como uma descrição do resultado desejado.
  • INSERT: é usada para inserir um registro (formalmente uma tupla) a uma tabela existente;
  • UPDATE: para mudar os valores de dados em uma ou mais linhas da tabela existente;
  • DELETE: permite remover linhas existentes de uma tabela.

Sequence:No Oracle é possível gerar de forma automática uma seqüência de números, usando o comando sequence. Isto pode ser bastante útil quando se pretende criar um número único para uma chave primária.

Iniciando o desenvolvimento

Depois de configurada todas as Topologias (passos apresentados na primeira parte do artigo), vamos iniciar o desenvolvimento no módulo Designer. A primeira tarefa que temos é criar um novo projeto. Na aba Projetos do Módulo Designer devemos clicar com o botão direito e escolher a opção “Inserir Projeto”. Vamos nomear nosso projeto como “PROJETO_ETL” conforme Figura 1.

Figura 1. Inserindo Projeto de ETL.

Ainda na Figura 1 vamos explorar alguns conceitos importantes. Na “Primeira Pasta” localizam-se os nossos objetos criados no ODI que são disponibilizados em estruturas de pastas para uma melhor organização. Porém, uma pasta sempre contém um conjunto de três tipos de objetos: Pacotes, Interfaces e Procedimentos.

Dentro da hierarquia do “PROJETO_ETL” ainda temos:

Para melhorar o entendimento vamos detalhar cada tipo de Módulo de Conhecimento (KM):

Nesta fase de nosso projeto ainda não temos nenhum KM. A cada novo projeto é fundamental a escolha de quais KMs iremos utilizar. Para o nosso projeto vamos importar os KMs necessários, que são dois:

No Módulo Designer, acessamos a aba “Projetos” e clicamos com o botão direito sobre a opção “Importar” e escolhemos a opção “Importar Knowledge Modules...”. Devemos então informar o diretório onde se encontram os KMs a serem importados. Originalmente os KMs que fazem parte da instalação do ODI estão na pasta “oracledi\oracledi\impexp”.

Várias opções serão apresentadas e devemos escolher as que se encaixam ao Projeto.

Os KMs que vamos utilizar no nosso projeto são:

Quando os KMs já estiverem importados podemos ter uma definição do que cada um faz, bastando clicar duas vezes sobre o mesmo, surgindo assim uma tela com a descrição e a funcionalidade do mesmo.

Para este processo de ETL não importamos todos os KMs, pois isso dificultaria a seleção dos mesmos no momento do desenvolvimento devido à grande quantidade de KMs existentes. Portanto, é uma boa prática importar para o seu projeto apenas os KMs que serão realmente utilizados, a fim de trabalhar com um ambiente mais “limpo” e com menos chances de selecionar um KM errado. Em relação aos KMs importados para o nosso projeto, suas funcionalidades ficarão mais claras no decorrer do Projeto, mais precisamente no momento do desenvolvimento das Interfaces.

Construindo a Estrutura do Projeto – Modelos de Dados

Partimos para a definição de nosso Modelo de Dados, e neste ponto o entendimento de dois conceitos são importantes: Modelo de Dados (Data Models) e o Armazenamento de Dados (Data Stores). Um Modelo de Dados pode conter N armazenamentos de dados (tabelas efetivas do banco de dados). É utilizado para agrupar tabelas de uma determinada tecnologia de um determinado Esquema Lógico. Em nosso Projeto teremos quatro Modelos de Dados, um para cada finalidade: Origem Oracle, Origem Firebird, Origem File e Destino Oracle. Dentro de cada modelo estarão os nossos armazenamentos de dados, ou seja, nossas tabelas do banco de dados.

Portanto, dentro do Módulo Designer, mais precisamente na aba Modelos, vamos criar pastas para melhor organização. Vamos inserir duas pastas de modelos: uma chamada “Destinos” e outra “Origens”.

Agora vamos inserir as pastas de modelos para ambas. Para isso, basta clicar com o botão direito sobre a pasta Destinos e selecionar a opção “Inserir Pasta de Modelos”. Vamos inserir a pasta “ORACLE”, onde ficarão as tabelas de destino da tecnologia ORACLE, e repetimos a tarefa para as Origens, criando três pastas: “FILE”, “FIREBIRD” e “ORACLE”, onde ficarão as tabelas de origem das suas respectivas tecnologias.

Inserindo o Modelo de Dados Oracle – Origem

Vamos criar nosso Modelo da Origem ORACLE. Para esta tarefa devemos clicar com o botão direito sobre a Pasta de Modelo ORACLE que acabamos de criar e escolher a opção “Inserir Modelo”.

Na janela que se abre devemos inserir o nome para o nosso modelo, selecionar a tecnologia (ORACLE) e a qual Esquema Lógico (ORACLE_ORIGEM) o modelo irá se referenciar. O nome de nosso Modelo é auto-explicativo (MODELO_ORACLE_ORIGEM).

Ainda nas configurações do Modelo vamos acessar a aba “Reverter”, pois devemos setar o Contexto que iremos utilizar para “importar” as nossas tabelas. Em nosso Projeto o Contexto selecionado é o “Desenvolvimento”. Nesta aba também devemos selecionar quais tipos de objetos queremos que a reversa importe para o ODI. Para o nosso caso selecionamos apenas Tabelas, pois queremos reverter apenas as tabelas criadas nos scripts (que se encontram no site da SQL Magazine). Nesta aba de configuração poderíamos também aplicar alguma máscara de filtro para que no momento da reversa o ODI selecionasse apenas os objetos que se adequassem a esta determinada máscara.

A próxima aba de configuração é a “Reversão Seletiva” (Figura 2). Nesta aba devemos escolher, das tabelas que passaram no filtro anterior, quais tabelas importar para o ODI. Para o nosso projeto iremos importar as quatro tabelas que estão alocadas no banco de dados. Após selecionar as tabelas podemos clicar na opção “Aplicar”, e após em “Reverter”.

Figura 2. Executando a Reversa do Modelo de Origem.

Uma mensagem de confirmação será exibida: “Deseja fazer engenharia reversa neste modelo antes de fechar esta janela?” Se anteriormente já clicamos na opção “Reverter” podemos clicar em “Não” nesta confirmação. Depois de “revertido”, teremos as tabelas da nossa origem ORACLE no ODI.

Inserindo o Modelo de Dados Firebird – Origem

Devemos agora inserir o Modelo de Dados também para o Firebird. Faremos o mesmo processo detalhado anteriormente apenas alterando a Tecnologia escolhida. Selecionamos a Tecnologia Interbase que foi a selecionada para utilização com o Firebird no momento da criação da Topologia.

Conforme a Figura 3, selecionamos a tecnologia Interbase e o Esquema Lógico FIREBIRD_ORIGEM.

Figura 3. Criando modelo de Origem do Firebird.

Após selecionar o contexto e quais objetos queremos importar na aba Reverter (novamente selecionamos Tabelas), e quais as tabelas que importaremos na aba Reversão Seletiva (tabelas criadas no script que se encontra no site da SQL Magazine), podemos clicar na opção “Aplicar” e após em “Reverter”. Se o procedimento for correto, as tabelas da Origem Firebird serão importadas.

Inserindo o Modelo de Dados File – Origem

Terminada a inclusão dos Modelos de Dados ORACLE e Firebird vamos partir para a inclusão do Modelo de Dados do tipo FILE. Para esta tecnologia existem algumas particularidades que devem ser observadas. Vamos proceder com a criação do modelo de forma normal seguindo os padrões da inclusão da Tecnologia ORACLE. Nomeamos o modelo para MODELO_FILE_ORIGEM e selecionamos a Tecnologia FILE. Também associamos neste ponto o Esquema Lógico FILE_ORIGEM. Vamos à aba Reverter, selecionando o contexto “Desenvolvimento”. A única particularidade está no momento de salvar o modelo: devemos salvá-lo sem revertê-lo.

Podemos notar que o ODI não apresentou nenhuma mensagem de aviso ou confirmação em relação à reversa no momento que nós criamos o modelo. Isso acontece porque a Tecnologia FILE não segue necessariamente um padrão. Podemos ter arquivos com delimitações por caracteres, como “;” (ponto e vírgula) ou então “|” (pipe) como podemos ter arquivos que não são delimitados mas sim fixos por um determinado valor em cada coluna. Todos estes padrões se encaixam na Tecnologia FILE. Devido a particularidades de cada arquivo devemos fazer a reversa de cada arquivo de forma individual.

Para isso devemos estar no Repositório de Trabalho do ODI, e clicar com o botão direito no “MODELO_FILE_ORIGEM” que se encontra dentro da pasta FILE. Devemos escolher a opção “Inserir Armazenamento de Dados”.

Na janela que será exibida, na aba “Definição”, devemos colocar um nome para o modelo de dados e devemos escolher o arquivo correspondente que queremos reverter. Neste caso o arquivo é do tipo TXT (dtempo.txt) e armazena as informações referentes à dimensão tempo de nosso Data Warehouse. Depois de feita a seleção do arquivo, vamos para a aba “Arquivos” (Figura 4), onde devemos informar se o arquivo possui ou não delimitação. No nosso caso, escolhemos que ele é “Delimitado”. Neste ponto informamos que o caractere separador de campos do arquivo dtempo.txt é o “;” (ponto e vírgula). Também nesta estrutura de configuração podemos informar se o arquivo possui cabeçalho e de quantas linhas o mesmo é formado. Para este caso informamos o valor 0 (zero). Se algum valor fosse informado, a quantidade de linhas informada seria retirada do início do arquivo e seria desprezada.

Outra opção que precisamos definir diz respeito ao “Separador de Registros”. Podemos selecionar se o arquivo tem separador do tipo:

Figura 4. Criando o armazenamento de dados da origem TXT.

Estes padrões de separadores de registros se referem às possíveis quebras de linhas do arquivo. Também devemos configurar o delimitador de texto que neste caso é ‘ (aspas simples), ou seja, as strings do arquivo texto são envoltos por aspas simples. Com esta configuração o ODI irá considerar apenas o conteúdo “interno” da string ignorando as aspas.

Neste ponto também podemos indicar qual separador decimal os nossos valores estão utilizando, o que não se aplica neste caso.

Finalizando o processo de configuração devemos clicar na aba “Colunas” e selecionar a opção reverter. Neste momento o ODI busca as informações da aba “arquivos” e separa em colunas automaticamente (Figura 5).

Figura 5. Coluna do modelo de origem TXT.

Por padrão as colunas ficam com nomes C1, C2, C..., mas podem ser renomeadas conforme necessidade e\ou organização.

Inserindo o Modelo de Dados Oracle – Destino

Vamos agora proceder com a criação do modelo de destino seguindo os padrões da inclusão da tecnologia Oracle para Origem. Nomeamos o modelo como MODELO_ORACLE_DESTINO conforme Figura 6.

Figura 6. Criação do Modelo de destino Oracle.

Devemos reverter as tabelas repetindo os mesmos passos do modelo de dados Oracle da origem. Para isso, na aba Definição devemos selecionar a tecnologia Oracle e o esquema lógico ORACLE_DESTINO. Na aba Reverter selecionamos o contexto de Desenvolvimento e o tipo de armazenamento de dados a ser revertido (Tabela), e na aba Reversão Seletiva escolhemos as tabelas contidas no script disponível no site da SQL Magazine. Depois deste passo estamos prontos para iniciar o desenvolvimento das interfaces.

Iniciando o Desenvolvimento das Interfaces

Neste ponto iniciamos efetivamente o desenvolvimento ETL. Vamos desenvolver as interfaces, procedimentos, variáveis e pacotes, que serão os objetos utilizados para a realização do ETL.

Desenvolvimento da Interface – Carga Destino DIM_CLIENTE

Para iniciarmos o desenvolvimento das interfaces vamos alternar da aba Modelos para a aba Projetos no Módulo Designer. Nesta aba vamos alterar o nome da “Primeira Pasta” para “DW”. Esta alteração pode ser feita dando duplo clique sobre a estrutura.

Vamos iniciar carregando as dimensões do DW. A primeira interface a ser desenvolvida deverá fazer a carga de dados para a Dimensão Cliente. Ainda na aba Projetos devemos expandir a pasta DW e clicar com o botão direito sobre Interfaces selecionando a opção “Inserir Interface”, conforme Figura 7.

Figura 7. Inserindo uma nova interface.

Vamos desenvolver a Interface para contemplar o ETL da Dimensão Cliente e, portanto, nomeamos a Interface como CLIENTES_IN. Neste passo também devemos selecionar o contexto de otimização, que serve para o ODI montar o fluxo de execução (Figura 8).

Figura 8. Criando a interface de clientes.

Para melhorar a explicação sobre o contexto de otimização, vamos imaginar o seguinte exemplo: temos em desenvolvimento dois esquemas que apontam para uma mesma instancia de banco de dados. Para o ODI, como os dois esquemas estão no mesmo banco não seria necessária a utilização de um LKM (o LKM busca os dados de data servers diferentes), pois o IKM (módulo de integração) conseguiria fazer sozinho a integração de dados, otimizando assim o código, pois diminuiria os “passos” do mesmo. Porém, se estes mesmos esquemas, em um contexto de Produção, estiverem em servidores fisicamente separados, o ODI necessitaria utilizar um LKM, pois a sua origem está fisicamente separada do destino.

Se a interface fosse construída com o contexto de otimização menos “fragmentado” (como o de desenvolvimento neste caso) teríamos um problema ao rodar esta interface em produção, pois o código gerado não contemplaria um LKM.

Portanto, ao selecionar um contexto de otimização, devemos escolher sempre o contexto mais “fragmentado”, pois o ODI irá se basear neste contexto para montar o fluxo do ETL. No nosso caso, como temos apenas um contexto, pode-se manter o contexto de desenvolvimento. Outra opção que podemos selecionar nesta etapa (Figura 8) esta relacionada à área de Stagging, que pode ser diferente do destino. Por padrão, a área de Stagging é sempre no destino, ou seja, os objetos temporários necessários ao processo de ETL serão criados no Esquema de Trabalho do destino setado anteriormente, no momento da criação da topologia (ESQUEMA_TMP do banco ORACLE).

Neste ponto poderíamos selecionar qualquer esquema para ser a Stagging, mas vamos mantê-lo no Esquema de Trabalho do destino. Após inserir esta nova Interface devemos acessar a aba “Diagrama”. Nesta estrutura serão armazenados todos os relacionamentos, regras e mapeamentos de origem e destino que deverão ser configurados. No lado direito (Figura 9) temos a tabela de destino, no esquerdo, teremos as tabelas de origem e seus relacionamentos.

Figura 9. Diagrama de uma Interface.

Na estrutura do Diagrama vamos montar a regra de ETL para o nosso destino. Primeiro devemos clicar na aba “Modelos” e selecionar a estrutura DESTINOS/ORACLE/MODELO_ORACLE_DESTINO. Após localizar a estrutura basta clicar e arrastar a tabela DIM_CLIENTE para dentro da estrutura de armazenamento DESTINO, como pode ser visto na Figura 10.

Figura 10. Adicionando as tabelas de Origem.

Posteriormente devemos selecionar e arrastar a ORIGEM para o lado esquerdo do Diagrama. Neste momento o ODI pergunta se desejamos fazer o mapeamento automático dos campos. Como na nossa estrutura a nomenclatura das colunas são iguais, o mapeamento iria funcionar sem problemas. Na prática de desenvolvimento de um projeto, o mapeamento automático não é recomendado. Na grande maioria dos casos, as nomenclaturas de origem e destino são diferentes e\ou existirá alguma regra de transformação. Desta forma o ODI pode mapear campos para os locais errados, gerando re-trabalho para mapeá-los novamente.

Portanto, selecione “Não” e vamos mapear manualmente. Porém, antes disso, temos que fazer um join entre tabelas de origem com o objetivo de popular a tabela DIM_CLIENTE. A DIM_CLIENTE recebe tanto as informações dos clientes quanto do seu tipo. Para isso, clique e arraste TIPOCLI para o diagrama. Podemos ver pela Figura 11 que o ODI identificou as colunas que fazem relacionamento entre as tabelas e já colocou o join automaticamente.

Figura 11. Montando os Joins entre as tabelas de Origem.

Se o processo de montagem dos joins não acontecesse de forma automática teríamos que clicar sobre a primeira coluna do relacionamento, arrastar e soltar em cima da segunda coluna do relacionamento. Este é o processo manual quando o mapeamento automatizado não acontece.

Podemos notar ao clicar no join (Figura 12) que várias opções são apresentadas (todas são auto-explicativas), como por exemplo, se o join vai ser um inner join ou um left outer join. Clicando nos diferentes tipos de joins, o ODI nos diz o que irá acontecer em cada caso.

Figura 12. Opções de Join para montagem da interface de carga.

No caso apresentado para a construção da DIM_CLIENTE utilizamos um inner join. Esta tarefa avisa que retornará “Todas as linhas emparelhadas pela condição de união entre CLIENTE e TIPOCLI”.

IMPORTANTE: Neste ponto temos a opção de executar este join na origem ou na área de teste (stagging). Se for na stagging, o ODI trará as duas tabelas inteiras para o esquema de trabalho e depois fará o join entre elas. Se a opção é na origem, o ODI fará o join na origem e trará apenas o resultado daquele join para o esquema de trabalho.

Esta escolha depende de cada caso. No nosso exemplo é mais eficiente resolver o join na origem e trazer resolvido para o destino, pois isso resultará em trazer apenas os registros que obedeceram à regra do join, tornando assim o volume de dados trafegados de uma ponta a outra menor.

Para mapear um campo no ODI o processo é relativamente simples. Deve-se clicar no campo de destino que se deseja mapear, clicar no campo de origem a ser mapeado, arrastar e soltar na área branca “Implementação”, que fica na parte de baixo do diagrama. O resultado pode ser visto na Figura 13.

Figura 13. Mapeando uma coluna no ODI.

Faltou apenas o mapeamento do campo ID_CLIENTE e neste passo faremos algo diferente. Todas as tabelas de destino têm um ID próprio e único que é a PK da tabela. Estas PKs devem ser populadas com um número único de uma sequence chamada SEQ_DESTINOS, que se encontra criada no banco de destino.

Agora, devemos clicar sobre a coluna ID_CLIENTE e clicar diretamente no ícone do “lápis” para abrir o editor de expressões (Figura 14).

Figura 14. Editor de expressões.

O editor de expressões auxilia a montar as expressões que estarão mapeadas nas colunas. Neste caso, mapeamos uma sequence na coluna ID_CLIENTE. Para isso, prefixamos o esquema onde a mesma se encontra no banco, por exemplo, ESQUEMA_DESTINO.SEQ_DESTINOS.

O procedimento de manter prefixado (ESQUEMA.OBJETO) o esquema na Interface desenvolvida não é recomendado para grandes projetos. Exemplo: o esquema principal está nomeado como ESQUEMA_DESTINO em desenvolvimento, mas em outro ambiente (produção) o esquema pode variar de nome. Esta alteração faria com que a Interface não executasse de maneira correta. A solução deste problema seria utilizar uma função própria do ODI que retorna o nome do esquema em que a interface esta sendo executada. Esta função pode ser encontrada dentro do Editor de Expressões (Figura 15), mais precisamente em Funções OdiRef. O ODI possui várias funções muito úteis. A lista completa destas funções podem ser encontradas no manual de referência da ferramenta.

Para este exemplo em vez de ter uma sequence com o esquema prefixado (ESQUEMA_DESTINO.SEQ_DESTINOS) substituiríamos pela função denominada getShemaName, Figura 15.

Figura 15. Editor de Expressões.

Após escrever o comando a ser mapeado confirmamos com um “OK” na janela. Voltamos para a montagem da Interface. Notamos na Figura 16 que, ao lado do nome das colunas, encontram-se pequenos ícones, como uma pequena janela, um martelo (que ainda não se encontra na tela), um alvo e uma chave.

Figura 16. Mapeamento completo para DIM_CLIENTE.

Cada símbolo possui um significado:

Podemos trocar o local que o campo será executado (resolvido) clicando na coluna que desejamos modificar e em seguida na opção “Executar em:”, selecionando o local escolhido. No caso da sequence, iremos especificar que irá executar no ambiente de destino. Esta troca de diretório tem um motivo: a sequence não deve ser avaliada durante o processo de ETL e deve ser executada somente no momento da inserção do novo registro no destino. Se não for estruturada desta maneira causará um erro na sua execução.

Outra tarefa necessária é a alteração da chave da tabela Cliente. Esta tabela tem como PK o campo ID_CLIENTE e é populado por uma sequence. Isso significa que o valor da PK sempre muda e novos registros seriam inseridos na tabela sempre que a Interface fosse executada. Se executássemos dez vezes a carga, os clientes estariam dez vezes duplicados na tabela de destino.

O correto para a tabela Cliente é existir apenas um código por cliente, ou seja, precisamos que a coluna CDCLI seja a chave natural (NK – Natural Key). Para o ODI levar em consideração a coluna CDCLI como chave e não a atual PK ID_CLIENTE devemos proceder com a alteração conforme a Figura 17. Ao clicar sobre a tabela de destino DIM_CLIENTE percebemos que na opção “Atualizar Chave” está selecionado “DIM_CLIENTE_PK” que representa a PK da tabela no ODI.

Figura 17. Chave de DIM_CLIENTE.

Trocamos o “Atualizar Chave” para a opção “sem definição” e agora temos a liberdade de selecionar a chave que necessitamos. Selecionamos então a coluna CDCLI e clicamos em “chave”, conforme Figura 18.

Figura 18. Mapeamento de DIM_CLIENTE.

Com isso a chave para o ODI passa a ser CDCLI. Clicando sobre as colunas, podemos notar na estrutura “Atualizar”, check-boxes de “Inserir”, “Atualizar”, “UD1”, “UD2”, etc. (Figura 19). Estes checks funcionam para configurar se o campo será inserido no destino, se ele será atualizado no destino ou se ele executará alguma das funções definidas pelo usuário (UD – User Defined). No nosso caso, todos os campos por padrão estão marcados como “Inserir” e “Atualizar”. Porém, no caso da coluna ID_CLIENTE devemos desmarcar a opção “Atualizar” (Figura 19), pois a sequence não pode participar do passo de update gerado pelo KM sob o risco de erros serem gerados na execução. Este processo ficará mais claro no momento da execução da interface que será explicado a seguir.

Figura 19. Configurando o comportamento dos campos.

Concluída as configurações vamos para a aba “Fluxo”. Na tela de Fluxo (Figura 20) é representada a forma como a ferramenta irá fazer a execução da Interface.

Figura 20. Fluxo de trabalho do ODI.

Para este caso o ODI demonstra apenas um único exemplo com a utilização do IKM, que por si só irá resolver todo processo de ETL. Esta estrutura é única devido às tabelas que estamos utilizando como origem e as tabelas que queremos popular (tabelas de destino) se encontrarem em um mesmo Data Server (uma mesma Origem) configurado na topologia.

Se esta estrutura estivesse em Data Servers diferentes, a ferramenta nos mostraria duas estruturas distintas, uma com a composição de um LKM responsável pela carga dos dados para as áreas de stage e outra com o IKM que realizaria os demais processos de ETL. Este caso será explorado no momento da construção das Interfaces que carregam os dados oriundos dos arquivos do tipo texto e do banco de dados Firebird.

Ao clicar sobre a caixa denominada “Alvo-Área de Teste” (Figura 20) podemos observar que o KM utilizado por padrão é o IKM (Oracle incremental Update). Resumidamente este KM faz cargas incrementais, ou seja, ele verifica a chave definida na interface (CDCLI neste caso) e se esta chave ainda não existe no destino o processo faz a inserção da mesma de forma automática. Se esta chave já existe o processo apenas faz o Update nas colunas selecionadas com a opção “Atualizar” (Figura 19).

Podemos notar também que o KM vem com várias opções de valores padrões. Ao clicar sobre cada opção, ao lado, apresenta-se a sua descrição. Para este trabalho iremos modificar apenas a opção “Flow Control” que devemos mudar para opção “não” (Figura 20). Quando a opção descrita estiver selecionada como “Sim” o ODI irá invocar o CKM (Validações – Ver explicação sobre CKM neste artigo) selecionado e fará a verificação dos dados durante o processo de ETL. Como não criamos nenhuma validação para esta tabela, podemos retirar a opção de “Flow Control” desta interface.

Para realizar a execução da interface basta clicar sobre o botão “Executar” no canto inferior direito da interface (Figura 21). Neste momento será apresentada uma tela questionando em qual contexto executar, neste caso o contexto de Desenvolvimento; qual o agente, vamos executar no agente local; e o nível de registro, que indica o grau de informações que deve ser gerado no log do ODI, que podemos deixar o valor padrão 5.

Figura 21. Execução de uma Interface.

Durante a execução da Interface podemos acessar a “Lista de sessões” do módulo Operator e acompanhar o processo de execução das cargas (Figura 22).

Verificando a execução (Figura 22), podemos observar os passos criados pelos KMs do ODI. Reparamos que a primeira palavra escrita é “Integração”. Isto significa que todos os passos gerados por esta Interface foram de um IKM.

Para carregar a tabela DIM_CLIENTES, a ferramenta gerou onze passos distintos. Os ícones em verde indicam comandos executados com sucesso. Ícones em amarelo indicam que o comando falhou, porém a execução continua normalmente. Ícones em vermelho significam erros que interrompem a execução da carga, que não foi o caso.

No exemplo da Figura 22 percebe-se que o passo indicou “atenção”. Isto aconteceu porque o ODI tentou dropar uma tabela temporária que ainda não existia no banco. Clicando duas vezes sobre qualquer passo é possível ver o que executou, quanto tempo levou para executar a carga, quantas linhas foram inseridas, entre outros.

Figura 22. Execução da Interface CLIENTES_IN.

Esta Interface (CLIENTES_IN) inseriu sete linhas na tabela de destino. Se esta Interface fosse executada novamente veríamos novamente os mesmos onze passos, mas no processo nenhuma nova linha seria inserida. Como esta Interface é incremental, ela carrega apenas as linhas que ainda não foram carregadas e faz a atualização de linhas quando a mesma não existir.

DICA: Para compreender melhor como funcionam as configurações feitas no ODI, tente marcar a opção “Atualização” no campo ID_CLIENTE que é carregada juntamente com a sequence ou mude o local de execução de “Destino” para “Stagging” e compare os passos de uma execução e outra. No começo parece complicado, mas depois que aprendemos os “pequenos truques” da ferramenta verificamos que o ODI é uma poderosa e flexível ferramenta para processos ETL.

Desenvolvimento da Interface – Carga Destino DIM_PRODUTO

O próximo passo para o projeto é criar a Interface que carrega a tabela DIM_PRODUTO. A tarefa para montagem da carga é a mesma explanada anteriormente. Desta forma, vamos direto para o Diagrama da Interface (Figura 23). Todas as tabelas desta estrutura são provenientes da origem FIREBIRD.

Figura 23. Diagrama de PRODUTOS_IN.

Importante: Devemos efetuar a modificação da coluna ID_PRODUTO para ser executada no banco de destino (Ícone do “Alvo” da coluna ID_PRODUTO na Figura 23). Também devemos desmarcar a opção “Atualizar” para este atributo. Outra modificação que deverá ser efetuada é a troca da chave da tabela (DIM_PRODUTO) para ser CDITEM e CDGRUPO, pois estes dois atributos referenciam a NK (Natural Key - Chave Natural) da tabela. Outro ponto importante é que ao clicar no ícone do “lápis”, o ODI perguntará qual é a tecnologia a ser considerada no editor, pois temos duas tecnologias no diagrama (Firebird e Oracle). Selecionaremos o Oracle pois a sequence está no banco Oracle.

Clicando na estrutura da aba “Fluxo” temos uma novidade: a “caixa” do LKM (Figura 24). Esta estrutura se encontra presente devido à necessidade de carregar dados que se encontram em outro banco de dados (neste caso o Firebird).

Figura 24. Fluxo de PRODUTOS_IN.

Com isso o ODI primeiro extrai estes dados da base de origem repassando os mesmos para a stagging área. Em relação ao IKM, este terá o papel de pegar os dados e inserir nas tabelas de destino.

Para a carga da tabela destino DIM_PRODUTO, vamos utilizar o LKM SQL to Oracle. Já em relação ao IKM selecionamos o IKM Oracle Incremental Update não esquecendo que neste devemos modificar a opção de “Flow Control” para “Não”.

Ao executar esta Interface os resultados podem ser consultados na “lista de sessões” do Operator (veja a Figura 25).

Figura 25. Execução de PRODUTOS_IN.

Notamos na Figura 25 que o número de passos de execuções aumentou para dezessete e que temos descrições das ações como “Carregando” e “Integração”. Os passos com as descrições carregando se referem aos passos gerados pelo LKM e os passos com “Integração” se referem aos passos gerados pelo IKM.

Desenvolvimento da Interface – Carga Destino DIM_VENDEDORES

Para criar a interface de vendedores basta seguir os mesmos passos das interfaces anteriores: selecionamos o nosso destino, a nossa origem, mapeamos os campos, colocamos a execução da sequence no alvo, desmarcamos a opção de “Atualizar” e trocamos a chave para CDVEND (Figura 26).

Figura 26. Mapeamento de VENDEDORES_IN.

Em alguns casos a utilização de um filtro para os dados se torna necessária e pode auxiliar no processo de carga. Para exemplificar a utilização de um filtro na Interface de carga vamos inserir para esta interface, especificamente, um filtro na nossa origem (representada por um funil amarelo no diagrama – Figura 26). Para fazer um filtro, basta clicar no campo que se deseja filtrar, arrastá-lo para o lado e soltar na área livre do diagrama. Após isso, podemos montar a estrutura e escrever o filtro que desejamos fazer. Neste caso colocaremos que o campo PERCCOM deve possuir valor menor a 50 (Figura 27).

Figura 27. Utilizando filtro no ODI.

Esta carga possui somente o IKM, pois se trata do mesmo banco de dados e fará a carga com a estratégia incremental (IKM Oracle Incremental Update). Modificamos a opção do “Flow Control” para “Não” e executamos a interface.

Desenvolvimento da Interface – Carga Destino DIM_TEMPO

Para a carga da dimensão tempo temos uma particularidade. A origem para esta carga é um arquivo texto com uma estrutura simples (Figura 28).

Figura 28. Mapeamento para TEMPO_IN.

Aqui temos uma novidade: no mapeamento da coluna DATA_DIA utilizamos a função TO_DATE do Oracle (Figura 29), pois estamos lendo uma string do arquivo texto e estamos populando um campo do tipo DATE (TO_DATE(DTE.DATA_DIA,'DD/MM/YYYY')). Neste caso não iremos utilizar a sequence do banco e sim a própria sequence existente no arquivo texto.

Figura 29. Mapeamento utilizando procedimento TO_DATE.

Na aba fluxo para este caso teremos um LKM e um IKM. O LKM que iremos utilizar será o LKM File to SQL. Para o IKM utilizaremos o Oracle Incremental, onde devemos setar a opção “Flow Control” igual a “Não”. Executando a interface podemos ver o resultado no Operator, como explicado anteriormente.

Desenvolvimento da Interface – Carga Destino FATO_VENDAS

Esta interface já tem uma lógica mais elaborada (Figura 30): estamos buscando as informações de duas origens: a tabela VENDA que tem sua origem proveniente do banco de dados Oracle e da tabela ITVENDA que vem do banco de dados Firebird. Além dessas origens ainda fazemos joins com as nossas tabelas de Dimensões, pois precisamos buscar os IDs que foram gravados anteriormente nas nossas interfaces. Os joins que são realizados são os seguintes:

Para este caso vamos inserir outro filtro (para reforçar o exemplo de utilização): DIM_TEMPO.TURNO = 'Manhã'. Notamos na Figura 30 que a estrutura DIM_TEMPO possui, assim como explicado anteriormente, um pequeno “funil” amarelo representando que existe um filtro no processo de carga desta estrutura.

Figura 30. Diagrama de FATO_VENDAS_IN.

No fluxo selecionamos o LKM SQL to Oracle para ler as tabelas do banco Firebird e o IKM Oracle Incremental Update para fazer a carga. Marcamos também a opção “Flow Control” no IKM para “Não”. Como padrão, podemos executar a interface e ver o seu resultado no Operator.

Desenvolvimento do Pacote para Carga de Dados

Após executar individualmente cada Interface podemos consultar as tabelas de destino e conferir que todas estão carregadas. Mesmo com a eficiência comprovada para cada carga este não é um modo prático para execução de cargas. Em um grande projeto, por exemplo, estas Interfaces não poderiam ser enviadas para outros ambientes, pois não são estruturas compiladas para execução em outros ambientes. Neste sentido necessitamos criar Pacotes para controlar o fluxo e criar cenários compilados para que a execução em outros ambientes seja garantida.

Para inserir um novo Pacote, no projeto DW, clique com o botão direito sobre a opção “Pacotes” e em seguida selecione “Inserir Pacote”. Na aba “Definição” nomeamos o pacote. É na aba “Diagrama” que será desenvolvido o fluxo do processo de ETL. Nesta mesma tela pode-se encontrar várias funcionalidades (em forma de botões) que podem ser detalhados com o simples “passar” do mouse sobre cada um.

A caixa de ferramentas do ODI contém diversos objetos que podem ser incluídos no fluxo ETL do nosso pacote. Entre eles temos objetos de envio de e-mail, execução de comandos do sistema operacional, processo de espera de eventos (tempo limite ou espera de algum registro em alguma tabela específica), manipulação de arquivos, entre outros. O detalhamento de cada componente pode ser visto no arquivo de ajuda do ODI, que se encontra no menu Ajuda na parte superior da tela.

Para montar o fluxo devemos colocar as interfaces no diagrama do pacote. Para isso, clicamos sobre alguma interface e arrastamos para dentro do diagrama, conforme Figura 31.

Figura 31. Adicionando as Interfaces ao Pacote.

Podemos notar na Figura 31 que a interface CLIENTES_IN possui uma pequena “flecha verde” que indica que ela vai ser o primeiro objeto a ser executado. Para modificar qual objeto será o primeiro a ser executado é possível clicar em cima do objeto escolhido com o botão direito e escolher a opção “Primeira etapa”. Se executássemos o pacote neste momento somente a interface CLIENTES_IN seria executada, pois ainda não criamos o fluxo de execução completo do pacote.

Para criar este fluxo devemos clicar no botão “ok” (Etapa seguinte ao êxito) que contém uma flecha verde, na barra superior. Após este passo deve-se clicar sobre o objeto de origem e arrastar até o objeto de destino, conforme Figura 32. Temos também o botão “ko” (Próxima etapa ao falhar) que contém uma flecha vermelha, que desviará o fluxo se algum erro acontecer. Aplicaremos o fluxo de erro em momentos onde for pertinente.

Figura 32. Criando Fluxo de Execução.

O mesmo procedimento deve ser repetido para o restante das Interfaces (Figura 33). Após isso, executaremos o pacote clicando no botão “Executar” (canto inferior direito).

OBSERVAÇÃO: Para manipular o local dos objetos no pacote, escolha o primeiro botão (o cursor branco – “Escolha livre”) na barra superior.

Figura 33. Fluxo do Pacote.

Observando a execução da Interface no módulo Operator (Figura 34) podemos verificar que agora todas as nossas interfaces estão agrupadas em uma única execução do pacote, evitando a execução individual de cada uma.

Figura 34. Execução do Pacote.

Outra tarefa importante pode ser realizada neste Pacote. Vamos implementar um LOG personalizado para guardar as informações importantes relacionadas a execução deste Pacote. Para isso usaremos a tabela LOG_CARGA que conterá o ID da sessão do ODI correspondente à execução e uma descrição informando se todos os processos da carga executaram com sucesso ou com erro. Para completar esta demanda vamos precisar criar uma Variável e dois novos Procedimentos: um para inserir os dados e outro para retornar o ID da sessão. Para completar esta tarefa precisamos entender melhor o que é uma Variável e um Procedimento no ODI.

Criando Variáveis

Para criar uma Variável devemos acessar o projeto PROJETO_ETL, na aba projetos, clicar com o botão direito sobre a opção “Variáveis” e escolher “Inserir Variável”. Na aba “Definição”, colocamos o nome da variável, escolhemos o seu tipo de dado e a sua Ação (Figura 35).

Figura 35. Criação de Variáveis no ODI.

Para a opção Ação, temos as seguintes opções:

A Ação escolhida neste caso é a “Não persistente”, pois não temos a necessidade de manter histórico para esta tarefa. Na aba “Atualizando” vamos adicionar um comando DDL que retornará o valor para a variável, ou seja, o comando é executado no banco de dados e o resultado é atribuído para a variável. Para este exemplo utilizamos um select simples na tabela “dual” (que retornará apenas um registro) utilizando a função do ODI <%=odiRef.getSession("SESS_NO")%>, que retornará o número da sessão. No combobox “Esquema” escolhemos em qual esquema queremos executar esta DDL, que neste caso é o ORACLE_DESTINO (Figura 36).

Figura 36. Configurando a variável.

Tabela “dual” Oracle: A tabela DUAL é uma pequena tabela no dicionário de dados que o Oracle ou qualquer usuário pode referenciar para garantir um resultado conhecido. Esta tabela possui apenas uma coluna, chamada DUMMY com apenas uma linha, contendo o valor X. A DUAL é criada automaticamente pelo Oracle, sob o esquema SYS, mas pode ser acessada por outros usuários. Sempre que precisamos verificar um resultado conhecido, como a data e hora do servidor ou o valor atual de uma sequence, simplesmente fazemos a consulta referenciando a tabela DUAL. Isto por que toda consulta SQL deve envolver uma tabela, porém, se utilizarmos qualquer tabela “povoada” nesta consulta, teremos uma série de inconvenientes, como estratégia de acesso ou eventual utilização de índices, etc.

O teste para verificar se o procedimento foi realizado com sucesso pode ser feito ao clicar no botão Renovar. Se a Ação da variável é “Historiar” ou “Valor mais recente”, podemos ver o valor da variável na aba Histórico (Figura 37).

Figura 37. Histórico da Variável.

DDL (Linguagem de Definição de Dados):A DDL permite ao usuário definir tabelas novas e elementos associados. A maioria dos bancos de dados de SQL comerciais têm extensões proprietárias no DDL. Os comandos básicos da DDL são poucos:

  • CREATE: cria um objeto (uma Tabela, por exemplo) dentro da base de dados;
  • DROP: apaga um objeto do banco de dados.

Alguns sistemas de banco de dados (Oracle, por exemplo) usam o comando ALTER, que permite ao usuário alterar um objeto, por exemplo, adicionando uma coluna a uma tabela existente. Outros comandos DDL: ALTER TABLE, CREATE INDEX, ALTER INDEX, DROP INDEX, CREATE VIEW, DROP VIEW.

Nosso próximo passo é adicionar a variável no pacote e setarmos a mesma para ser executada como demanda inicial, pois queremos ter o número da sessão para gravar no log antes de começar o processo de ETL. Quando clicamos sobre a variável, podemos observar as suas propriedades, entre elas o “Tipo”, que pode ser setado de várias formas (o ícone no pacote e suas propriedades mudarão conforme o que for setado). As opções de Tipo são:

Para o nosso pacote, escolheremos o tipo Renovar variável, pois queremos que a variável contenha o valor retornado do select da aba “Atualizando”. Isto faz com que tenhamos o valor da sessão do ODI atribuída a nossa variável, com o objetivo de gravarmos posteriormente no log (Figura 38).

Figura 38. Tipos de Variáveis.

Criando Procedimentos

Para criar Procedimentos no ODI devemos acessar a pasta DW, clicar com o botão direito sobre a opção “Procedimentos” e depois em “Inserir Procedimento” (Figura 39).

Figura 39. Inserindo novo procedimento.

Na aba “Definição” devemos apenas colocar o nome do nosso Procedimento. Já na aba “Detalhes”, devemos clicar no primeiro botão “Adicionar” na parte superior. Após este passo será aberta uma janela onde deve ser inserido o comando que queremos que este Procedimento execute. Percebemos aqui o nível de flexibilidade de trabalhar com o ODI. Nesta tela que foi apresentada é possível adicionar qualquer tipo de comando de qualquer tipo de tecnologia suportada pelo ODI, entre elas Oracle, Java, DBase, Hyperion Essbase, Java Script, entre outros.

A lista completa de tecnologias suportadas pode ser vista no combobox “Tecnologia”. Para este exemplo, faremos apenas um simples insert em uma tabela, mas as possibilidades são muito maiores, podendo ter blocos inteiros de PL/SQL com uma lógica muito mais complexa, tudo dependendo da necessidade do projeto.

Portanto, escolhemos a tecnologia Oracle, o esquema ORACLE_DESTINO (onde está a tabela de log) e escrevemos o comando a ser realizado, conforme a Figura 40.

Figura 40. Criando novo Procedimento.

Notamos alguns detalhes diferentes neste procedimento:

Podemos clicar em OK para fechar esta janela (Figura 40). Observe que poderíamos incluir quantos comandos fossem necessários, bastando apenas clicar no botão “Adicionar”. Poderíamos inclusive executar comandos de N tecnologias diferentes em ordem seqüencial.

Nossa próxima tarefa é realizar a inclusão de outro procedimento. Para criar procedimentos no ODI devemos acessar novamente a pasta DW, clicar com o botão direito sobre a opção “Procedimentos” e clicar em “Inserir Procedimento”. Para esta estrutura basta nomeá-la e clicar em OK, pois iremos inserir uma nova Opção para este Procedimento.Opções são parâmetros que são repassados para o Procedimento. Para inserirmos uma Opção clicamos com o botão direito sobre o Procedimento e em seguida “Inserir Opção”.

Será inserida uma Opção para indicar ao Procedimento se desejamos gravar uma mensagem de sucesso ou erro. Uma Opção pode ser de três tipos:

Escolheremos o tipo “Valor” (ver Figura 41).

Figura 41. Criando uma nova Opção.

Vamos abrir novamente o procedimento, agora para criar um comando. Escolhemos neste sentido a tecnologia Oracle, o esquema ORACLE_DESTINO e digitamos o comando conforme a Figura 42. Este comando fará com que a tabela de log seja atualizada com uma mensagem de Erro ou de Sucesso, conforme o parâmetro passado para ele.

Figura 42. Procedimento para gravar detalhes em LOG.

Neste comando temos o <%=odiRef.getOption("STATUS")%> que irá buscar o valor passado para o parâmetro através da Opção que criamos no passo anterior. Clicamos em OK e vamos inserir os Procedimentos no nosso fluxo do pacote.

Na Figura 43 visualizamos o Fluxo de nossa carga.

Figura 43. Fluxo Final do Pacote.

A leitura deste Fluxo pode ser feita desta forma:

1- Comece executando a atualização da variável SESSAO_ODI;

2- Insira um registro na tabela de LOG;

3- Execute as cinco interfaces e grave o status final na tabela do LOG;

4- Se algum procedimento der errado (flechas vermelhas), grave no LOG o status de erro.

As flechas verdes indicam o fluxo sem erros no pacote. As flechas vermelhas indicam o fluxo a ser tomado se algum erro ocorrer.

Para incluir as flechas vermelhas, clique no botão “ko” na barra superior, clique no objeto origem e arraste para o objeto destino. Para as flechas verdes, funciona da mesma forma, mas selecionando o botão “ok”. A última tarefa necessária para execução do pacote é setar a Opção de cada procedimento de Update conforme a sua finalidade. Temos, portanto dois procedimentos, um que registrará as mensagens de erro e outro as mensagens de sucesso. Clicando no Procedimento que irá gravar a mensagem de erro (UPDATE_LOG_pr), vamos na aba “Opções” para inserir o valor de STATUS que este Procedimento deve receber quando for executado, que neste caso é ‘E’ (ERRO) (Figura 44).

Figura 44. Setando o Status do procedimento de erro.

Seguiremos os mesmos passos para outro procedimento (também UPDATE_LOG_pr), onde adicionamos o STATUS para ‘S’ (SUCESSO). Pronto, agora podemos executar o nosso pacote clicando no botão Executar na parte inferior da tela.

Executando um Pacote

Executando uma carga com sucesso (Figura 45) podemos notar na nossa tabela de log (LOG_CARGA) o seguinte registro: “A CARGA DA SESSAO 77001 TERMINOU COM SUCESSO!”

Figura 45. Execução com sucesso do pacote.

Neste ponto podemos simular um erro para verificar a diferença com o processo de carga anterior. Para esta simulação vamos dropar a tabela FATO_VENDAS do banco de destino. Executando o cenário observamos que o fluxo foi desviado para o procedimento de LOG e foi gravado o seguinte registro (Figura 46): “A CARGA DA SESSAO 79001 TERMINOU COM ERRO! VEJA OPERATOR PARA MAIS DETALHES.”

Figura 46. Execução com erro do pacote.

Percebe-se que existe uma diferença entre a Figura 45, que teve a execução da carga aplicada com sucesso e a Figura 46 que resultou em erro.

Gerando um Cenário

Agora que temos nosso pacote completo, falta apenas criar um cenário, que nada mais é do que a versão “compilada” do pacote. É este cenário que será mandado para outros ambientes (testes, produção, etc.) e que será utilizado para rodar as cargas. Para gerar um cenário, basta clicar com o botão direito sobre o pacote e depois em “Gerar cenário” (Figura 47).

Figura 47. Gerando um cenário.

Quando geramos um cenário, temos a opção de colocar uma versão para o mesmo e também a opção de dizer quais são as variáveis que o cenário receberá de entrada. Neste exemplo não temos variáveis de entrada, logo, podemos desmarcá-las.

Pronto! Temos nosso cenário criado, como pode ser visto na Figura 48.

Figura 48. Cenário Criado.

Este cenário funciona como qualquer programa compilado, onde não sofre mais alterações. É possível então fazer modificações nas nossas interfaces, modificar o fluxo do pacote, etc., porém este cenário continuará com a versão compilada anteriormente. Podemos, no entanto, recriar o cenário para refletir as modificações que por ventura foram realizadas, bastando para isso clicar com o botão direito sobre o cenário gerado e escolher a opção “Regenerar...”.

Conclusão

Vimos neste artigo a facilidade e a versatilidade do ODI para construir processos de ETL. Sem muito esforço, conseguimos integrar diferentes origens de dados (Oracle, Firebird e arquivo texto) para um destino único Oracle. Fora a facilidade de se trabalhar com uma ferramenta visual, vimos que os Módulos de Conhecimento (KMs) nos facilitam a manutenção e a padronização dos códigos, tornando assim o ODI uma grande ferramenta para o desenvolvimento dos processos de ETL.

Links

PRODUCT ORACLE: The Official Site
www.oracle.com/products/middleware/odi/oracle-data-integrator.html

TECNOLOGIAS ODI: The Official Site
www.oracle.com/technology/software/products/odi/index.html

Artigos relacionados