【问题标题】:What is the best way to separate and save addresses from a string in PL/SQL?在 PL/SQL 中从字符串中分离和保存地址的最佳方法是什么?
【发布时间】:2019-06-19 05:57:33
【问题描述】:

在我们的数据库中,我们有带有公司地址的表格。 一些公司有超过 1 个地址。在这种情况下,我们曾经将地址保存在这样的列中:

'119021, г Москва, ул Льва Толстого, д 16; 117312, г Москва, ул Вавилова, д 19' 

所以地址之间有分号和空格。

现在我必须通过从现有表中复制数据来创建不同的表,其中每个地址都有自己的行。

我应该在我的程序中使用哪些工具

  • 判断一列中是否有一个或多个地址
  • 如果地址超过 1 个,则分开地址

我应该使用 REGEXP 吗?如果是,如何?

【问题讨论】:

标签: sql regex string oracle plsql


【解决方案1】:

首先,如果我没有向未来的搜索者指出,在单个列中存储多条信息违反了数据库设计的第一种正常形式,并且只会给您带来麻烦,那么我将是失职的。既然你正在补救这件事,我向你致敬!

当列表中有空元素时,第二次使用'[^;]+' 形式的正则表达式模式来解析分隔字符串会失败。有关为什么这是一个坏主意的更多信息,请参阅此处:https://stackoverflow.com/a/31464699/2543416

现在,我并不完全是厄运和忧郁!我不知道您使用的是什么字符集,但您会明白的。此示例使用允许 NULL 列表元素和 1 个或多个分号后跟空格或行尾的正则表达式形式。

with tbl(id, str) as (
  select 1, '119021-1, г Москва, ул Льва Толстого, д 16; 117312-1, г Москва, ул Вавилова, д 19' from dual union all
  select 2, '119021-2, г Москва, ул Льва Толстого, д 16;;; 117312-2, г Москва, ул Вавилова, д 19' from dual
)
select id, regexp_substr(str, '(.*?)(;+ |$)', 1, level, NULL, 1) addr_fixed
from tbl
connect by regexp_substr(str, '(.*?)(;+ |$)', 1, level, NULL, 1) is not null
  and prior id = id
  and prior sys_guid() is not null;


        ID ADDR_FIXED                                   
---------- ---------------------------------------------
         1 119021-1, ¿ ¿¿¿¿¿¿, ¿¿ ¿¿¿¿ ¿¿¿¿¿¿¿¿, ¿ 16   
         1 117312-1, ¿ ¿¿¿¿¿¿, ¿¿ ¿¿¿¿¿¿¿¿, ¿ 19        
         2 119021-2, ¿ ¿¿¿¿¿¿, ¿¿ ¿¿¿¿ ¿¿¿¿¿¿¿¿, ¿ 16   
         2 117312-2, ¿ ¿¿¿¿¿¿, ¿¿ ¿¿¿¿¿¿¿¿, ¿ 19        

4 rows selected.

【讨论】:

  • 谢谢!我已经尝试过了,这就是我想要的!
  • 我希望有一天 REGEXP 对我来说不会那么神秘
【解决方案2】:

判断一列中是否有一个或多个地址

您可以使用REGEXP_COUNT 计算分号字符在字段中出现的次数:将其加 1 即可获得地址数:

SELECT t.*, regexp_count(t.address, ';') + 1 FROM mytable t;

如果有超过 1 个,则分开地址

如 O.Jones 所述,要将字符串拆分为新行,一种方法是将正则表达式函数与递归查询结合使用,例如:

SELECT TRIM(regexp_substr( t.address, '[^;]+', 1,  LEVEL )) regexp_substr
FROM mytable t
CONNECT BY LEVEL <= regexp_count( t.address,  ';' ) + 1;

这种类型的语句可以嵌入到INSERT ... AS SELECTCREATE ... AS SELECT 订单中,例如:

CREATE TABLE newtable as
SELECT t.name, TRIM(regexp_substr( t.address, '[^;]+', 1,  LEVEL )) regexp_substr
FROM mytable t
CONNECT BY LEVEL <= regexp_count( t.address,  ';' ) + 1;

Demo on Oracle 11gR2

【讨论】:

  • 非常感谢您提供这个详细而有用的答案!这非常有效!
  • 如何修改'[^;]+',使其可以匹配零个、一个或多个分号。事实证明,我们的数据库中有以下值:address1;address2 或 address1;;地址2?甚至地址1;;;地址2
猜你喜欢
  • 2018-11-19
  • 2013-04-06
  • 2021-06-28
  • 1970-01-01
  • 2018-07-27
  • 2019-07-30
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多