Pergunta

Eu tenho uma tabela cheia de números de telefone formatados arbitrariamente, como este

027 123 5644
021 393-5593
(07) 123 456
042123456

Preciso procurar um número de telefone em um formato arbitrário semelhante (por exemplo, 07123456 deve encontrar a entrada (07) 123 456

A maneira como eu faria isso em uma linguagem de programação normal é retirar todos os caracteres não numéricos da 'agulha', depois passar por cada número no palheiro, retirar todos os caracteres não numéricos e comparar com a agulha, por exemplo (em rubi)

digits_only = lambda{ |n| n.gsub /[^\d]/, '' }

needle = digits_only[input_phone_number]
haystack.map(&digits_only).include?(needle)

O problema é que preciso fazer isso no MySQL.Ele tem uma série de funções de string, nenhuma das quais realmente parece fazer o que eu quero.

Atualmente posso pensar em 2 'soluções'

  • Hackeie juntos uma consulta franken de CONCAT e SUBSTR
  • Insira um % entre cada caractere da agulha (então é assim: %0%7%1%2%3%4%5%6% )

No entanto, nenhuma destas parece ser uma solução particularmente elegante.
Espero que alguém possa ajudar ou eu posso ser forçado a usar a solução %%%%%%

Atualizar:Isso opera em um conjunto relativamente fixo de dados, talvez com algumas centenas de linhas.Eu só não queria fazer algo ridiculamente ruim que faria os futuros programadores chorarem.

Se o conjunto de dados crescer, adotarei a abordagem 'phoneStripped'.Obrigado por todos os comentários!


você poderia usar uma função "substituir" para remover quaisquer instâncias de "(", "-" e " ",

Não estou preocupado com o resultado ser numérico.Os personagens principais que preciso considerar são +, -, (, ) e spaceEntão essa solução seria assim?

SELECT * FROM people 
WHERE 
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(phonenumber, '('),')'),'-'),' '),'+')
LIKE '123456'

Isso não seria terrivelmente lento?

Foi útil?

Solução

Isso parece um problema desde o início.Qualquer tipo de pesquisa que você fizer exigirá uma varredura na tabela e todos nós sabemos que isso é ruim.

Que tal adicionar uma coluna com um hash dos números de telefone atuais depois de retirar todos os caracteres de formatação.Então você pode pelo menos indexar os valores de hash e evitar uma varredura completa da tabela.

Ou a quantidade de dados é pequena e não se espera que cresça muito?Então talvez apenas coloque todos os números no cliente e faça uma pesquisa lá.

Outras dicas

Eu sei que esta é uma história antiga, mas descobri enquanto procurava uma solução semelhante.

Um simples REGEXP pode funcionar:

select * from phone_table where phone1 REGEXP "07[^0-9]*123[^0-9]*456"

Isto corresponderia ao phonenumber coluna com ou sem caracteres de separação.

Uma ideia pronta para uso, mas você poderia usar uma função "substituir" para remover quaisquer instâncias de "(", "-" e " " e, em seguida, usar uma função "isnumérica" ​​para testar se a string resultante é um número?

Em seguida, você pode fazer o mesmo com a sequência do número de telefone que está procurando e compará-los como números inteiros.

Claro, isso não funcionará para números como 1800-MATT-ROCKS.:)

Minha solução seria algo parecido com o que John Dyer disse.Eu adicionaria uma segunda coluna (por exemplophoneStripped) que é removido na inserção e atualização.Indexe esta coluna e pesquise nela (depois de retirar o termo de pesquisa, é claro).

Você também pode adicionar um gatilho para atualizar automaticamente a coluna, embora eu não tenha trabalhado com gatilhos.Mas como você disse, é realmente difícil escrever o código MySQL para remover as strings, então provavelmente é mais fácil fazer isso no código do cliente.

(Eu sei que é tarde, mas comecei a procurar por aqui :)

sugiro usar funções php, e não padrões mysql, então você terá algum código como este:

$tmp_phone = '';
for ($i=0; $i < strlen($phone); $i++)
   if (is_numeric($phone[$i]))
       $tmp_phone .= '%'.$phone[$i];
$tmp_phone .= '%';
$search_condition .= " and phone LIKE '" . $tmp_phone . "' ";

