使用查找表和替换函数更新子字符串

时间:2013-11-22 19:00:10

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

这是我的设置:

表1(table_with_info):包含我想要替换的子字符串的varchars列表。

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

我想要做的是将table_with_info中的所有子字符串替换为sub_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。

2 个答案:

答案 0 :(得分:3)

您的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

答案 1 :(得分: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参数,以避免不必要地更新行。