"Propagando Soluções"

Funções de comparação de strings no MySQL

Filed under: Programação, Banco de dados, MySQL, Recuperação de informaçõesAdriano de Oliveira Gonçalves | 10 de Fevereiro de 2008 @ 16:31:27 (Views: 1342)

Boa noite, prezado leitor! Faz um bom tempo que não escrevo, não é verdade? Espero neste ano de 2008 conseguir escrever com mais freqüência… :) Bem, hoje quero falar um pouco sobre algumas das funções de comparação de strings no MySQL. Se você já trabalha com SQL, naturalmente já está acostumado a usar a função LIKE em suas querys, para casos como “SELECT * FROM pessoas WHERE nome LIKE ‘João%’”. De fato, o LIKE resolve boa parte dos nossos problemas, mas uma leve passeada no Manual do MySQL nos revela algumas funções a mais que podem ser muito úteis em alguns casos específicos, e podem poupar bastante trabalho e dor de cabeça na hora de fazer seleção de registros. É sobre elas que quero falar neste artigo - vamos passar por algumas delas, apresentando sua aplicação e vendo alguns exemplos. Estou considerando aqui MySQL versão 4.1 ou superior.

LIKE

Esta função, como já disse, é bastante conhecida dos usuários da linguagem SQL. No MySQL ela é utilizada com a seguinte sintaxe:

expressão LIKE expressão

As duas expressões em questão podem ser campos, valores estáticos, cálculos numéricos, combinações de outras funções, etc. Um exemplo bem básico foi o que dei no início desse texto:

SELECT * FROM pessoas WHERE nome LIKE ‘João%’

O caracter % é interpretado nessa expressão como um meta-caracter, que casa com valores que tenham qualquer número de caracteres (inclusive nenhum) no trecho do texto onde ele é colocado. Logo, a expressão acima casaria com nome = “João da Silva”, nome  = “João Pedro”, mas não casaria com nome = “José João Pedro”. Você pode colocar o meta-caracter % onde quiser na expressão e quantas vezes quiser. Existe ainda o meta-caracter “_”. Ele corresponde a exatamente um caracter, nem menos nem mais do que isso. Por exemplo, na expressão abaixo:

SELECT * FROM pessoas WHERE nome LIKE ‘João_’

Essa expressão casa nome = “João!”, mas não casa com nome = “João da Silva” nem com nome = “João”. O LIKE também pode ser utilizado precedido da opção NOT, que inverte a comparação, fazendo ela casar com todos os registros que não satisfaçam à condição definida. É importante dizer também que o LIKE no MySQL trabalha, por padrão, em caso insensitivo, ou seja, não faz diferença entre letras minúsculas e maiúsculas. Caso queira mudar isso, use a opção BINARY (mais informações no manual). Abaixo, mais alguns exemplos do uso do LIKE:

SELECT * FROM pessoas WHERE nome NOT LIKE ‘Pedro%’
SELECT * FROM pessoas WHERE CONCAT(nome,idade) LIKE ‘A%23′

REGEXP (ou RLIKE)

Esta é uma função muito útil, que permite fazer buscas casando expressões regulares no padrão pad. Você pode utilizá-la tanto como REGEXP ou RLIKE, é a mesma coisa. O uso de expressões regulares em buscas SQL permite realizar comparações mais complexas, sem que seja necessário programar isso no seu aplicativo. Caso não esteja familiarizado com expressões regulares, recomendo ler o Guia de Consulta Rápida do Aurélio “Verde”, uma referência muito boa, bem humorada e em Português. Também é interessante dar uma olhada no apêndice do Manual do MySQL sobre expressões regulares; onde existe uma explicação mais rápida e com alguns exemplos. Assim como o LIKE, a REGEXP também pode ser utilizada com a opção NOT. Abaixo, alguns exemplos de querys utilizando a função REGEXP:

# Casa com todos os registros em que o campo dado tem apenas caracteres numéricos

SELECT * FROM log_operacoes WHERE dado REGEXP ‘^[0-9]*$’                                   

# Casa com todos os registros em que o campo descricao não possui uma tag <form …> (Ex.: <form>, <form method=”get”>, etc.)

SELECT * FROM produtos WHERE descricao NOT REGEXP ‘<form[^>]*>’                     

# Casa com todos os nomes que comecem com “João da Silva”, “João Manoel” ou “João Pedro”

SELECT * FROM pessoas WHERE nome  REGEXP ‘João (da Silva|Manoel|Pedro).*’

MATCH .. AGAINST

