.comment-link {margin-left:.6em;}

Friday, April 27, 2007

Query Ignorando Acentos


Olá Marcio, tudo bem contigo?
Eu estava lendo o seu blog e li o assunto sobre acentuação, fiz os testes igual ao seu e funcionou beleza.
O que esta acontecendo comigo, instalei o Oracle 10g XE e importei uma tabela de endereços que já vieram com acentos.
Bom quando faço uma pesquisa EX:
SELECT * FROM CEP_ENDERECO
WHERE LOWER(DSC_LOGRADOURO) LIKE LOWER('%ALÍPIO%') -> Retorna True

Mas não consigo retornar um registo que esta como ALIPIO, ou seja tenho que fazer uma pesquisa com acento e outra sem.

Para responder essa dúvida, é necessário explicar um conceito antes: 'Á' é diferente de 'A', ou seja, a letra "A" com acento agudo é diferente da letra "A" sem acento, então, quando em uma query há uma condição (WHERE) buscando por "A", o Oracle vai comparar números por trás da cena, ele compara os códigos ASCII.

Para conseguir alcançar o objetivo de trazer os nomes ignorando acentos, é preciso uma tradução customizada das letras que possuem acento para suas respectivas, ou seja, transformar a "letra com acento" em "letra sem acento". Em minha proposta para a solução, vou trabalhar com o código ASCII das letras ao invés da figura, isso serve para fixar o conceito da diferença entre as letras acentuadas e "normais".

A visualização do código ASCII é facilmente obtida através da função DUMP. Com ela, poderemos ver a diferença dos códigos entre as letras acentuadas e não-acentuadas. Dessa forma, o Oracle utiliza a comparação dos códigos e chega a conclusão que "Atílio" <> "Atilio".

Para exemplificar, criei uma tabela "t" com um campo "x" varchar2(20) e inseri 4 linhas. Veja a comparação da coluna com o dump ao lado.

ops$marcio:LX10G> col y format a40
ops$marcio:LX10G> select x, dump(x) y from t;

X Y
-------------------- ----------------------------------------
ALIPIO Typ=1 Len=6: 65,76,73,80,73,79
ALÍPIO Typ=1 Len=6: 65,76,205,80,73,79
MÁRCIO Typ=1 Len=6: 77,193,82,67,73,79
MARCIO Typ=1 Len=6: 77,65,82,67,73,79

4 rows selected.

Como podemos observar a partir do resultado acima,

A=65 e Á=193
I=73 e Í=205

Resta agora, "igualar" as letras através de tradução (translate).

ops$marcio:LX10G> select x, translate(x,chr(193)||chr(205), 'AI') y from t;

X Y
-------------------- ----------------------------------------
ALIPIO ALIPIO
ALÍPIO ALIPIO
MÁRCIO MARCIO
MARCIO MARCIO

4 rows selected.
Note que mantive o translate apenas das letras acentuadas no exemplo acima para simplificar, porém, para uma solução ampla e definitiva, é preciso de-para de todas as letras desejadas.

Agora podemos facilmente comparar o registro traduzido a um argumento, como o apresentado pelo autor da dúvida.

ops$marcio:LX10G> select x from t where translate(x,chr(205)||chr(193),'IA') like '%ALIPIO%';

X
--------------------
ALIPIO
ALÍPIO

2 rows selected.

ops$marcio:LX10G> select x from t where translate(x,chr(205)||chr(193),'IA') like '%MARCIO%';

X
--------------------
MÁRCIO
MARCIO

2 rows selected.
Analisando um pouco mais a solução, "de cara" salta aos olhos um problema sério de performance. Eu uso uma função em um campo e, portanto, inviabiliza o uso de índice, mas isso já é parte de outro artigo. Não deixe de pesquisar índice baseado em função para dimunir o tempo de busca deste tipo de query. Outro ponto também é o "cacheamento" de função que começou na versão 9i e está muito melhor na versão 10g, o Oracle "cachea" a função e depois resolve a query, isso evita muitas vezes um exagerado número de switch de contexto.

Labels:


Comments:
Oi Marcio, vi que é possivel setar uma variavel na sessão, tornando a query sensivel ou nao a acentos.

http://www.rampant-books.com/10g_77.htm

Grande abraço.
 
Fabrício, eu já havia escrito algo depois deste post.
http://mportes.blogspot.com/2007/07/consulta-ignorando-acentos.html
 
Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?