我有一张桌子,里面装满了任意格式的电话号码,就像这样

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 中执行此操作。它有许多字符串函数,但似乎没有一个真正能实现我想要的功能。

目前我能想到两个“解决方案”

  • 破解一个弗兰肯查询 CONCATSUBSTR
  • 插入一个 % 在针的每个字符之间(所以它是这样的: %0%7%1%2%3%4%5%6% )

然而,这些似乎都不是特别优雅的解决方案。
希望有人可以提供帮助,否则我可能会被迫使用 %%%%%% 解决方案

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

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


您可以使用“替换”函数来删除“(”、“-”和“”的任何实例吗?

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

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

那不是会非常慢吗?

有帮助吗?

解决方案

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

在删除所有格式字符后添加一个包含当前电话号码哈希的列怎么样?然后您至少可以索引哈希值并避免全面的表扫描。

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

其他提示

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

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

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

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

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

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

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

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

您还可以添加一个触发器来自动更新列,尽管我没有使用过触发器。但正如你所说,编写 MySQL 代码来剥离字符串确实很困难,因此在客户端代码中执行此操作可能会更容易。

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

我建议使用 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 . "' ";

这是MySQL的一个问题——正则表达式函数可以匹配,但不能替换。 看这个帖子 寻找可能的解决方案。

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

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

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

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

MySQL可以基于正则表达式进行搜索。

当然,但是考虑到任意格式,如果我的干草堆包含 "(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)

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

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

也许甚至设置一个视图(不确定视图上的 mysql)将所有由正则表达式剥离的电话号码保存为普通电话号码?

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

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

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

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

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

需要安装额外的软件包,然后你可以使用 REGEXP_REPLACE

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

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;

我会使用谷歌的 lib电话号码 将数字格式化为 E164 格式。我将添加第二列“e164_number”来存储 e164 格式的数字并在其上添加索引。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top