如何将电话号码中的城市代码与 Informix SQL 匹配?

How to match city code in telephone number with Informix SQL?

我有几张桌子。一个包含拨入 phone 个号码:

  num
  84951234567
  74957654321
  4951357246
  83855112345
  73855154321
  3855113524

另一个有城市代码:

  city_code      city_name
  495            Moscow
  38551          Kalmanka

我需要得到以下信息:

  num             call_from
  84951234567     Moscow
  74957654321     Moscow
  4951357246      Moscow
  83855112345     Kalmanka
  73855154321     Kalmanka
  3855113524      Kalmanka

Phone带城市代码的号码总是10位;它前面可以有 7 或 8,也可以什么都不加。城市代码可以有 3 到 5 位数字。 Num 存储为 VARCHAR。 可以用 SQL 解决这个问题吗?

这很难有效地完成。假设城市代码之间没有前缀(如“123”和“1234”),您可以尝试:

select d.*, c.city_name
from dialedin d join
     cities c
     on c.citycode = left(d.num, length(citycode))

这可能无法很好地优化,因为 left() 包含来自两个表的列,这通常会排除使用索引。另一种方法是多重连接:

select d.*, coalesce(c3.city_name, c4.city_name, c5.city_name)
from dialedin d left join
     cities c3
     on c3.citycode = left(d.num, 3) left join
     cities c4
     on c4.citycode = left(d.num, 4) left join
     cities c5
     on c5.citycode = left(d.num, 5) 

多重 join 方法的另一个优点是您可以采用匹配的最长前缀:

select d.*, coalesce(c3.city_name, c4.city_name, c5.city_name)
from dialedin d left join
     cities c5
     on c3.citycode = left(d.num, 5) left join
     cities c4
     on c4.citycode = left(d.num, 4) and c5.citycode is null left join
     cities c3
     on c5.citycode = left(d.num, 3) and c4.citycode is null

Paul在评论中提到的解决方案,你也修改为

        JOIN ON city_code = LEFT(num, 5) or city_code = LEFT(num, 3)

编辑

让我们看看可能的情况。如果您有最多 5 位代码,并且前缀为数字 7 或 8,那么总共会有 6 位数字。您的广义 join 子句将是

       JOIN ON LEFT(num, 6) LIKE '%'+city_code+'%'

希望这能奏效。

如另一个答案所述,要使此查询高效很复杂。 通过此查询,您可以获得所需的内容:

SELECT p.num, c.city_name
  FROM phones p, cities c
 WHERE LEFT(p.num, LENGTH(c.city_code)+1) MATCHES "*" || c.city_code || "*"

因为可以作为 phone 数字中 city_code 之前的前缀,所以我取了一个长度为 (city_code) + 1

的子串

* 编辑 *

我用 outer 连接到 select phones 而没有 city_code,因为带有城市代码的 phone 总是 10 位数字,首先,我对 phone 的最后 10 位数字进行子字符串化(如另一个答案所示)

SELECT p.num, c.city_name
  FROM phones p, OUTER cities c
 WHERE c.city_code = LEFT(RIGHT(p.num, 10), LENGTH(c.city_code))

假设您使用的是 Informix 14.10 服务器,而不是更早的版本,例如 12.10 或 11.70(或任何更早的版本,不受支持),那么您可以考虑:

WITH mapped_cities(city_code, city_name) AS
     (SELECT        city_code, city_name FROM city_codes
      UNION
      SELECT '7' || city_code, city_name FROM city_codes
      UNION
      SELECT '8' || city_code, city_name FROM city_codes
     )
SELECT d.num, m.city_name AS call_from
  FROM dialled_phone_numbers AS d
  JOIN mapped_cities AS m
    ON m.city_code = LEFT(d.num, LENGTH(m.city_code))

样本 tables 和数据设置如下(一些数据来自问题,一些数据来自评论):

CREATE TABLE dialled_phone_numbers(num VARCHAR(11) NOT NULL PRIMARY KEY);
CREATE TABLE city_codes(city_code VARCHAR(5) NOT NULL PRIMARY KEY, city_name VARCHAR(25) NOT NULL);

INSERT INTO city_codes VALUES('812', 'St.Petersburg');
INSERT INTO city_codes VALUES('3812', 'Omsk');
INSERT INTO city_codes VALUES('495', 'Moscow');
INSERT INTO city_codes VALUES('38551', 'Kalmanka');

INSERT INTO dialled_phone_numbers VALUES('3812217715');
INSERT INTO dialled_phone_numbers VALUES('3855113524');
INSERT INTO dialled_phone_numbers VALUES('4951357246');
INSERT INTO dialled_phone_numbers VALUES('73855154321');
INSERT INTO dialled_phone_numbers VALUES('74957654321');
INSERT INTO dialled_phone_numbers VALUES('84951234567');

显示的查询产生输出:

num          call_from
VARCHAR(11)  VARCHAR(25)
3812217715   Omsk
3855113524   Kalmanka
4951357246   Moscow
73855154321  Kalmanka
74957654321  Moscow
84951234567  Moscow

显然,可以使用 ORDER BY 子句更好地排序。

WITH 子句生成以 'nothing' 或 7 或 8 作为前缀的前缀列表。然后,使用 Gordon Linoff's .

中第一个代码段的连接条件,将其用于连接 dialled_phone_numbers table 中数字的开头

如果您有没有 WITH 子句(通用 table 表达式或 CTE)的旧版本 Informix,那么您可以使用临时 table 来保存 UNION 子句的结果查询在 WITH 子句中使用并改为加入。

您对

感兴趣
  • 所有 10 位数字
  • 所有以 7 开头的 11 位数字
  • 所有以 8 开头的 11 位数字

Select那些和外部加入城市(或者内部加入它们,如果你只想看匹配甚至看到它保证你只得到匹配):

select
  p.phone_number,
  c.city
from phonecalls p
left join cities c on right(p.phone_number, 10) like c.city_code || '%'
where length(p.phone_number) = 10
or (length(p.phone_number) = 11 and p.phone_number like '7%')
or (length(p.phone_number) = 11 and p.phone_number like '8%')
order by c.city, right(p.phone_number, 10), p.phone_number;

(如果这太慢,您可能需要考虑编写一个用户定义的函数以从那些 10/11 位数字中获取 10 位数字并为此创建一个函数索引。查询将读取像 select ... join cities c on ten_digit_number(p.phone_number) like c.city_code || '%' ... where ten_digit_number(p.phone_number) is not null.)