{"id":749,"date":"2017-07-09T16:32:00","date_gmt":"2017-07-09T19:32:00","guid":{"rendered":"http:\/\/www.profissionaloracle.com.br\/swillians\/2017\/07\/09\/capturando-erros-em-dml-s\/"},"modified":"2024-01-27T11:43:42","modified_gmt":"2024-01-27T14:43:42","slug":"capturando-erros-em-dml-s","status":"publish","type":"post","link":"https:\/\/www.profissionaloracle.com.br\/2017\/07\/09\/capturando-erros-em-dml-s\/","title":{"rendered":"Capturando Erros em DML’s"},"content":{"rendered":"\n
<\/p>\n\n\n\n
Capturando Erros em DML’s<\/h3>\n\n\n\n
\u00c9 de extrema necessidade se precaver dos erros que podem ocorrer em cargas de dados, transformando-os e\/ou sanitizando-os para uma melhor qualidade dos registros. Mas ora ou outra surgem linhas que trazem alguma informa\u00e7\u00e3o incompat\u00edvel com o tipo de dados de alguma coluna da tabela. \u00c9 a\u00ed que est\u00e1 o problema.<\/p>\n\n\n\n
Na verdade \u00e9 pr\u00e1tica comum de mercado criar tabelas acess\u00f3rias para aglutinar estes poss\u00edveis erros para uma posterior auditoria e tratativa. Mas grande parte das vezes vejo que o processo \u00e9 feito de forma manual, atrav\u00e9s de Exception\u2019s no c\u00f3digo PL\/SQL. N\u00e3o \u00e9 errado, contudo deixamos de utilizar uma \u00f3tima feature do Oracle, o qual automatiza o processo dentro do pr\u00f3prio comando DML.<\/p>\n\n\n\n
As cl\u00e1usulas LOG ERRORS, REJECT e LIMIT auxilia no preenchimento dos erros ocasionalmente ocorridos na tabela acess\u00f3ria criada pela procedure DBMS_ERRLOG.CREATE_ERROR_LOG. Ao utilizar estas op\u00e7\u00f5es o Oracle ir\u00e1 fazer todo o trabalho sujo.<\/p>\n\n\n\n
Vamos para a parte pr\u00e1tica para entender como este processo funciona:<\/p>\n\n\n\n
\n
Inicialmente criaremos a tabela TEMP_TESTE_1 para receber uma posterior carga de dados:<\/li>\n<\/ul>\n\n\n\n
CREATE TABLE temp_teste_1\n (\n ID NUMBER,\n NOME VARCHAR2(10),\n CLASSE NUMBER\n );<\/code><\/pre>\n\n\n\n
\n
Com a utiliza\u00e7\u00e3o da procedure DBMS_ERRLOG.CREATE_ERROR_LOG cria-se a tabela, a qual ser\u00e1 gravados os log\u2019s.<\/li>\n<\/ul>\n\n\n\n
Em suma, a procedure DBMS_ERRLOG.CREATE_ERROR_LOG faz uma c\u00f3pia da tabela principal, adicionando as colunas ORA_ERR_NUMBER$, ORA_ERR_MESG$, ORA_ERR_ROWID$, ORA_ERR_OPTYP$ e ORA_ERR_TAG$. Outro ponto interessante \u00e9 que todas as demais colunas pertencente a tabela principal n\u00e3o s\u00e3o criadas considerando o mesmo tipo de dados, mas sim como VARCHAR2(4000), tendo como objetivo desfazer quaisquer eventuais incompatibilidade de dados.<\/p>\n\n\n\n
Outro adendo \u00e9 que a procedure DBMS_ERRLOG.CREATE_ERROR_LOG apenas automatiza o processo da cria\u00e7\u00e3o da tabela. Contudo, pode-se cri\u00e1-la manualmente atrav\u00e9s do CREATE TABLE, respeitando apenas a obrigatoriedade da exist\u00eancia dos campos de final $.<\/p>\n\n\n
\n <\/figcaption><\/figure><\/div>\n\n\n
\n
Agora se cria a tabela que conter\u00e1 os dados que ser\u00e3o utilizados para a carga com 1000 registros. Com intuito de promover erro intencional, 200 linhas possuem 11 caracteres no campo NOME, gerando incompatibilidade de tamanho de campo com a respectiva coluna da tabela TEMP_TESTE_1, a qual tem tamanho m\u00e1ximo de 10 caracteres.<\/li>\n<\/ul>\n\n\n\n
CREATE TABLE temp_teste_carga AS\nSELECT ROUND(dbms_random.value(1,10)) ID,\n (\n CASE MOD(ROWNUM,5)\n WHEN 0\n THEN CAST (dbms_random.STRING('U',11) AS VARCHAR2(11))\n ELSE CAST (dbms_random.STRING('U',10) AS VARCHAR2(10))\n END) NOME,\n MOD(ROWNUM,5) CLASSE\nFROM DUAL\n CONNECT BY LEVEL <= 1000;\n\nSELECT count(*)\nFROM temp_teste_carga;<\/code><\/pre>\n\n\n
\n <\/figcaption><\/figure><\/div>\n\n\n
Em decorr\u00eancia de existirem 200 linhas com tamanho incompat\u00edvel para o campo NOME, ser\u00e1 emitido o INSERT abaixo. Logo em seguida percebemos que o erro ORA-12899 \u00e9 emitido e \u00e9 feito ROLLBACK para a transa\u00e7\u00e3o como um todo, ou seja, nenhuma linha \u00e9 gravada na tabela TEMP_TESTE_1.<\/p>\n\n\n\n
INSERT INTO TEMP_TESTE_1\nSELECT *\nFROM TEMP_TESTE_CARGA;<\/code><\/pre>\n\n\n
\n <\/figcaption><\/figure><\/div>\n\n\n
Agora se utilizando das instru\u00e7\u00f5es LOG ERRORS, REJECT e LIMIT ser\u00e1 emitido um novo INSERT. Nota-se que foram inseridas 800 linhas das 1000 originais. Os demais 200 registros com tamanho incompat\u00edvel n\u00e3o provoca ROLLBACK da instru\u00e7\u00e3o como um todo, sendo apenas registrados na tabela TEMP_TESTE_1_LOG, conforme podemos ver nas evid\u00eancias abaixo:<\/p>\n\n\n\n
INSERT INTO TEMP_TESTE_1\nSELECT *\nFROM TEMP_TESTE_CARGA\nLOG ERRORS INTO temp_teste_1_log REJECT LIMIT UNLIMITED;<\/code><\/pre>\n\n\n
\n <\/figcaption><\/figure><\/div>\n\n\n
SELECT COUNT(*) FROM temp_teste_1_log;<\/code><\/pre>\n\n\n
\n <\/figcaption><\/figure><\/div>\n\n\n
Os logs de erros s\u00e3o gravados para cada uma das linhas na tabela TEMP_TESTE_1_LOG, nos dando total rastreabilidade para auditoria e tratativas futuras.<\/p>\n\n\n\n
Analisando individualmente cada linhas v\u00ea-se que \u00e9 registrado:<\/p>\n\n\n\n
\n
C\u00f3digo do erro;<\/li>\n\n\n\n
A mensagem descritiva do erro;<\/li>\n\n\n\n
O tipo do processo, sendo: Insert, Update, Delete;<\/li>\n\n\n\n
Os dados das linhas da tabela principal coluna a coluna;<\/li>\n<\/ul>\n\n\n
\n <\/figcaption><\/figure><\/div>\n\n\n
Outros pontos relevantes s\u00e3o:<\/p>\n\n\n\n
\n
A instru\u00e7\u00e3o UNLIMITED pode ser substitu\u00edda por um n\u00famero, onde o mesmo controlar\u00e1 a quantidade de erros que pode ser emitido antes de um eventual ROLLBACK de toda a instru\u00e7\u00e3o;<\/li>\n\n\n\n
Ainda que o INSERT seja atrav\u00e9s de Direct Path a grava\u00e7\u00e3o na tabela de log ser\u00e1 em Conventional Path. Contudo, isto n\u00e3o deve ser um problema para performance como um todo, haja vista que dever\u00e1 ser logado apenas erros ocasionais. Se existirem n\u00fameros parecidos de inserts com sucesso e com erro, pare tudo o que tiver fazendo e volte para o passo de transforma\u00e7\u00e3o das informa\u00e7\u00f5es.<\/li>\n\n\n\n
A tabela de log \u00e9 manipulada atrav\u00e9s de AUTONOMOUS_TRANSACTION, ou seja, poss\u00edveis ROLLBACK na instru\u00e7\u00e3o principal n\u00e3o ir\u00e1 influenciar\u00e1 e preservar\u00e1 os registros da tabela de log.<\/li>\n\n\n\n
LONGs, LOBs e tipo de dados Objeto n\u00e3o dever\u00e3o ser criados na tabela de log;<\/li>\n<\/ul>\n\n\n\n