[英]How to replace all subsets of characters based on values of other tables in pl/pgsql?
我一直在研究如何基于其他行的列值替换单个行的字符串的子集,但由于更新仅针对的第一行值而无法执行另一张桌子。 因此,我计划将其插入到plpsql函数的循环中。
这是我的桌子的摘录。 主表:
Table "public.tbl_main"
Column | Type | Modifiers
-----------------------+--------+-----------
maptarget | text |
expression | text |
maptarget | expression
-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
43194-0 | 363787002:70434600=(386053000:704347000=(414237002:704320005=259470008,704318007=118539007,704319004=50863008),704327008=122592007,246501002=703690001,370132008=30766002)
查找表:
Table "public.tbl_values"
Column | Type | Modifiers
-----------------------+--------+-----------
conceptid | bigint |
term | text |
conceptid | term
-----------+------------------------------------------
386053000 | Patient evaluation procedure (procedure)
363787002 | Observable entity (observable entity)
704347000 | Observes (attribute)
704320005 | Towards (attribute)
704318007 | Property type (attribute)
我想创建一个函数,使用tbl_values.conceptid
作为表达式字符串中每个数字值的链接,将tbl_main.expression
列中的所有数字值替换为其对应的tbl_values.term
。
由于我是plpgsql LOOP
的新手,因此我目前停留在循环部分。 这是我职能的草稿。
--create first a test table
drop table if exists tbl_test;
create table tbl_test as select * from tbl_main limit 1;
--
create or replace function test ()
RETURNS SETOF tbl_main
LANGUAGE plpgsql
AS $function$
declare
resultItem tbl_main;
v_mapTarget text;
v_expression text;
ctr int;
begin
v_mapTarget:='';
v_expression:='';
ctr:=1;
for resultItem in (select * from tbl_test) loop
v_mapTarget:=resultItem.mapTarget;
select into v_expression expression from ee;
raise notice 'parameter used: %',v_mapTarget;
raise notice 'current expression: %',v_expression;
update ee set expression=replace(v_expression, new_exp::text, term) from (select new_exp::text, term from tbl_values offset ctr limit 1) b ;
ctr:=ctr+1;
raise notice 'counter: %', ctr;
v_expression:= (select expression from ee);
resultItem.expression:= v_expression;
raise notice 'current expression: %',v_expression;
return next resultItem;
end loop;
return;
end;
$function$;
任何进一步的信息将不胜感激。
我的Postgres版本:
x86_64-unknown-linux-gnu上的PostgreSQL 9.3.6,由gcc(Ubuntu 4.8.2-19ubuntu1)编译4.8.2,64位
循环始终是最后的手段。 即使在这种情况下,使用查询连接查询字符串并执行一次也要便宜得多:
CREATE OR REPLACE FUNCTION f_make_expression(_expr text, OUT result text) AS
$func$
BEGIN
EXECUTE (
SELECT 'SELECT ' || string_agg('replace(', '') || '$1,'
|| string_agg(format('%L,%L)', conceptid::text, v.term), ','
ORDER BY conceptid DESC)
FROM (
SELECT conceptid::bigint
FROM regexp_split_to_table($1, '\D+') conceptid
WHERE conceptid <> ''
) m
JOIN tbl_values v USING (conceptid)
)
USING _expr
INTO result;
END
$func$ LANGUAGE plpgsql;
呼叫:
SELECT *, f_make_expression(expression) FROM tbl_main;
但是 ,如果不是所有的conceptid
都具有相同的位数,则该操作可能会模棱两可。 首先用更多数字替换conceptid
以避免发生这种情况ORDER BY conceptid DESC
会这样做-并确保替换字符串不会引起歧义(下一步可能会替换的数字)。 有关这些陷阱的更多答案:
令牌$1
在这里使用两种不同的方式,不要被误导:
regexp_split_to_table( $1 , '\\D+')
这个引用了第一个函数参数_expr
。 您也可以使用参数名称。
|| ' $1 ,'
这会将对通过USING
子句传递给EXECUTE
的第一个表达式的引用连接到SQL字符串中。 外部函数的参数在EXECUTE
内部不可见,您必须显式传递它们。
纯粹的巧合是,外部函数的$1
( _expr
)作为$1
传递给EXECUTE
。 最好在USING
子句( $3
)中将$7
作为第三个表达式移交...
我在小提琴中添加了调试功能。 通过较小的修改,您可以输出生成的SQL字符串以对其进行检查:
这是一个纯SQL替代方法。 可能还会更快:
CREATE OR REPLACE FUNCTION f_make_expression_sql(_expr text)
RETURNS text AS
$func$
SELECT string_agg(CASE WHEN $1 ~ '^\d'
THEN txt || COALESCE(v.term, t.conceptid)
ELSE COALESCE(v.term, t.conceptid) || txt END
, '' ORDER BY rn) AS result
FROM (
SELECT *, row_number() OVER () AS rn
FROM (
SELECT regexp_split_to_table($1, '\D+') conceptid
, regexp_split_to_table($1, '\d+') txt
) sub
) t
LEFT JOIN tbl_values v ON v.conceptid = NULLIF(t.conceptid, '')::int
$func$ LANGUAGE sql STABLE;
在Postgres 9.4中,这可以通过两个新功能变得更加优雅:
ROWS FROM
到替换旧的(怪异的)技术以同步集返回功能 WITH ORDINALITY
可靠地 WITH ORDINALITY
获取行号:
CREATE OR REPLACE FUNCTION f_make_expression_sql(_expr text)
RETURNS text AS
$func$
SELECT string_agg(CASE WHEN $1 ~ '^\d'
THEN txt || COALESCE(v.term, t.conceptid)
ELSE COALESCE(v.term, t.conceptid) || txt END
, '' ORDER BY rn) AS result
FROM ROWS FROM (
regexp_split_to_table($1, '\D+')
, regexp_split_to_table($1, '\d+')
) WITH ORDINALITY AS t(conceptid, txt, rn)
LEFT JOIN tbl_values v ON v.conceptid = NULLIF(t.conceptid, '')::int
$func$ LANGUAGE sql STABLE;
SQL Fiddle演示了Postgres 9.3的全部内容。
还有另一种方法,不用创建功能...使用“ WITH RECURSIVE”。 与数千行查找表一起使用。
您需要将以下表名称和列更改为您的名称:
tbl_main,strsourcetext,strreplacedtext;
查找表,strreplacefrom,strreplaceto。
WITH RECURSIVE replaced AS (
(SELECT
strsourcetext,
strreplacedtext,
array_agg(strreplacefrom ORDER BY length(strreplacefrom) DESC, strreplacefrom, strreplaceto) AS arrreplacefrom,
array_agg(strreplaceto ORDER BY length(strreplacefrom) DESC, strreplacefrom, strreplaceto) AS arrreplaceto,
count(1) AS intcount,
1 AS intindex
FROM tbl_main, lookuptable WHERE tbl_main.strsourcetext LIKE '%' || strreplacefrom || '%'
GROUP BY strsourcetext)
UNION ALL
SELECT
strsourcetext,
replace(strreplacedtext, arrreplacefrom[intindex], arrreplaceto[intindex]) AS strreplacedtext,
arrreplacefrom,
arrreplaceto,
intcount,
intindex+1 AS intindex
FROM replaced WHERE intindex<=intcount
)
SELECT strsourcetext,
(array_agg(strreplacedtext ORDER BY intindex DESC))[1] AS strreplacedtext
FROM replaced
GROUP BY strsourcetext
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.