15

我有一张桌子,里面全是任意格式的电话号码,像这样

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

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

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

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”方法。感谢所有的反馈!


您能否使用“替换”函数来删除“(”、“-”和“”的任何实例,

我不担心结果是数字。我需要考虑的主要角色是, , 和+那么-这个(解决 方案会是这样吗?)space

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

那不是很慢吗?

4

17 回答 17

13

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

在删除所有格式字符后,如何添加一个包含当前电话号码哈希的列。然后,您至少可以索引哈希值并避免完整的表扫描。

还是数据量很小,预计不会增长太多?然后可能只是将所有数字吸入客户端并在那里运行搜索。

于 2008-09-03T01:21:31.373 回答
6

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

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

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

这将匹配phonenumber有或没有任何分隔字符的列。

于 2014-01-23T18:40:47.353 回答
2

一个开箱即用的想法,但是您是否可以使用“替换”函数来去除“(”、“-”和“”的任何实例,然后使用“isnumeric”函数来测试结果字符串是否是一个数字?

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

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

于 2008-09-02T23:03:52.497 回答
2

这是 MySQL 的一个问题——正则表达式函数可以匹配,但不能替换。请参阅此帖子以获取可能的解决方案。

于 2008-09-02T23:16:19.050 回答
2

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

于 2008-09-02T23:18:03.103 回答
2

我的解决方案将类似于 John Dyer 所说的那样。我会添加在插入和更新时被剥离的第二列(例如phoneStripped)。索引此列并在其上进行搜索(当然,在删除您的搜索词之后)。

您也可以添加一个触发器来自动更新列,尽管我没有使用过触发器。但是就像你说的那样,编写 MySQL 代码来剥离字符串真的很困难,所以在你的客户端代码中这样做可能更容易。

(我知道这已经很晚了,但我才开始在这里四处寻找:)

于 2008-09-18T18:54:23.897 回答
2

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

正则表达式在视觉上变得令人震惊并不是真正的问题,因为只有 mysql “看到”它。请注意,您应该在正则表达式中使用 '*' 而不是 '+' (cfr. post with [\D] 来自 OP)。

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

于 2010-03-11T16:27:41.457 回答
2

我建议使用 php 函数,而不是 mysql 模式,所以你会有一些这样的代码:

$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 . "' ";
于 2011-01-03T14:13:56.603 回答
2

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

“内部”查询将检索所有电话号码并对其进行格式化,删除非数字字符。

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

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

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

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

于 2020-03-26T16:22:22.037 回答
0

MySQL 可以根据正则表达式进行搜索。

当然,但是考虑到任意格式,如果我的 haystack 包含"(027) 123 456"(请记住空格的位置可以改变,它可能很容易027 12 3456并且我想将它与 匹配027123456,因此我的正则表达式是否需要这样?

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

(实际上它会更糟,因为 mysql 手册似乎没有表明它支持\D

如果是这样的话,是不是和我的%%%%%想法差不多?

于 2008-09-02T23:21:17.120 回答
0

只是一个想法,但您不能使用正则表达式快速去除字符,然后像@Matt Hamilton 建议的那样与它进行比较吗?

甚至可能设置一个视图(不确定视图上的mysql),它将所有被正则表达式剥离的电话号码保留为普通电话号码?

于 2008-09-02T23:32:11.727 回答
0

祸是我。我最终这样做了:

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

find(:first, :conditions => ['trim(mobile_phone) like ?', mre])
于 2008-09-02T23:58:03.100 回答
0

如果这是定期发生的事情,也许将数据修改为一种格式,然后设置搜索表单以去除任何非字母数字(如果您允许像 310-BELL 这样的数字)将是一个好主意. 拥有易于搜索格式的数据是成功的一半。

于 2008-09-03T00:44:15.423 回答
0

可以在 http://udf-regexp.php-baustelle.de/trac/ 找到一个可能的解决方案

需要安装额外的包,然后你可以玩 REGEXP_REPLACE

于 2009-05-26T16:32:50.327 回答
0

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

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 ;

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

DECLARE var_PhoneNumberRegex        VARCHAR(200);
SET var_PhoneNumberRegex = udfn_GetPhoneRegex('+ 123 555 7890');
SELECT * FROM Customer WHERE phonenumber REGEXP var_PhoneNumberRegex;
于 2013-09-18T11:16:26.040 回答
0

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

于 2017-01-30T10:48:59.437 回答
0

这是适用于 PHP 用户的有效解决方案。

这使用 PHP 中的循环来构建正则表达式。然后使用 RLIKE 运算符搜索 MySQL 中的数据库。

$phone = '(456) 584-5874'                      // can be any format
$phone = preg_replace('/[^0-9]/', '', $phone); // strip non-numeric characters
$len = strlen($phone);                         // get length of phone number
for ($i = 0; $i < $len - 1; $i++) {
    $regex .= $phone[$i] . "[^[:digit:]]*";
}
$regex .= $phone[$len - 1];

这将创建一个如下所示的正则表达式: 4[^[:digit:]]*5[^[:digit:]]*6[^[:digit:]]*5[^[:digit:]]* 8[^[:digit:]]*4[^[:digit:]]*5[^[:digit:]]*8[^[:digit:]]*7[^[:digit:]]* 4

现在制定你的 MySQL 是这样的:

$sql = "SELECT Client FROM tb_clients WHERE Phone RLIKE '$regex'"

注意:我尝试了其他几个发布的答案,但发现了性能问题。例如,在我们的大型数据库上,运行 IsNumeric 示例需要 16 秒。但是这个解决方案立即运行。而且这个解决方案与旧的 MySQL 版本兼容。

于 2022-01-01T15:13:33.427 回答