Este é um problema com o MySQL - a função regex pode corresponder, mas não pode substituir. Veja esta postagem para uma possível solução.

É possível executar uma consulta para reformatar os dados para corresponder ao formato desejado e depois executar uma consulta simples?Dessa forma, mesmo que a reformatação inicial seja lenta, isso realmente não importa.

Ver

http://www.mfs-erp.org/community/blog/find-phone-number-in-database-format-independent

Não é realmente um problema que a expressão regular se torne visualmente terrível, já que apenas o mysql a "vê".Observe que em vez de '+' (cfr.post com [\D] do OP) você deve usar '*' na expressão regular.

Alguns usuários estão preocupados com o desempenho (pesquisa não indexada), mas em uma tabela com 100.000 clientes, esta consulta, quando emitida a partir de uma interface de usuário, retorna imediatamente, sem atraso perceptível.

O MySQL pode pesquisar com base em expressões regulares.

Claro, mas dada a formatação arbitrária, se meu palheiro contivesse "(027) 123 456" (tenha em mente que a posição dos espaços pode mudar, poderia facilmente ser 027 12 3456 e eu queria combiná-lo com 027123456, minha regex precisaria ser esta?

"^[\D]+0[\D]+2[\D]+7[\D]+1[\D]+2[\D]+3[\D]+4[\D]+5[\D]+6$"

(na verdade seria pior porque o manual do mysql não parece indicar que ele suporta \D)

Se for esse o caso, não é mais ou menos igual à minha ideia %%%%%?

Apenas uma ideia, mas você não poderia usar o Regex para retirar rapidamente os caracteres e depois compará-los com os sugeridos por @Matt Hamilton?

Talvez até mesmo configurar uma visualização (não tenho certeza do mysql nas visualizações) que manteria todos os números de telefone separados por regex em um número de telefone simples?

Ai de mim.Acabei fazendo isso:

mre = mobile_number && ('%' + mobile_number.gsub(/\D/, '').scan(/./m).join('%'))

find(:first, :conditions => ['trim(mobile_phone) like ?', mre])

se isso é algo que acontece regularmente, talvez modificar os dados para que tenham todos um formato e, em seguida, configurar o formulário de pesquisa para eliminar qualquer não-alfanumérico (se você permitir números como 310-BELL) seria uma boa ideia .Ter dados em um formato facilmente pesquisável é metade da batalha.

uma possível solução pode ser encontrada em http://udf-regexp.php-baustelle.de/trac/

pacote adicional precisa ser instalado, então você pode brincar com REGEXP_REPLACE

Crie uma função definida pelo usuário para criar Regex dinamicamente.

DELIMITER //

CREATE FUNCTION udfn_GetPhoneRegex
(   
    var_Input VARCHAR(25)
)
RETURNS VARCHAR(200)

BEGIN
    DECLARE iterator INT          DEFAULT 1;
    DECLARE phoneregex VARCHAR(200)          DEFAULT '';

    DECLARE output   VARCHAR(25) DEFAULT '';


   WHILE iterator < (LENGTH(var_Input) + 1) DO
      IF SUBSTRING(var_Input, iterator, 1) IN ( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ) THEN
         SET output = CONCAT(output, SUBSTRING(var_Input, iterator, 1));
      END IF;
      SET iterator = iterator + 1;
   END WHILE;
    SET output = RIGHT(output,10);
    SET iterator = 1;
    WHILE iterator < (LENGTH(output) + 1) DO
         SET phoneregex = CONCAT(phoneregex,'[^0-9]*',SUBSTRING(output, iterator, 1));
         SET iterator = iterator + 1;
    END WHILE;
    SET phoneregex = CONCAT(phoneregex,'$');
   RETURN phoneregex;
END//
DELIMITER ;

Chame essa função definida pelo usuário em seu procedimento armazenado.

DECLARE var_PhoneNumberRegex        VARCHAR(200);
SET var_PhoneNumberRegex = udfn_GetPhoneRegex('+ 123 555 7890');
SELECT * FROM Customer WHERE phonenumber REGEXP var_PhoneNumberRegex;

Eu usaria o Google libPhoneNumber para formatar um número no formato E164.Eu adicionaria uma segunda coluna chamada "e164_number" para armazenar o número formatado em e164 e adicionar um índice a ele.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top