[英]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.