MATCH() é uma função que contém toda uma inteligência de recuperação de informações embutida nela, de forma que os resultados são retornados após serem submetidos a cálculos de similaridade. Isso significa que ela trata as buscas de forma parecida com os sites de buscas que conhecemos, que mostram os sites mais “relevantes” na frente. Esta função funciona apenas em tabelas do tipo MyISAM, em campos que tenham um índice FULLTEXT, que pode ser criado para campos do tipo CHAR, VARCHAR ou TEXT. A sintaxe dessa função é a seguinte:

MATCH(coluna1, coluna2, …) AGAINST(expressão)

Abaixo, um exemplo básico de uso dessa função na tabela de posts do meu blog Wordpress/Xoops:

SELECT * FROM xoops_wp_posts WHERE MATCH(post_title, post_content) AGAINST(’Ajax’)

A query acima faz uma busca por relevância nos campos post_title e post_content, procurando pelo termo ‘Ajax’. Para fazer essa pesquisa foi necessário criar um index FULLTEXT na tabela xoops_wp_posts com os dois campos. É necessário criar um índice FULLTEXT para cada grupo de campos que deseja usar em buscas MATCH AGAINST, mesmo que algum dos campos envolvidos já esteja em outro índice.

Nesta função, a busca é feita em caso-insensitivo, e para cada linha da tabela, a função MATCH() retorna um valor de relevância, isto é, uma medida de similaridade entre a string pesquisada (no AGAINST) e o texto nos campos identificados em MATCH(). Quando a função MATCH() é utilizada no WHERE, como no exemplo acima, os resultados já vêm ordenados por relevância em ordem decrescente, ou seja, os registros mais relevantes primeiro. Esses números de relevância são valores float não negativos. Se a relevância for 0 (zero), significa que não há nenhuma similaridade entre a string pesquisada e o conteúdo das colunas. Esse valor é computado baseado no número de palavras no campo, o número de palavras únicas naquele campo, o número de palavras na coleção e o número de documentos (linhas) que contenham uma palavra particular.

Boolean mode

Falando por alto, IN BOOLEAN MODE é uma opção da função MATCH() que permite que sejam colocados alguns modificadores para personalizar um pouco mais a nossa busca, como fazemos no Google. Como, por exemplo, usar o - (sinal de menos) para definir que queremos todos os registros em que determinada palavra não apareça no campo. Digamos que eu queira fazer essa mesma busca que eu fiz na query acima, mas queira todos os registros que possuem a palavra ‘Ajax’ e não possuam a palavra “xajax”, eu poderia fazer assim:

SELECT * FROM xoops_wp_posts WHERE MATCH(post_title, post_content) AGAINST(’+Ajax -xajax‘ IN BOOLEAN MODE)

string_mysql3.jpg

Segue abaixo a descrição dos modificadores do boolean mode:

  • +
    Indica que a palavra deve estar presente em cada linha retornada.
  • -
    Indica que a palavra não deve estar presente em qualquer linha retornada.
  • < >
    Estes dois operadores são usados para alterar a contribuição de uma palavra no valor de relevância que é atribuído a um registro. O operador <  reduz a contribuição e o operador > a aumenta.
  • ( )
    Parênteses são usado para agrupar palavras em subexpressões.
  • ~
    Um til precedente atua como um operador de negação, tornando a contribuição da palavra para a relevância da linha ser negativa. Ele é útil para marcar palavras “ruidosas”. Linhas com tais palavras terão uma avaliação mais baixa que outras, mas não será excluída, como seria com o operador -.
  • *
    Um asterisco é um operador de truncamento (parecido com o % do LIKE). Diferente dos outros operadores, ele deve ser inserido ao fim da palavra, não deve vir no início ou no meio.

  • A frase que é colocada entre aspas duplas “, coincidem apenas com linhas que contenha esta frase literalmente, como foi digitada.

Mais alguns exemplos do uso dos modificadores:

  • adriano ajax
    Encontra linhas que contenham pelo menos uma dessas palavras.
  • +adriano +php
    …ambas as palavras.
  • +email internet
    …palavra “email”, mas a linha ganha mais relevância se também contiver a palavra “internet”.
  • +email -internet
    …palavra “email”, mas não “internet”.
  • +mobile +(>motorola <nokia)
    …”mobile” e “motorola” ou “mobile” e “nokia” (em qualquer ordem), mas avalia melhor “mobile motorola” do que “mobile nokia”.
  • php*
    …”php”, “phpMyAdmin”, “phpboleto”, “phpbrasil”…
  • “alguma coisa”
    …”alguma coisa legal”, “mais alguma coisa”, mas não “alguma boa coisa”


