我的数据库中存储了不同的电话格式,例如:
727.170.4799
1-416-958-6390
1.561.374.4268
(813) 929-5892
Run Code Online (Sandbox Code Playgroud)
此表中大约有约 200 万条记录,因此修改需要很长时间。我需要通过执行电话查找来查找记录。
我控制的一件事是输入,我可以保证它将是数字。但是我不知道他们是否要输入带有或不带有国家/地区代码的数字,因此以这个数字为例,1-416-958-6390
这两个都用于查找:
14169586390
4169586390
Run Code Online (Sandbox Code Playgroud)
应该返回同一行 ( 1-416-958-6390
)
如何尽可能高效地拨打电话号码?
这是我得到的,但它超级慢(正如预期的那样):
select * from patients
WHERE ( REPLACE(phone_number, '-', '') LIKE '%4169586390' )
Run Code Online (Sandbox Code Playgroud)
要清理数字,请使用正则表达式删除所有不是数字的内容:
regexp_replace(phone_number, '[^0-9]', '', 'g')
Run Code Online (Sandbox Code Playgroud)
您可以在其上创建索引,但不会使用常规 B 树索引,例如通配符位于左侧的 LIKE 条件。
在 Postgres 中,您有两种选择:您可以在反向值上创建常规 B 树索引并使用它:
create index on patients ( reverse(regexp_replace(phone_number, '[^0-9]', '', 'g')) );
Run Code Online (Sandbox Code Playgroud)
然后使用该表达式并将其与反向输入进行比较:
select *
from patients
where reverse(regexp_replace(phone_number, '[^0-9]', '', 'g')) like reverse('4169586390')||'%'
Run Code Online (Sandbox Code Playgroud)
另一种选择是安装pg_trgm扩展并在表达式上创建一个三元组索引。这样你就可以“按原样”使用输入:
create index on patients
using gist ( regexp_replace(phone_number, '[^0-9]', '', 'g') gist_trgm_ops);
Run Code Online (Sandbox Code Playgroud)
然后该索引可用于:
select *
from patients
where regexp_replace(phone_number, '[^0-9]', '', 'g') like '%4169586390'
Run Code Online (Sandbox Code Playgroud)
然而,GiST 索引比 B-Tree 索引更大,维护起来也更慢。
如果您不想在每个查询中重复该正则表达式,您可以创建一个包含它的视图,例如:
create view patients_clean_phones
as
select ... other columns ..., regexp_replace(phone_number, '[^0-9]', '', 'g') as clean_phone
from patients;
Run Code Online (Sandbox Code Playgroud)
然后使用(假设您使用的是 GiST 索引,因此不需要反向):
select *
from patients_clean_phones
where clean_phone like '4169586390%';
Run Code Online (Sandbox Code Playgroud)
Postgres 足够聪明,在这种情况下仍然使用索引。
如果您使用的是 Postgres 12,则视图的替代方法是使用带有正则表达式的计算列,然后在计算列上创建索引。
方案A:( 适用于美国和加拿大)
从删除标点和领先的“1”都柱和传入的值是所有真正需要的。 这是许多搜索问题的通用解决方案 - 清理表和传入的查询,目的是提高查找效率。
方案B:
REVERSE(num)
,称之为rev
。它可能是一个“生成”列。索引它。REVERSE("1.561.374.4268")
LEFT(REVERSE("..."), 4)
=“8624”WHERE rev LIKE CONCAT(LEFT(REVERSE(inc), 4)
, '%')`注意反转避免了前导通配符,这会阻止索引的有效使用。您现在有一个可能的值的小列表(大约 200 的 2M)。
REGEXP
检查所有格式和其余数字。更好的是从反向列表中删除所有标点符号。那么唯一的变化是它是否以1
. 如果您仅限于美国和加拿大,只需从rev
列和传入值中删除“1” 。
如果您正在检查“987-654-3210”的某些变体,其中任何一个都将完成对 200 的检查:
phone REGEXP "987.*654.*3210" -- no anchoring needed
rev REGEXP "^0123.*456.*987" -- one anchor needed
Run Code Online (Sandbox Code Playgroud)
所以...
计划C:
rev
计算自的列REVERSE(phone)
然后,在搜索“987-654-3210”的某些变体时,请执行
WHERE rev LIKE '0123%' AND rev RLIKE "^0123.*456.*987"
注: LIKE
愿意使用索引;RLIKE
不是。
清洁
旧版本:
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(num, '-', ''),
')', ''),
'(', ''),
' ', ''),
'.', '')
Run Code Online (Sandbox Code Playgroud)
在 MySQL 8.0 或 MariaDB 10.0 中:
REGEXP_REPLACE(num, '[^0-9]', '')
Run Code Online (Sandbox Code Playgroud)
另外(任何版本),要删除前导“1”,这是一种方法:
RIGHT(num, 10)
Run Code Online (Sandbox Code Playgroud)