Domanda

Ho una tabella piena di numeri di telefono formattati arbitrariamente, come questo

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

Devo cercare un numero di telefono in un formato altrettanto arbitrario (ad es. 07123456 dovrebbe trovare la voce (07) 123 456

Il modo in cui lo farei in un normale linguaggio di programmazione è quello di togliere tutti i caratteri non numerici dall'ago, quindi esaminare ogni numero nel pagliaio, togliere tutti i caratteri non numerici, quindi confrontarli con l'ago, ad esempio (in rubino)

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

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

Il problema è che devo farlo in MySQL.Ha una serie di funzioni stringa, nessuna delle quali sembra fare davvero quello che voglio.

Attualmente mi vengono in mente 2 "soluzioni"

  • Hack insieme una query di Franken CONCAT E SUBSTR
  • Inserisci un % tra ogni carattere dell'ago (quindi è così: %0%7%1%2%3%4%5%6% )

Nessuna delle due, però, sembra una soluzione particolarmente elegante.
Spero che qualcuno possa aiutarmi o potrei essere costretto a utilizzare la soluzione %%%%%%.

Aggiornamento:Funziona su un insieme di dati relativamente fisso, con forse poche centinaia di righe.Semplicemente non volevo fare qualcosa di ridicolmente brutto su cui i futuri programmatori avrebbero pianto.

Se il set di dati cresce, adotterò l'approccio "phoneStripped".Grazie per tutto il feedback!


potresti usare una funzione "sostituisci" per eliminare qualsiasi istanza di "(", "-" e " ",

Non mi preoccupa che il risultato sia numerico.I personaggi principali che devo considerare sono +, -, (, ) E spaceQuindi la soluzione sarebbe questa?

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

Non sarebbe terribilmente lento?

È stato utile?

Soluzione

Sembra un problema fin dall'inizio.Qualsiasi tipo di ricerca effettuata richiederà una scansione della tabella e sappiamo tutti che non va bene.

Che ne dici di aggiungere una colonna con un hash dei numeri di telefono correnti dopo aver eliminato tutti i caratteri di formattazione.Quindi puoi almeno indicizzare i valori hash ed evitare una scansione completa della tabella.

Oppure la quantità di dati è piccola e non si prevede che cresca molto?Quindi magari semplicemente inserendo tutti i numeri nel client ed eseguendo una ricerca lì.

Altri suggerimenti

So che questa è storia antica, ma l'ho trovata mentre cercavo una soluzione simile.

Un semplice REGEXP può funzionare:

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

Ciò corrisponderebbe a phonenumber colonna con o senza caratteri di separazione.

Un'idea pronta all'uso, ma potresti usare una funzione "sostituisci" per eliminare qualsiasi istanza di "(", "-" e " ", e quindi usare una funzione "isnumeric" per verificare se la stringa risultante è un numero?

Quindi potresti fare lo stesso con la stringa del numero di telefono che stai cercando e confrontarli come numeri interi.

Naturalmente, questo non funzionerà per numeri come 1800-MATT-ROCKS.:)

La mia soluzione sarebbe qualcosa sulla falsariga di ciò che ha detto John Dyer.Aggiungerei una seconda colonna (ad es.phoneStripped) che viene rimosso durante l'inserimento e l'aggiornamento.Indicizza questa colonna e cercala (dopo aver eliminato il termine di ricerca, ovviamente).

Potresti anche aggiungere un trigger per aggiornare automaticamente la colonna, anche se non ho lavorato con i trigger.Ma come hai detto tu, è davvero difficile scrivere il codice MySQL per rimuovere le stringhe, quindi probabilmente è più semplice farlo semplicemente nel codice client.

(So ​​che è tardi, ma ho appena iniziato a guardarmi intorno :)

suggerisco di utilizzare le funzioni php e non i pattern mysql, quindi avrai del codice come questo:

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

Questo è un problema con MySQL: la funzione regex può corrispondere, ma non può sostituire. Vedi questo articolo per una possibile soluzione.

È possibile eseguire una query per riformattare i dati in modo che corrispondano al formato desiderato e quindi eseguire semplicemente una query semplice?In questo modo, anche se la riformattazione iniziale è lenta, non ha molta importanza.

Vedere

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

Non è davvero un problema che l'espressione regolare diventi visivamente spaventosa, poiché solo mysql la "vede".Da notare che al posto del '+' (cfr.post con [\D] dall'OP) dovresti usare '*' nell'espressione regolare.

Alcuni utenti sono preoccupati per le prestazioni (ricerca non indicizzata), ma in una tabella con 100.000 clienti, questa query, quando inviata da un'interfaccia utente, ritorna immediatamente, senza ritardi evidenti.

MySQL può effettuare ricerche in base alle espressioni regolari.

Certo, ma data la formattazione arbitraria, se il mio pagliaio contenesse "(027) 123 456" (tieni presente che la posizione degli spazi può cambiare, potrebbe essere altrettanto facilmente 027 12 3456 e volevo abbinarlo 027123456, la mia espressione regolare dovrebbe quindi essere questa?

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

(in realtà sarebbe peggio dato che il manuale di mysql non sembra indicare che supporti \D)

Se è così, non è più o meno la stessa della mia idea %%%%%?

Solo un'idea, ma non potresti usare Regex per eliminare rapidamente i personaggi e poi confrontarli con quelli come suggerito da @Matt Hamilton?

Forse anche impostare una vista (non sono sicuro di MySQL sulle visualizzazioni) che contenga tutti i numeri di telefono rimossi da regex in un semplice numero di telefono?

Guai a me.Alla fine ho fatto questo:

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

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

se questo è qualcosa che accadrà regolarmente, forse modificare i dati in modo che siano tutti in un unico formato e quindi impostare il modulo di ricerca per eliminare qualsiasi non alfanumerico (se consenti numeri come 310-BELL) sarebbe una buona idea .Avere i dati in un formato facilmente ricercabile è metà dell'opera.

una possibile soluzione può essere trovata all'indirizzo http://udf-regexp.php-baustelle.de/trac/

è necessario installare un pacchetto aggiuntivo, quindi puoi giocare con REGEXP_REPLACE

Crea una funzione definita dall'utente per creare dinamicamente 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 ;

Chiama quella funzione definita dall'utente nella procedura memorizzata.

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

Utilizzerei quello di Google libNumerotelefono per formattare un numero nel formato E164.Aggiungerei una seconda colonna chiamata "e164_number" per memorizzare il numero formattato e164 e aggiungere un indice su di esso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top