【问题标题】:Update substrings using lookup table and replace function使用查找表和替换函数更新子字符串
【发布时间】:2013-12-07 18:46:55
【问题描述】:

这是我的设置:

表 1 (table_with_info):包含我想替换的带有子字符串的 varchar 列表。

表 2 (sub_info):包含两列:table_with_info 中我要替换的子字符串和我要替换的字符串。

我想做的是用 sub_info 中的替换替换 table_with_info 中的所有子字符串。

这在一定程度上可行,但问题是select replace(...) 为每个替换的单词返回一个新行,而不是替换单个行中的所有单词。

我正在尽我所能解释,但我不知道是不是太清楚了。这是正在发生的事情/我想要发生的事情的代码示例。

这是我的代码:

create table table_with_info
(
    val varchar
);

insert into table_with_info values
  ('this this is test data');

create table sub_info
(
    word_from varchar,
    word_to varchar
);

insert into sub_info values
  ('this','replace1')
, ('test', 'replace2');

update table_with_info set val = (select replace("val", "word_from", "word_to")
from "table_with_info", "sub_info"

update() 函数不起作用,因为 select() 返回两行:

Row 1: replace1 replace1 is test data
Row 2: this this is replace2 data

所以我希望它返回的 select 语句是:

Row 1: replace1 replace1 is test data

有什么想法吗?我无法在我正在运行的系统上创建 UDF。

【问题讨论】:

  • 您在引用带有“”的列,您知道吗?这不是将它们视为字符串而不是字段吗?
  • 您确定不希望 select 语句返回 replace1 replace1 is replace2 data
  • @nrathaus:标准 SQL 和 PostgreSQL 在包含空格的标识符周围使用双引号,区分大小写,... MySQL 为此使用反引号,各种 MS 产品使用括号。字符串文字使用单引号。

标签: sql postgresql replace sql-update pattern-matching


【解决方案1】:

您的UPDATE 语句在多个方面不正确。 Consult the manual 在您尝试再次运行类似的东西之前。你引入了两个交叉连接,这会使这个语句变得非常昂贵,而且会产生废话。

要正确执行此操作,您需要按顺序管理每个UPDATE。在单个语句中,一个行版本消除了另一个行版本,而每次替换都将使用相同的原始行版本。您可以为此使用 DO statement 或将其包装在 plpgsql 函数中:

DO
$do$
DECLARE
   r sub_info;
BEGIN

FOR r IN
   TABLE sub_info
   -- SELECT * FROM sub_info ORDER BY ??? -- order is relevant
LOOP
   UPDATE table_with_info
   SET    val = replace(val, r.word_from, r.word_to) 
   WHERE  val LIKE ('%' || r.word_from || '%'); -- avoid empty updates
END LOOP;

END
$do$;
  • 请注意,应用更新的顺序会有所不同!如果第一次更新创建了第二个匹配的字符串(但不是其他情况)..
    因此,如果相关,请在sub_info 中订购您的列。

  • 避免空更新。如果没有额外的 WHERE 子句,您将编写许多新的行版本而无需更改任何内容。又贵又没用。

  • 双引号对于合法的小写名称是可选的。

->SQLfiddle

【讨论】:

    【解决方案2】:

    扩展 Erwin 的答案,带有动态 SQL 的 do 块也可以解决问题:

    do $$
    declare
      rec record;
      repl text;
    begin
      repl := 'val'; -- quote_ident() this if needed
      for rec in select word_from, word_to from sub_info
      loop
        repl := 'replace(' || repl || ', '
                           || quote_literal(rec.word_from) || ', '
                           || quote_literal(rec.word_to) || ')';
      end loop;
      -- now do them all in a single query
      execute 'update ' || 'table_with_info'::regclass || ' set val = ' || repl;
    end;
    $$ language plpgsql;
    

    或者,以类似的方式构建一个like 参数以避免不必要地更新行。

    【讨论】:

    • 我想我知道这是怎么回事了。您无需编写多个行版本,而是获取该值并在 plpgsql 中运行所有替换,从而节省一些中间行版本。只是您当前的版本不起作用。见小提琴:sqlfiddle.com/#!15/346cd/5。而目前的设计只适用于单行。
    • 如果我全力以赴,我会编写一个函数来创建或替换一个多替换函数(按顺序管理所有替换),然后在单个 @987654324 中调用该函数@。或者,如果sub_info 表上的更改比函数调用少,则保留多替换函数并使用sub_info 表上的触发器重写它。
    • 现在可以使用(已修复错别字)。是的,这是另一种方法。 :-)
    • 我们的方法都没有解决的一件事是,如果rec.word_fromrec.word_to 中,则结果可能会根据替换发生的顺序而不一致。 @ErwinBrandstetter
    • +1 表示工作版本。至于顺序:我确实在我的回答中深入解决了这个问题......
    猜你喜欢
    • 1970-01-01
    • 2014-06-06
    • 1970-01-01
    • 2018-06-09
    • 2021-12-11
    • 2011-08-22
    • 2018-01-09
    • 1970-01-01
    相关资源
    最近更新 更多