Existe um padrão para armazenar números de telefone normalizados em um banco de dados?

95

Qual é uma boa estrutura de dados para armazenar números de telefone em campos de banco de dados? Estou procurando algo que seja flexível o suficiente para lidar com números internacionais e também algo que permita que as várias partes do número sejam consultadas com eficiência.

Edit: Apenas para esclarecer o caso de uso aqui: Atualmente, armazeno números em um único campo varchar e os deixo exatamente como o cliente os inseriu. Então, quando o número é necessário por código, eu normalizo. O problema é que, se eu quiser consultar alguns milhões de linhas para encontrar números de telefone correspondentes, isso envolve uma função, como

where dbo.f_normalizenum(num1) = dbo.f_normalizenum(num2)

o que é terrivelmente ineficiente. Além disso, as consultas que procuram coisas como o código de área tornam-se extremamente complicadas quando é apenas um único campo varchar.

[Editar]

As pessoas fizeram muitas sugestões boas aqui, obrigado! Como uma atualização, aqui está o que estou fazendo agora: eu ainda armazeno os números exatamente como foram inseridos, em um campo varchar, mas em vez de normalizar as coisas no momento da consulta, tenho um gatilho que faz todo o trabalho conforme os registros são inseridos ou atualizado. Portanto, tenho ints ou bigints para todas as partes que preciso consultar, e esses campos são indexados para tornar as consultas mais rápidas.

Eric Z Beard
fonte
Uma resposta contemporânea para a pergunta está aqui - stackoverflow.com/a/51761170/968003 . A essência disso - use RFC 3966 para armazenamento e libphonenumber para análise / validação.
Alex Klaus

Respostas:

80

Primeiro, além do código do país, não existe um padrão real. O melhor que você pode fazer é reconhecer, pelo código do país, a qual nação um determinado número de telefone pertence e lidar com o resto do número de acordo com o formato dessa nação.

Geralmente, no entanto, equipamentos de telefone e outros são padronizados, então você quase sempre pode dividir um determinado número de telefone nos seguintes componentes

  • C Código do país de 1 a 10 dígitos (agora 4 ou menos, mas isso pode mudar)
  • Um código de área (província / estado / região) de 0 a 10 dígitos (pode realmente querer um campo de região e um campo de área separadamente, em vez de um código de área)
  • E Código de troca (prefixo ou switch) de 0 a 10 dígitos
  • Número da linha L 1-10 dígitos

Com esse método, você pode separar potencialmente os números de forma que possa localizar, por exemplo, pessoas que podem ser próximas umas das outras porque têm o mesmo país, área e códigos de câmbio. Com os telefones celulares, isso não é mais algo com que você pode contar.

Além disso, dentro de cada país existem padrões diferentes. Você sempre pode depender de um (AAA) EEE-LLLL nos EUA, mas em outro país você pode ter trocas nas cidades (AAA) EE-LLL e simplesmente números de linha nas áreas rurais (AAA) LLLL. Você terá que começar no topo de uma árvore de alguma forma e formatá-los conforme você tiver informações. Por exemplo, o código do país 0 tem um formato conhecido para o resto do número, mas para o código do país 5432 você pode precisar examinar o código de área antes de entender o resto do número.

Você também pode querer lidar com vanitynúmeros como (800) Lucky-Guy, que requer o reconhecimento de que, se for um número dos EUA, há muitos dígitos (e você pode precisar de representação completa para publicidade ou outros fins) e que nos EUA as letras mapeiam para o números de forma diferente do que na Alemanha.

Você também pode querer armazenar o número inteiro separadamente como um campo de texto (com internacionalização) para que possa voltar mais tarde e re-analisar os números conforme as coisas mudam ou como um backup no caso de alguém enviar um método incorreto para analisar o formato de um determinado país e perde informações.

