关于字符串:在mysql中搜索电话号码

关于字符串:在mysql中搜索电话号码

Searching for phone numbers in mysql

我有一张桌子,上面满是任意格式的电话号码,例如

1
2
3
4
027 123 5644
021 393-5593
(07) 123 456
042123456

我需要搜索类似任意格式的电话号码(例如07123456应该找到条目(07) 123 456

我将以普通的编程语言执行此操作的方法是将所有非数字字符从"针"中去除,然后遍历大海捞针中的每个数字,将所有非数字字符从中去除,然后与针进行比较,例如(ruby)

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

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

问题是,我需要在MySQL中执行此操作。它具有许多字符串函数,似乎没有一个真正可以满足我的要求。

目前我可以想到2个"解决方案"

  • 一起整理CONCATSUBSTR的弗兰肯查询
  • 在针的每个字符之间插入一个%(就像这样:%0%7%1%2%3%4%5%6%)

但是,这两个都不是特别优雅的解决方案。
希望有人可以提供帮助,或者我可能被迫使用%%%%%%%解决方案

更新:这是对一组相对固定的数据进行操作,可能有几百行。我只是不想做一些可笑的坏事,以致将来的程序员会哭泣。

如果数据集增加,我将采用" phoneStripped"方法。感谢您的所有反馈!


could you use a"replace" function to strip out any instances of"(","-" and"",

我不担心结果是数字。
我需要考虑的主要字符是+-()space
那么该解决方案看起来像这样吗?

1
2
3
4
SELECT * FROM people
WHERE
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(phonenumber, '('),')'),'-'),' '),'+')
LIKE '123456'

那不是很慢吗?


这从一开始就是一个问题。您进行的任何类型的搜索都需要进行表扫描,我们都知道这很不好。

在除去所有格式字符后,如何添加带有当前电话号码哈希的列。然后,您至少可以为哈希值建立索引,并避免进行完整的表扫描。

还是数据量很小并且预计增长不会很大?
然后也许只是将所有数字吸入客户端并在其中进行搜索。


我知道这是古老的历史,但是我在寻找类似解决方案时发现了它。

一个简单的REGEXP可能起作用:

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

这将与phonenumber列匹配,带有或不带有任何分隔字符。


这是MySQL的问题-regex函数可以匹配,但不能替换。请参阅此帖子以获取可能的解决方案。


一个开箱即用的想法,但是您是否可以使用"替换"功能删除"(","-"和""的任何实例,然后使用" isnumeric"功能测试是否结果字符串是数字吗?

然后,您可以对要搜索的电话号码字符串执行相同的操作,并将它们比较为整数。

当然,这不适用于1800-MATT-ROCKS之类的数字。 :)


我的解决方案将类似于约翰·代尔(John Dyer)所说的。我要添加第二列(例如phoneStripped),该列会在插入和更新时被剥离。为该列建立索引并对其进行搜索(当然,在删除搜索词之后)。

您也可以添加触发器以自动更新列,尽管我没有使用触发器。但是就像您说的那样,编写MySQL代码来剥离字符串确实很困难,因此仅在客户端代码中执行操作可能会更容易。

(我知道这很晚了,但是我才开始四处看看:)


我建议使用php函数,而不要使用mysql模式,因此您将具有以下代码:

1
2
3
4
5
6
$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 ."'";

请参阅

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

因为只有mysql能"看到"它,所以正则表达式在视觉上不会令人吃惊。请注意,您应在正则表达式中使用" *",而不是""(在OP中以[\\\\ D]开头)。

一些用户担心性能(非索引搜索),但是在有100000个客户的表中,从用户界面发出此查询后,立即返回,没有明显的延迟。


是否可以运行查询以重新格式化数据以匹配所需格式,然后仅运行简单查询?这样,即使最初的重新格式化很慢,您也没关系。


可能的解决方案可以在以下网址找到://udf-regexp.php-baustelle.de/trac/

需要安装其他软件包,然后可以使用REGEXP_REPLACE


正如John Dyer所说,您应该考虑将数据固定在数据库中,并且仅存储数字。但是,如果您面临与我相同的情况(我无法运行更新查询),则发现的解决方法是合并两个查询。

"内部"查询将检索所有电话号码,并将其格式化以除去非数字字符。

1
SELECT REGEXP_REPLACE(column_name, '[^0-9]', '') phone_formatted FROM table_name

其结果将是所有没有任何特殊字符的电话号码。之后,"外部"查询仅需要获取您要查找的条目。
这两个查询将是:

1
2
3
SELECT phone_formatted FROM (
    SELECT REGEXP_REPLACE(column_name, '[^0-9]', '') phone_formatted FROM table_name
) AS result WHERE phone_formatted = 9999999999

重要提示:不使用AS结果,但应该在该位置以避免错误。


创建用户定义的函数以动态创建正则表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 ;

在存储过程中调用该用户定义函数。

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

我是祸。我最终这样做:

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

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

这只是一个主意,但是您不能使用Regex快速删除字符,然后与@Matt Hamilton建议的字符进行比较吗?

甚至可以设置一个视图(不确定视图中是否使用mysql),该视图会将正则表达式剥离的所有电话号码都保留为普通电话号码?


MySQL can search based on regular expressions.

当然可以,但是考虑到任意格式,如果我的干草堆包含"(027) 123 456"(请记住空格的位置可以更改,那么它可能很容易是027 12 3456,而我想与027123456匹配)正则表达式因此需要这个吗?

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

(实际上情况会更糟,因为mysql手册似乎并不表明它支持\\D)

如果是这种情况,那与我的%%%%%想法差不多吗?


我将使用Google的libPhoneNumber将数字格式化为E164格式。我将添加第二列" e164_number"来存储e164格式的数字并在其上添加索引。


如果这是定期发生的事情,则可能将数据修改为全部一种格式,然后设置搜索表单以去除任何非字母数字(如果允许使用310-BELL这样的数字),一个好主意。以易于搜索的格式存储数据是成功的一半。


推荐阅读