在 PostgreSQL 函数中替代嵌套正则表达式替换?

Alternative to nesting regex replace in PostgreSQL functions?

现在,我有一个视图,其中包含一堆常见的条件字符串替换和开放文本字段的替换 - 在本例中为区域分类。

(请忽略地理的准确性,我只是在处理历史标准分配。另外,我知道我可以用 REPLACE 加快速度,甚至只是清理 RegEx 语句以供回顾 - 我我只是在这里询问 variable/nesting。)

    CREATE OR REPLACE FUNCTION public.region_cleanup(record_region text)
     RETURNS text
     LANGUAGE sql
     STRICT
    AS $function$
    SELECT  REGEXP_REPLACE(
            REGEXP_REPLACE(
            REGEXP_REPLACE(
            REGEXP_REPLACE(
            REGEXP_REPLACE(
            REGEXP_REPLACE(record_region,'(NORTH AMERICA\s\-\sUSA\s\-\sUSA)','USA')
            ,'Rest\sof\sthe\sWorld\s\-\s','')
            ,'NORTH\sAMERICA\s\-\sCANADA','NORTH AMERICA - Canada')
            ,'\&amp\;','&')
            ,'Georgia\s\-\sGeorgia','MIDDLE EAST - Georgia')
            ,'EUROPE - Turkey','MIDDLE EAST - Turkey')

使用此函数的示例输出在我的数据集中看起来像这样,提取受影响的记录(有些已经是正确的格式):

record_region_input record_region_output
NORTH AMERICA - USA - USA - NORTHEAST - Massachusetts - Boston Metro USA - NORTHEAST - Massachusetts - Boston Metro
NORTH AMERICA - USA - USA - MIDATLANTIC - Virginia USA - MIDATLANTIC - Virginia
Rest of the World - ASIA - Thailand ASIA - Thailand
Rest of the World - EUROPE - Portugal EUROPE - Portugal
Rest of the World - ASIA - China - Shanghai Metro ASIA - China - Shanghai Metro
Georgia - Georgia MIDDLE EAST - Georgia

这...很好。正则表达式是必需的,因为在这些字符串之前或之后可能会出现大量可变性,并且我在其他地方有一个适当的验证列表。这只是对常见历史命名问题的批量清理。

问题是我从哪里得到了数百种此类“已知替代品”(100+),用于公司命名或跨部门标准等。拥有数十个 REGEXP_REPLACE( 嵌套语句使得 editing/adding/dropping 任何事情都成为令人抓狂的计数游戏。

我正在尝试专门清理 Postgres 中的数据,因为我当前的管道并不总是允许在上传之前进行标准化。我知道如何在纯 SQL 之外干净地解决这个问题,但是在 'vanilla' PostgreSQL 实例 (v12+) 中是否有更好的方法来转换视图的字符串?

使用示例函数更新了示例 input/output table。

如果您将一串数据拆分为其他区域,那么替换区域对您来说可能会很容易。例如:

with tb as (
    select 1 as id, 'NORTH AMERICA - USA - USA - NORTHEAST - Massachusetts - Boston Metro' as record_region_input
    union all 
    select 2 as id, 'NORTH AMERICA - USA - USA - MIDATLANTIC - Virginia'
    union all 
    select 3 as id, 'Rest of the World - ASIA - China - Shanghai Metro' 
)
select * from (
    select distinct tb.id, unnest(string_to_array(record_region_input, ' - ')) as region from tb 
    order by tb.id 
) a1 where a1.region not in ('NORTH AMERICA', 'Rest of the World');

-- Result: 
1   Boston Metro
1   Massachusetts
1   NORTHEAST
1   USA
2   MIDATLANTIC
2   USA
2   Virginia
3   ASIA
3   China
3   Shanghai Metro

之后,例如,对于复制区域,您可以使用 distinct,对于不需要的区域,您可以使用 NOT in,并且您可以使用 like '%ASIA%' 来获取包含 ASIA 和等所有过程完成后,您可以再次合并更正后的字符串。示例:

with tb as (
    select 1 as id, 'NORTH AMERICA - USA - USA - NORTHEAST - Massachusetts - Boston Metro' as record_region_input
    union all 
    select 2 as id, 'NORTH AMERICA - USA - USA - MIDATLANTIC - Virginia'
    union all 
    select 3 as id, 'Rest of the World - ASIA - China - Shanghai Metro' 
)
select a1.id, string_agg(a1.region, ' - ')  from (
    select distinct tb.id, unnest(string_to_array(record_region_input, ' - ')) as region from tb 
    order by tb.id 
) a1 where a1.region not in ('NORTH AMERICA', 'Rest of the World')
group by a1.id 

-- Return: 
1   Boston Metro - Massachusetts - NORTHEAST - USA
2   MIDATLANTIC - USA - Virginia
3   ASIA - China - Shanghai Metro

这是一个简单的想法,也许这个想法可以帮助您替换区域。