Adam Davis
fonte
1
Sabe de alguma boa validação de JavaScript para tentar validar isso?
cmcculloh
6
E164 define limites muito mais rígidos para o comprimento dos números: 1-3 para países e um comprimento máximo de 15. Isso não mudará tão cedo, conhecendo o sistema global de telefonia.
Rico
Os comprimentos que você especificou parecem estar, de acordo com ITU-T E.164, completamente errados. Seria útil se você pudesse postar um link para o documento de padrões do qual você obtém suas informações ou explicar por que E.164 não se aplica.
Abtin Forouzandeh
5
@Abtin - nem todo sistema de telefone está em conformidade com ITU-T E.164. A grande maioria deles, entretanto, e vale a pena pesar a escolha entre estar em conformidade com os padrões e bloquear algumas pessoas ou ir além do que o padrão diz e aceitar todos. Observe que E.164 pode ser visto como um subconjunto do esquema acima. Ainda assim, acredito que o melhor formato é aquele que o usuário inseriu exatamente e, em seguida, ter um algoritmo de análise para tokenizar quando necessário, em vez de armazenar o formulário tokenizado no banco de dados.
Adam Davis
1) Pode-se assumir que todos os números internacionais estão em conformidade com os componentes CAE? 2) Você pode assumir que o componente C é a única coisa que difere dependendo de onde você está discando? Por exemplo, o número dos EUA 850-555-1234 tem A = 850 e E = 555-1234, e então C = 1 se estiver discando dos EUA e C = 001 se estiver discando do Reino Unido. Sendo independente de onde você está discando, A e E não são dinâmicos de forma alguma, correto?
AaronLS de
55

KISS - Estou ficando cansado de muitos sites dos EUA. Eles têm alguns códigos habilmente escritos para validar códigos postais e números de telefone. Quando eu digito minhas informações de contato norueguesas perfeitamente válidas, descubro que muitas vezes elas são rejeitadas.

Deixe uma string, a menos que você tenha alguma necessidade específica de algo mais avançado.

