如何在 SQL 中将电话号码前缀与电话号码中的国家/地区相匹配

use*_*641 5 sql postgresql phone-number libphonenumber

我正在尝试从数字列表中提取国家/地区代码前缀,并将它们与它们所属的区域进行匹配。数据可能看起来像这样:

| id | phone_number   |
|----|----------------|
| 1  | +27000000000   |
| 2  | +16840000000   |
| 3  | +10000000000   |
| 4  | +27000000000   |
Run Code Online (Sandbox Code Playgroud)

这里的国家代码是:

  • 美属萨摩亚:+1684
  • 美国和加勒比海地区:+1
  • 南非:+27

期望的结果是这样的:

| country                     | count |
|-----------------------------|-------|
| South Africa                | 2     |
| American Samoa              | 1     |
| United States and Caribbean | 1     |
Run Code Online (Sandbox Code Playgroud)

有一些困难是因为

  • 国家/地区前缀代码从 1 到 4 个数字不等,即使没有国家/地区前缀,
  • 电话号码的长度因地而异。
  • 我没有对此数据库的写访问权限,因此添加另一列虽然可能是最好的解决方案,但在此用例中不起作用

这是我当前的解决方案:

SELECT 
CASE
    WHEN SUBSTRING(phone_number,1,5) = '+1684' THEN 'American Samoa'
    WHEN SUBSTRING(phone_number,1,5) = '+1264' THEN 'Anguilla'
    ...
    WHEN SUBSTRING(phone_number,1,5) = '+1599' THEN 'Saint Martin'
    WHEN SUBSTRING(phone_number,1,4) = '+355' THEN 'Albania'
    WHEN SUBSTRING(phone_number,1,4) = '+213' THEN 'Algeria'
    ...
    WHEN SUBSTRING(phone_number,1,4) = '+263' THEN 'Zimbabwe'
    WHEN SUBSTRING(phone_number,1,3) = '+93' THEN 'Afghanistan'
    WHEN SUBSTRING(phone_number,1,3) = '+54' THEN 'Argentina'
    ...
    WHEN SUBSTRING(phone_number,1,3) = '+58' THEN 'Venezuela'
    WHEN SUBSTRING(phone_number,1,3) = '+84' THEN 'Vietnam'
    WHEN SUBSTRING(phone_number,1,2) = '+1' THEN 'United States and Caribbean'
    WHEN SUBSTRING(phone_number,1,2) = '+7' THEN 'Kazakhstan, Russia'
    ELSE 'unknown'
END as country_name,
count(*)
FROM users
GROUP BY country_name
order by count desc
Run Code Online (Sandbox Code Playgroud)

约有 205WHEN ... THEN例。看起来效率很低而且超时。我认为这是因为它在每一行上运行模式匹配。这需要扩展到大约数十百万行

有没有更有效的方法来做到这一点?

我正在使用 postgreSQL 9.6.16

Tho*_*ner 1

尽管阅读了整个表格,索引仍然可以提供帮助。为了聚合每个国家/地区代码的数据,DBMS 必须按国家/地区代码对所有行进行排序。排序是一项昂贵的操作,尤其是在大型数据集上。如果您有一个关于国家/地区代码的索引,DBMS 将找到已在索引中预先排序的代码,并且可以避免对数据进行排序的工作。

您在列中没有单独的国家/地区代码,但每个电话号码都以代码开头,因此您可以索引完整的电话号码:

create index idx on users (phone_number);
Run Code Online (Sandbox Code Playgroud)

然后,您必须让 DBMS 清楚地知道您对字符串的开头感兴趣,因此它会考虑使用索引。调用电话号码等功能SUBSTRING可能会使 DBMS 对此视而不见。LIKE代替使用。根据文档(https://www.postgresql.org/docs/9.3/indexes-types.html),字符串索引可用于LIKE 'something%'

WHEN phone_number LIKE '+1684%' THEN 'American Samoa'
Run Code Online (Sandbox Code Playgroud)

不能保证这会有所帮助,但我认为值得一试。这取决于优化器是否看到使用索引中预先排序的电话号码的优势。