如何将电话号码中的城市代码与 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
.)
我有几张桌子。一个包含拨入 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
.)