Bjorn Reppen
fonte
Um bom velho nvarchar(42)com um pouco de validação /^+?[0-9 -\.\(\)#*]{4,41}$/funciona muito bem!
SandRock,
Eu concordo, mas discordo ao mesmo tempo. Geralmente você deseja fazer algo com esse número de telefone armazenado, como exibi-lo. Em vez de seguir esse caminho de tentar analisá-lo o suficiente para exibi-lo como você deseja, prefiro armazená-lo de forma normalizada. Agora, não estou dizendo que devemos ir tão longe para impor parênteses em torno do código de área. O que estou dizendo é que são todos números, sem travessões etc.
The Muffin Man
4
Acredito que os números de telefone devem ser analisados ​​antes de serem armazenados, para que possam ser validados e armazenados de forma normalizada. A análise e formatação internacional de números de telefone é perfeitamente possível com googlei18n / libphonenumber .
Roel de
21

A página da Wikipedia em E.164 deve informar tudo o que você precisa saber.

Rico
fonte
3
não, esse padrão apenas define como os números de telefone são estruturados (eles são compostos por três números), mas não especifica como eles devem ser exibidos e / ou armazenados. Eu disse padrão? Eu quis dizer recomendação .
BlueWizard
8

Aqui está minha estrutura proposta, gostaria de receber feedback:

O campo do banco de dados do telefone deve ser um varchar (42) com o seguinte formato:

CountryCode - Número x Extensão

Então, por exemplo, nos EUA, poderíamos ter:

1-2125551234x1234

Isso representaria um número dos EUA (código do país 1) com código de área / número (212) 555 1234 e ramal 1234.

Separar o código do país com um traço torna o código do país claro para quem está lendo os dados. Isso não é estritamente necessário porque os códigos de país são " códigos de prefixo " (você pode lê-los da esquerda para a direita e sempre será capaz de determinar sem ambigüidade o país). Mas, uma vez que os códigos de país têm comprimentos variados (entre 1 e 4 caracteres no momento), você não pode dizer facilmente o código do país a menos que use algum tipo de separador.

Eu uso um "x" para separar a extensão porque, caso contrário, realmente não seria possível (em muitos casos) descobrir qual era o número e qual era a extensão.

Desta forma, você pode armazenar o número inteiro, incluindo o código do país e o ramal, em um único campo do banco de dados, que você pode usar para acelerar suas consultas, em vez de ingressar em uma função definida pelo usuário como tem feito penosamente até agora .

Por que escolhi um varchar (42)? Bem, em primeiro lugar, os números de telefone internacionais terão comprimentos variados, daí o "var". Estou armazenando um hífen e um "x", o que explica o "char" e, de qualquer forma, você não fará aritmética inteira com os números de telefone (eu acho), então não faz sentido tentar usar um tipo numérico . Quanto ao comprimento de 42, usei o comprimento máximo possível de todos os campos somados, com base na resposta de Adam Davis, e adicionei 2 para o travessão e o 'x ".

deixado em branco involuntariamente
fonte
7

Procure E.164. Basicamente, você armazena o número de telefone como um código que começa com o prefixo do país e um sufixo PBX opcional. A exibição é, então, um problema de localização. A validação também pode ser feita, mas também é um problema de localização (com base no prefixo do país).

Por exemplo, + 12125551212 + 202 seria formatado na localidade en_US como (212) 555-1212 x202. Teria um formato diferente em en_GBou de_DE.

Há muitas informações por aí sobre o ITU-T E.164, mas são bastante enigmáticas.

Jcoby
fonte
6

Pessoalmente, gosto da ideia de armazenar um número de telefone varchar normalizado (por exemplo, 9991234567) e, é claro, formatar esse número de telefone em linha conforme você o exibe.

Desta forma, todos os dados em seu banco de dados ficam "limpos" e livres de formatação

Mike Fielden
fonte
4

Armazenamento

Armazene telefones em RFC 3966 (como +1-202-555-0252, +1-202-555-7166;ext=22). A principal diferença do E.164 são

  • Sem limite de comprimento
  • Suporte de extensões

Para otimizar o desempenho das operações de visualização, armazene o telefone no formato Nacional / Internacional próximo ao campo RFC 3966.

Não armazene o código do país em um campo separado, a menos que você tenha um motivo sério para isso. Por quê? Porque você não deve pedir o código do país na IU.

Geralmente, as pessoas entram nos telefones quando os ouvem. Por exemplo, se o formato local vai começar de 0ou 8, seria irritante para o usuário fazer a transformação do número na cabeça (como, " OK, não digite '0', escolha o país e digite o resto do pessoa disse neste campo ").

Análise

O Google está a seu lado e você pode validar e analisar qualquer número de telefone usando sua biblioteca libphonenumber . Existem portas para quase todos os idiomas.

Portanto, deixe o usuário inserir apenas " 0449053501" ou " 04 4905 3501" ou " (04) 4905 3501". A ferramenta descobrirá o resto para você.

Veja a demonstração oficial para ter uma ideia do quanto isso ajuda.

Alex Klaus
fonte
3

Talvez armazenando as seções de número de telefone em colunas diferentes, permitindo entradas em branco ou nulas?

Thomas Owens
fonte
3

Ok, então com base nas informações desta página, aqui está um início em um validador de número de telefone internacional:

function validatePhone(phoneNumber) {
    var valid = true;
    var stripped = phoneNumber.replace(/[\(\)\.\-\ \+\x]/g, '');    

    if(phoneNumber == ""){
        valid = false;
    }else if (isNaN(parseInt(stripped))) {
        valid = false;
    }else if (stripped.length > 40) {
        valid = false;
    }
    return valid;
}

Vagamente baseado em um script desta página: http://www.webcheatsheet.com/javascript/form_validation.php

cmcculloh
fonte
2

O padrão para formatar números é e.164 . Você sempre deve armazenar os números neste formato. Você nunca deve permitir o número do ramal no mesmo campo com o número do telefone, eles devem ser armazenados separadamente. Quanto a numérico vs alfanumérico, depende do que você fará com esses dados.

Brian West
fonte
1

Acho que o texto livre (talvez varchar (25)) é o padrão mais usado. Isso permitirá qualquer formato, seja nacional ou internacional.

Acho que o principal fator motivador pode ser como exatamente você está consultando esses números e o que está fazendo com eles.

Don
fonte
Isso ignora o ponto da questão, que é padronizar o conteúdo dos campos do BD para garantir uma correspondência única. Como posso ter certeza de que, ao consultar o número de telefone 800-555-1212, ele corresponde se o usuário pode inserir "(800) 555-1212", "+1.800.555.1212" ou qualquer outro valor equivalente? Esse é o desafio que está sendo enfrentado.
Irongaze.com
1

Acho que a maioria dos formulários da web permite corretamente o código do país, código de área e os 7 dígitos restantes, mas quase sempre me esqueço de permitir a entrada de um ramal. Isso quase sempre acaba fazendo com que eu fale palavrões, já que no trabalho não temos recepcionista e meu ramal é necessário para me alcançar.

Aaron
fonte
1

Acho que a maioria dos formulários da web permite corretamente o código do país, código de área e os 7 dígitos restantes, mas quase sempre me esqueço de permitir a entrada de um ramal. Isso quase sempre acaba fazendo com que eu fale palavrões, já que no trabalho não temos recepcionista e meu ramal é necessário para me alcançar.

Eu teria que verificar, mas acho que nosso esquema de banco de dados é semelhante. Temos um código de país (pode ser os EUA, não tenho certeza), código de área, 7 dígitos e ramal.

Thomas Owens
fonte
1

Que tal armazenar uma coluna de texto livre que mostra uma versão amigável do número de telefone e, em seguida, uma versão normalizada que remove espaços, colchetes e expande '+'. Por exemplo:

Amigável: +44 (0) 181 4642542

Normalizado: 00441814642542

ColinYounger
fonte
10
Para quem exatamente +44 (0) 181 4642542 se destina a ser amigável? Usuários do Reino Unido que podem não saber o que fazer com o +44 se não estiverem acostumados a discar internacionalmente, ou usuários internacionais que não saberão que devem ignorar o (0)?
Mark Baker
0

Eu escolheria um campo de texto livre e um campo que contém uma versão puramente numérica do número de telefone. Eu deixaria a representação do número de telefone para o usuário e usaria o campo normalizado especificamente para comparações de número de telefone em aplicativos baseados em TAPI ou ao tentar localizar entradas duplas em uma lista telefônica. Claro que não faz mal fornecer ao usuário um esquema de entrada que adiciona inteligência, como campos separados para código do país (se necessário), código de área, número de base e ramal.


fonte
0

De onde você está conseguindo os números de telefone? Se você os está obtendo de parte da rede telefônica, obterá uma sequência de dígitos e um tipo de número e plano, por exemplo

441234567890 tipo / plano 0x11 (que significa internacional E.164)

Na maioria dos casos, a melhor coisa a fazer é armazenar todos eles como estão e normalizar para exibição, embora armazenar números normalizados possa ser útil se você quiser usá-los como uma chave única ou semelhante.

Mark Baker
fonte
0

Amigável: +44 (0) 181 464 2542 normalizado: 00441814642542

O (0) não é válido no formato internacional. Consulte o padrão ITU-T E.123.

O formato "normalizado" não seria útil para leitores dos Estados Unidos, pois eles usam 011 para acesso internacional.

cantor dave
fonte
0

Usei três maneiras diferentes de armazenar números de telefone, dependendo dos requisitos de uso.

  1. Se o número estiver sendo armazenado apenas para recuperação humana e não for usado para pesquisar, ele será armazenado em um campo do tipo string exatamente como o usuário o inseriu.
  2. Se o campo for pesquisado, quaisquer caracteres extras, como +, espaços e colchetes, etc., serão removidos e o número restante será armazenado em um campo do tipo string.
  3. Finalmente, se o número de telefone vai ser usado por um aplicativo de computador / telefone, então, neste caso, ele deverá ser inserido e armazenado como um número de telefone válido utilizável pelo sistema, esta opção é claro, sendo a mais difícil de codificar para.
Jimoc
fonte