这是我的设置:
表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。
答案 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子句,您可以编写许多新的行版本而不进行任何更改。昂贵无用。
对于合法的小写名称,双引号是可选的。
答案 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参数,以避免不必要地更新行。