【问题标题】:How do I create a function in postgres that updates a field with a series of 'REGEXP_REPLACE's如何在 postgres 中创建一个函数,用一系列“REGEXP_REPLACE”更新字段
【发布时间】:2018-04-17 14:57:47
【问题描述】:

我正在尝试创建一个使文本列同质化的函数。它是一系列 regex_replaces 在 case when 函数中。

我相信以下(缩短的)代码应该给我解决方案:

CREATE OR REPLACE FUNCTION clean_data(address_token text) RETURNS 
setof text
AS
$$
BEGIN
return case when address_token like '%allee' OR address_token LIKE '%ally' OR address_token LIKE '%aly' then regexp_replace(address_token,'(allee|ally|aly)$', 'alley')
when address_token like '%annex' OR address_token LIKE '%annx' OR address_token LIKE '%anx' then regexp_replace(address_token,'(annex$|annx$|anx$)', 'anex')
when address_token like '%arc' then regexp_replace(address_token ,'arc$', 'arcade')

.
.
.

when address_token like '%wls' then regexp_replace(address_token ,'wls$', 'wells') else address_token;
END;
$$ LANGUAGE plpgsql;

CREATE TABLE newtable AS
select postcode, (clean_data(address1)) as address1 (clean_data(address2)) as address2, (clean_data(address3)) as address3
from oldtable where postcode SIMILAR TO '(a|b)%';

但是,当我运行它时,我收到错误消息:

RETURN cannot have a parameter in function returning set
LINE 5:   return case when address_token like '%allee' OR address_to...
             ^
HINT:  Use RETURN NEXT or RETURN QUERY.

当我接受它的建议并改用“返回查询”时,我被告知:

syntax error at or near "case"
LINE 5:   return query case when address_token like '%allee' OR addr...

我觉得这不是很有帮助。

这个函数的正确写法是什么?

我对 SQL 函数比较陌生,并且不能 100% 确定:

  • 'returns setof text':这会按预期返回一个字段吗?
  • 语言:这是 SQL 还是 plpgsql
  • 'RETURN' vs 'RETURN NEXT' vs 'RETURN QUERY':我不确定这里的区别

过去几个小时我一直在谷歌上搜索,进展甚微,了解也很少,因此我们将不胜感激

【问题讨论】:

    标签: postgresql plpgsql


    【解决方案1】:

    关键错误在于 setof 返回多行数据:您的函数一次只对一行进行操作,因此应该简单地返回 text。您已将此标记为 plpgsql,但由于它只是一条语句,因此它与 SQL 一样好:SQL 中不需要 BEGINEND 语句,您可以使用 select 而不是 return结果。虽然您的CASE 语句很长,但这仍然是一个简单的函数,一个输入和一个输出为in the docs

    第二个错误是你错过了CASE语句的END:你需要结束case,然后结束plpgsql函数,所以你会有一个双重结束。

    CREATE OR REPLACE FUNCTION clean_data(address_token text) RETURNS text
    AS
    $$
    BEGIN
    return case when address_token like '%allee' OR address_token LIKE '%ally' OR address_token LIKE '%aly' then regexp_replace(address_token,'(allee|ally|aly)$', 'alley')
    .
    .
    .
    when address_token like '%wls' then regexp_replace(address_token ,'wls$', 'wells') else address_token
    end;
    END;
    $$ LANGUAGE plpgsql;
    

    【讨论】:

    • 工作得很好,谢谢。我的印象是返回文本将返回单个字符串,而不是一列字符串。
    【解决方案2】:

    PostgreSQL 手册说:

    可以声明SQL函数返回一个集合(即多行) 通过将函数的返回类型指定为 SETOF sometype,或 等效地通过将其声明为 RETURNS TABLE(columns)

    因此,您使用 setof 返回 rows。在您的情况下,您将返回一个“字段”。所以你应该把你的回报改成RETURNS TEXT(不带setof),或者如果你需要一个表格,你应该把你的案例的输出设置成一个记录或行。

    【讨论】:

      【解决方案3】:

      为什么要为 case 表达式烦恼呢。只需按原样使用您的 regexp_replace 函数,它们已经在您的案例语句中体现了谓词。

      最好把你的表达式放到一个表格中,然后循环遍历它们:

      SQL Fiddle

      PostgreSQL 9.3 架构设置

      create table samples(address_token text);
      create table replacements(exp varchar(30), value varchar(30), flags varchar(10));
      
      INSERT INTO samples
          (address_token)
      VALUES
          ('DB Ally'),
          ('SQL Annex'),
          ('Penny Arc'),
          ('CPU Wls')
      ;
      
      INSERT INTO replacements
          (exp, value, flags)
      VALUES
          ('(allee|ally|aly)$', 'alley', 'i'),
          ('(annex$|annx$|anx$)', 'anex', 'i'),
          ('arc$', 'arcade', 'i'),
          ('wls$', 'wells', 'i')
      ;
      
      create or replace function clean_data(address_token text) returns text
      as
      $$
      DECLARE
        r record;
        result text;
      BEGIN
        result := address_token;
        for r in (select exp, value, flags from replacements) loop
            result := regexp_replace(result, r.exp, r.value, r.flags);
        end loop;
        return result;
      end;
      $$ LANGUAGE plpgsql;
      /
      

      查询 1

      select * from replacements
      

      Results

      |                 exp |  value | flags |
      |---------------------|--------|-------|
      |   (allee|ally|aly)$ |  alley |     i |
      | (annex$|annx$|anx$) |   anex |     i |
      |                arc$ | arcade |     i |
      |                wls$ |  wells |     i |
      

      查询 2

      select address_token, clean_data(address_token) new_val
        from samples
      

      Results

      | address_token |      new_val |
      |---------------|--------------|
      |       DB Ally |     DB alley |
      |     SQL Annex |     SQL anex |
      |     Penny Arc | Penny arcade |
      |       CPU Wls |    CPU wells |
      

      【讨论】:

      • 感谢哨兵的这个回答。我选择了@Adrian Cox 的答案,因为它按要求更正了我的查询。我曾考虑使用与您描述的方法类似的方法,但我的印象是,当替换表变得非常大时,循环遍历它可能会变得非常耗时。但是,Adrian 提到了这种情况,when 也一次只对一行进行操作,因此最终可能会花费类似的时间。下次有机会我会进行实验。
      • 也感谢分享 SQLfiddle,看起来像一个我不知道存在的宝贵资源。
      • 使用 CASE 语句有一个好处,即一旦选择了一个分支,就不会再测试其他分支,但对于选定的分支,正则表达式会重复相同的测试。要在 for 循环版本中获得类似的短路功能,请在 END LOOP; 之前添加退出条件:EXIT WHEN result <> address_token;
      猜你喜欢
      • 2011-12-10
      • 2018-04-20
      • 2018-12-12
      • 2014-05-10
      • 1970-01-01
      • 2023-02-24
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多