Question

J'ai un tableau rempli de numéros de téléphone formatés arbitrairement, comme celui-ci

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

Je dois rechercher un numéro de téléphone dans un format tout aussi arbitraire (par ex. 07123456 devrait trouver l'entrée (07) 123 456

La façon dont je procéderais dans un langage de programmation normal est de supprimer tous les caractères non numériques de «l'aiguille», puis de parcourir chaque nombre dans la botte de foin, d'en supprimer tous les caractères non numériques, puis de comparer avec l'aiguille, par exemple (en rubis)

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

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

Le problème est que je dois le faire dans MySQL.Il possède une multitude de fonctions de chaîne, dont aucune ne semble vraiment faire ce que je veux.

Actuellement, je peux penser à 2 « solutions »

  • Hackez ensemble une requête Franken de CONCAT et SUBSTR
  • Insérez un % entre chaque caractère de l'aiguille (donc c'est comme ça : %0%7%1%2%3%4%5%6% )

Cependant, aucune de ces solutions ne semble être particulièrement élégante.
J'espère que quelqu'un pourra m'aider ou je pourrais être obligé d'utiliser la solution %%%%%%

Mise à jour:Cela fonctionne sur un ensemble de données relativement fixe, avec peut-être quelques centaines de lignes.Je ne voulais tout simplement pas faire quelque chose de ridiculement mauvais sur lequel les futurs programmeurs pleureraient.

Si l'ensemble de données s'agrandit, j'adopterai l'approche « phoneStripped ».Merci pour tous les commentaires!


pourriez-vous utiliser une fonction "remplacer" pour supprimer toutes les instances de "(", "-" et " ",

Je ne crains pas que le résultat soit numérique.Les personnages principaux que je dois considérer sont +, -, (, ) et spaceAlors, cette solution ressemblerait-elle à ceci ?

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

Ne serait-ce pas terriblement lent ?

Était-ce utile?

La solution

Cela ressemble à un problème dès le départ.Tout type de recherche que vous effectuez nécessitera une analyse de table et nous savons tous que c'est mauvais.

Que diriez-vous d'ajouter une colonne avec un hachage des numéros de téléphone actuels après avoir supprimé tous les caractères de formatage.Ensuite, vous pouvez au moins indexer les valeurs de hachage et éviter une analyse complète de la table.

Ou la quantité de données est-elle faible et ne devrait-elle pas augmenter considérablement ?Ensuite, peut-être simplement en aspirant tous les numéros dans le client et en y effectuant une recherche.

Autres conseils

Je sais que c'est de l'histoire ancienne, mais je l'ai trouvé en cherchant une solution similaire.

Une simple REGEXP peut fonctionner :

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

Cela correspondrait à phonenumber colonne avec ou sans caractères de séparation.

Une idée originale, mais pourriez-vous utiliser une fonction "remplacer" pour supprimer toutes les instances de "(", "-" et " ", puis utiliser une fonction "isnumérique" pour tester si la chaîne résultante est-ce un nombre ?

Ensuite, vous pouvez faire de même avec la chaîne de numéro de téléphone que vous recherchez et les comparer sous forme d'entiers.

Bien sûr, cela ne fonctionnera pas pour des nombres comme 1800-MATT-ROCKS.:)

Ma solution ressemblerait à ce que John Dyer a dit.J'ajouterais une deuxième colonne (par ex.phoneStripped) qui est supprimé lors de l'insertion et de la mise à jour.Indexez cette colonne et effectuez une recherche dessus (après avoir supprimé votre terme de recherche, bien sûr).

Vous pouvez également ajouter un déclencheur pour mettre à jour automatiquement la colonne, même si je n'ai pas travaillé avec des déclencheurs.Mais comme vous l'avez dit, il est vraiment difficile d'écrire le code MySQL pour supprimer les chaînes, il est donc probablement plus facile de le faire simplement dans votre code client.

(Je sais qu'il est tard, mais je viens juste de commencer à chercher ici :)

je suggère d'utiliser des fonctions php, et non des modèles mysql, vous aurez donc du code comme celui-ci :

$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 . "' ";

C'est un problème avec MySQL : la fonction regex peut correspondre, mais elle ne peut pas remplacer. Voir cet article pour une éventuelle solution.

Est-il possible d'exécuter une requête pour reformater les données afin qu'elles correspondent au format souhaité, puis d'exécuter simplement une requête simple ?De cette façon, même si le reformatage initial est lent, cela n'a pas vraiment d'importance.

Voir

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

Ce n'est pas vraiment un problème que l'expression régulière devienne visuellement épouvantable, puisque seul MySQL la "voit".Notez qu'au lieu de '+' (cfr.poster avec [\D] depuis l'OP), vous devez utiliser '*' dans l'expression régulière.

Certains utilisateurs s'inquiètent des performances (recherche non indexée), mais dans une table contenant 100 000 clients, cette requête, lorsqu'elle est émise depuis une interface utilisateur, revient immédiatement, sans délai notable.

MySQL peut effectuer une recherche basée sur des expressions régulières.

Bien sûr, mais étant donné le formatage arbitraire, si ma botte de foin contenait "(027) 123 456" (gardez à l'esprit que la position des espaces peut changer, cela pourrait tout aussi bien être 027 12 3456 et je voulais le faire correspondre avec 027123456, mon regex devrait-elle donc être celle-ci ?

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

(en fait, ce serait pire car le manuel de MySQL ne semble pas indiquer qu'il prend en charge \D)

Si tel est le cas, n'est-ce pas plus ou moins la même chose que mon idée %%%%% ?

Juste une idée, mais ne pourriez-vous pas utiliser Regex pour supprimer rapidement les caractères, puis comparer avec cela, comme l'a suggéré @Matt Hamilton ?

Peut-être même configurer une vue (pas sûr de MySQL sur les vues) qui contiendrait tous les numéros de téléphone supprimés par regex en un numéro de téléphone simple ?

Pauvre de moi.J'ai fini par faire ceci :

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

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

si c'est quelque chose qui va se produire régulièrement, modifier les données pour qu'elles soient dans un seul format, puis configurer le formulaire de recherche pour supprimer tout chiffre non alphanumérique (si vous autorisez des nombres comme 310-BELL) serait une bonne idée .Disposer de données dans un format facile à rechercher représente la moitié de la bataille.

une solution possible peut être trouvée sur http ://udf-regexp.php-baustelle.de/trac/

un package supplémentaire doit être installé, vous pouvez alors jouer avec REGEXP_REPLACE

Créez une fonction définie par l'utilisateur pour créer dynamiquement des Regex.

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 ;

Appelez cette fonction définie par l'utilisateur dans votre procédure stockée.

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

J'utiliserais celui de Google libPhoneNuméro pour formater un nombre au format E164.J'ajouterais une deuxième colonne appelée "e164_number" pour stocker le numéro au format e164 et y ajouter un index.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top