在 oracle sql 中使用 regexp_replace 标准化地址

Posted

技术标签:

【中文标题】在 oracle sql 中使用 regexp_replace 标准化地址【英文标题】:Standardizing addresses using regexp_replace in oracle sql 【发布时间】:2021-02-22 21:05:50 【问题描述】:

非常感谢大家的帮助。

我们正在标准化我们客户的地址。

我有一个包含客户数据的主客户表,地址字段要标准化。

我有一个映射表,其中包含从 from_str 到 to_str 值的映射。

我需要将from_str对应的to_str放入地址数据中。

如果标准化后地址的长度超过35个字符,则从字符串右侧到末尾的前一个空格('')创建为单独的字段地址2

你能帮忙处理一下 sql 或 pl/sql 吗?使用 Oracle 12c 数据库。

我在下面编写的代码不适用于所有 from_str 值...仅适用于前 2 行。

感谢任何帮助。

到目前为止的代码:

with addresses as
(
    select cust_id,address addr from
    (
    select 10 cust_id,'9 Help Street, Level 4' address from dual union all
    select 11 cust_id,'22 Victoria Street' address from dual union all
    select 12 cust_id,'1495 Franklin Str' address from dual union all
    select 13 cust_id,'30 Hasivim St.,Petah-Tikva' address from dual union all
    select 14 cust_id,'2 Jakaranda St' address from dual union all
    select 15 cust_id,'61, Science Park Rd' address from dual union all
    select 16 cust_id,'61, Social park road' address from dual union all
    select 17 cust_id,'Av. Hermanos Escobar 5756' address from dual union all
    select 18 cust_id,'Ave. Hermanos Escobar 5756' address from dual union all
    select 19 cust_id,'8000 W FLORISSANT AVE' address from dual union all
    select 20 cust_id,'8600 MEMORIAL PKWY SW' address from dual union all
    select 21 cust_id,'8200 FLORISSANTMEMORIALWAYABOVE SW' address from dual union all
    select 22 cust_id,'8600 MEMORIALFLORISSANT PKWY SW' address from dual
    ) t1
),
  replacements as
    (
    select id,to_str,from_string from_str from
    (
    select 1 id,'St' to_str,'Street' from_string from dual union all
  select 2 id,'St' to_str,'St.' from_string from dual union all
  select 3 id,'St' to_str,'Str' from_string from dual union all
  select 4 id,'St' to_str,'St' from_string from dual union all
  select 5 id,'Rd' to_str,'Rd.' from_string from dual union all
  select 6 id,'Rd' to_str,'road' from_string from dual union all
  select 7 id,'Av' to_str,'Av.' from_string from dual union all
  select 8 id,'Av' to_str,'Ave.' from_string from dual union all
  select 9 id,'Av' to_str,'Avenue' from_string from dual union all
  select 10 id,'Av' to_str,'Aven.' from_string from dual union all
  select 11 id,'West' to_str,'W' from_string from dual union all
  select 12 id,'South West' to_str,'SW.' from_string from dual
  ) t2
),
  r(addr,test_addr,l) as 
  (
      select  addr,regexp_replace(addr,'(^|\W)' || from_str || '(\W|$)','\1' || to_str || '\2') test_addr,
          id - 1
         from  
         addresses,
         replacements
         where id = (select count(*) from replacements)
      union all
         select addr,regexp_replace(addr,'(^|\W)' || from_str || '(\W|$)','\1' || to_str || '\2') test_addr,
          l - 1
         from  r,
         replacements
         where id = l
   )
  select addr,test_addr,l
  from  r
  where l=0
  ;

预期输出:

cust_id address

10      9 Help St, Level 4
11      22 Victoria St
12      1495 Franklin St
13      30 Hasivim St ,Petah-Tikva
14      2 Jakaranda St
15      61, Science Park Rd
16      61, Social park Rd
17      Av Hermanos Escobar 5756
18      Av Hermanos Escobar 5756
19      8000 West FLORISSANT Ave
20      8600 MEMORIAL PKWY South West

如果地址长度超过 35 个字符,则预期输出为:

cust_id address                              address2
21      8200 FLORISSANTMEMORIALWAYABOVE      South West
22      8600 MEMORIALFLORISSANT PKWY         South West

【问题讨论】:

如果能提供一些样本数据和想要的结果就更好了。 @PavelSmirnov,我已经在查询中提供了示例数据,并在我的问题中给出了预期的输出,谢谢 【参考方案1】:

这是一种应用多个替换操作的有趣方式。所以 - 正如你所提到的,你有两个问题。对于第一个,CTE 的递归部分是在 addr 上执行 regexp_replace() 而不是 test_addr(上一个递归步骤的修改输出)。所以只应用列表中的最后一条规则。

r(addr,test_addr, l) as 
(
    select  addr,regexp_replace(addr,'(^|\W)' || from_str || '(\W|$)','\1' || to_str || '\2') test_addr, 
        id - 1
       from  
       addresses,
       replacements
       where id = (select count(*) from replacements)
    union all
       -- if you do regexp_replace on addr, it throws out the previous replace (which is in r.test_addr)
       select addr,regexp_replace(test_addr,'(^|\W)' || from_str || '(\W|$)','\1' || to_str || '\2') test_addr, 
        l - 1
       from  r,
       replacements
       where id = l
 )

对于“超过 35 个字符”的问题,我建议使用 substr/instr - 虽然难以阅读,但它们通常很快。

select addr,test_addr,l, 
  case when length(test_addr) > 35 then
      substr(test_addr, 1, instr(substr(test_addr,1,35), ' ', -1))
    else test_addr
    end as addr1,
  case when length(test_addr) > 35 then
      substr(test_addr, instr(substr(test_addr,1,35), ' ', -1))
    else null
    end as addr2
from  r
where l=0
;

可能有一种更优雅的方式来完成这部分,这只是我想到的第一件事。

【讨论】:

非常感谢。第一个查询有效。超过 35 个字符的查询,一个小故障,我会调查一下。 我尝试了您的查询,但不适用于所有场景...这里有一个场景不起作用..您能帮忙吗? 该查询不适用于某些国家/地区,但适用于某些国家...我发布了一个标题为“标准化地址,sql 不起作用”的新帖子..您能帮忙吗?跨度>

以上是关于在 oracle sql 中使用 regexp_replace 标准化地址的主要内容,如果未能解决你的问题,请参考以下文章

在oracle中,事务中使用下列SQL语句不会引起锁定?

oracle触发器中定时执行sql

SQL SERVER和ORACLE中SQL语句一样吗?

Oracle 12c - 在 REST 调用中使用动态 SQL

SQL 在 SQL Server 和 Oracle 中返回固定数据

ORACLE LEFT JOIN 子查询 在SQL SERVER中可以使用如图中的子查询,ORACLE中怎么实现