A função MATCH() possui ainda mais alguns recursos, opções e configurações, que podem ser consultados no Manual do MySQL. A sua flexibilidade e inteligência fazem dela uma ferramenta ágil para seleções em sites que envolvam buscas de sites, notícias, blogs, currículos e outras entidades que possuam conteúdo de texto extenso.

Concluindo, podemos dizer que há mais nas ferramentas do que podemos ver sem o manual, e estas funções aqui apresentadas são um pouco desse “mais”, que ajuda a tornar a vida do desenvolvedor mais fácil. Por isso, passeiem pelos manuais de vez em quando, eles costumam ter paisagens tecnologicamente lindas! Leiam mesmo aquilo que vocês não estão precisando no momento, pois poderão precisar desse conteúdo depois. O MySQL mesmo tem muito a oferecer, e garanto que se você for gastar mais um tempo nos manuais vai encontrar muita coisa útil, e imagino até que coisas que você vai dizer “porque eu não vi isso antes!” :) Não se esqueça também que utilizar funções específicas do SGBD torna a sua aplicação mais acoplada a ele, de forma que isso pode dificultar caso queira mudar de SGBD no futuro, o que não acontece na maioria dos projetos de pequeno e médio porte. No demais, vida longa e próspera a todos. Segue abaixo algumas referências para consulta, das quais foram extraídos alguns dos trechos e idéias deste artigo:

13 Comentários

  1. Comentário by Silvio Eberardo:

    Adriano,

    Não posso comentar sobre o tecniquês, mas vc já pensou em comentar o que significa a compra da MySQL pela Sun?

    Abraços,

    Silvio

  2. Comentário by Luis Felipe Alonso Perez:

    Eu estou usando a expressão de busca Match Against, só que eu estou com um pequeno problema o resultado a minha query só busca acima de 3 caracteres.

    Por que isso

  3. Comentário by Adriano de Oliveira Gonçalves:

    Olá Luis, bom dia. Isso é uma configuração do MySQL. Essa configuração pode ser alterada no servidor, de acordo com essas instruções do manual -> http://dev.mysql.com/doc/refman/4.1/pt/fulltext-fine-tuning.html

    Abs,

    Adriano

  4. Comentário by Irineu:

    Parabens… me ajudou muito as explicações sobre o Match Against.
    Obrigado

  5. Comentário by AUGUSTO GOULART:

    Adriano… boa tarde…
    Para utilização do Match Against com o PHP é preciso alguma configuração específica ou o uso de alguma DLL especíca ??

    Aguardo seu retorno…

    Obrigado

  6. Comentário by AUGUSTO GOULART:

    Ahhhh… mais uma coisa…
    Existe alguma “manha” na abertura do MySQL ?? tipo… estar de acordo com o “collation” ou página de código ??

    Aguardo seu retorno…

    +1x… Obrigado

  7. Comentário by Adriano de Oliveira Gonçalves:

    Olá Augusto, não precisa não. É um recurso do MySQL mesmo, não do PHP.

    Abraços!

  8. Comentário by Adriano de Oliveira Gonçalves:

    Como assim “na abertura do MySQL”?

  9. Comentário by AUGUSTO GOULART:

    Boa tarde Adriano…
    Poderia me dar um exemplo de como usar o MATCH/AGAINT junto com o LIKE ??
    Porque estou perguntando isto ?… Eu explico…
    Minha “query” com MATCH/AGAINT está funcionando perfeitamente só que para isso é necessário que a pessoa pesquise a palavra completa (Ex. internet) mas se a pessoa digitar somente “intern” a query não atende… sendo assim acho que fazendo uma composição do LIKE junto com o MATCH/AGAINT seria o ideal…

    Aguardo seu retorno e desde já OBRIGADO pelo “help”…

  10. Comentário by Adriano de Oliveira Gonçalves:

    Tenta usar AND ou o MATCH AGAINST no BOOLEAN MODE (vide artigo).

    Abs!

  11. Comentário by AUGUSTO GOULART:

    É… com o IN BOOLEAN MODE utilizando o “*” funciona… desde que a busca seja somente para uma palavra e esta seja a parte inicial, diferentemente do “%” do LIKE que independe se está no início ou no fim…
    Utilizando o AND LIKE não aconteceu nada… o que seria o ideal…

    Mais alguma idéia ?? hehehe

    Abraços…

  12. Comentário by Adriano de Oliveira Gonçalves:

    Desculpe, eu quis dizer OR, não AND.

  13. Comentário by Adriano de Oliveira Gonçalves:

    E o “*” vc pode tentar inserir no final de todas as palavras digitadas.

Deixe um comentário


:: Adriano de Oliveira Gonçalves, 2004-2008 - contato@adrianoweb.com.br ::

Xoops PhP MySql ApaChe FireFox RSS