如何根据 pl/pgsql 中其他表的值替换所有字符子集?
How to replace all subsets of characters based on values of other tables in pl/pgsql?
我一直在研究如何根据其他行的列的值替换单行的字符串子集,但无法这样做,因为更新仅适用于其他 table 的第一行值。所以我打算将其插入 plpsql 函数的循环中。
这是我的 table 的片段。主要 table:
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:
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_main.expression
列中的所有数值替换为相应的 tbl_values.term
,使用 tbl_values.conceptid
作为每个数字的 link表达式字符串中的值。
由于我是 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 版本:
PostgreSQL 9.3.6 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu
4.8.2-19ubuntu1) 4.8.2, 64-bit
PL/pgSQL 动态函数 SQL
循环始终是最后的手段。即使在这种情况下,使用查询连接查询字符串并执行一次 :
的成本也大大降低
CREATE OR REPLACE FUNCTION f_make_expression(_expr text, OUT result text) AS
$func$
BEGIN
EXECUTE (
SELECT 'SELECT ' || string_agg('replace(', '') || ','
|| string_agg(format('%L,%L)', conceptid::text, v.term), ','
ORDER BY conceptid DESC)
FROM (
SELECT conceptid::bigint
FROM regexp_split_to_table(, '\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
这样做 - 并确保替换字符串不会引起歧义(可能在下一步中替换的数字)。有关这些陷阱的更多相关答案:
令牌</code></strong>在这里有两种<em>不同的</em>用法,不要被误导:</p>
<ol>
<li><pre><code>regexp_split_to_table(<b></b>, '\D+')
这个引用第一个函数参数_expr
。您也可以使用参数名称。
|| '<b></b>,'
这将对通过 USING
子句传递给 EXECUTE
的第一个表达式的引用连接到 SQL 字符串中。外层函数的参数在EXECUTE
内部是不可见的,必须显式传递。
外函数的</code>(<code>_expr
)作为</code>传递给<code>EXECUTE
纯属巧合。不妨交出 </code> 作为 <code>USING
子句中的第三个表达式 (</code>) ...</p>
<p>我在 fiddle 中添加了调试功能。通过稍作修改,您可以输出生成的 SQL 字符串来检查它:</p>
<h3>SQL 函数</h3>
<p>这是一个纯粹的 SQL 替代方案。可能也更快:</p>
<pre><code>CREATE OR REPLACE FUNCTION f_make_expression_sql(_expr text)
RETURNS text AS
$func$
SELECT string_agg(CASE WHEN ~ '^\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(, '\D+') conceptid
, regexp_split_to_table(, '\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
替换旧的(奇怪的)技术来同步 set-returning 函数
WITH ORDINALITY
即时获取行号 可靠 :
- PostgreSQL unnest() with element number
CREATE OR REPLACE FUNCTION f_make_expression_sql(_expr text)
RETURNS text AS
$func$
SELECT string_agg(CASE WHEN ~ '^\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(, '\D+')
, regexp_split_to_table(, '\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"。将其与数千行的查找表一起使用。
您需要将以下 table 个名称和列更改为您的名称:
tbl_main、strsourcetext、strreplacedtext;
查找table、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
我一直在研究如何根据其他行的列的值替换单行的字符串子集,但无法这样做,因为更新仅适用于其他 table 的第一行值。所以我打算将其插入 plpsql 函数的循环中。
这是我的 table 的片段。主要 table:
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:
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_main.expression
列中的所有数值替换为相应的 tbl_values.term
,使用 tbl_values.conceptid
作为每个数字的 link表达式字符串中的值。
由于我是 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 版本:
PostgreSQL 9.3.6 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2, 64-bit
PL/pgSQL 动态函数 SQL
循环始终是最后的手段。即使在这种情况下,使用查询连接查询字符串并执行一次 :
的成本也大大降低CREATE OR REPLACE FUNCTION f_make_expression(_expr text, OUT result text) AS
$func$
BEGIN
EXECUTE (
SELECT 'SELECT ' || string_agg('replace(', '') || ','
|| string_agg(format('%L,%L)', conceptid::text, v.term), ','
ORDER BY conceptid DESC)
FROM (
SELECT conceptid::bigint
FROM regexp_split_to_table(, '\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
这样做 - 并确保替换字符串不会引起歧义(可能在下一步中替换的数字)。有关这些陷阱的更多相关答案:
令牌 这个引用第一个函数参数 这将对通过 外函数的 在 Postgres 9.4 中,这可以通过两个新功能变得更加优雅:</code></strong>在这里有两种<em>不同的</em>用法,不要被误导:</p>
<ol>
<li><pre><code>regexp_split_to_table(<b></b>, '\D+')
_expr
。您也可以使用参数名称。|| '<b></b>,'
USING
子句传递给 EXECUTE
的第一个表达式的引用连接到 SQL 字符串中。外层函数的参数在EXECUTE
内部是不可见的,必须显式传递。</code>(<code>_expr
)作为</code>传递给<code>EXECUTE
纯属巧合。不妨交出 </code> 作为 <code>USING
子句中的第三个表达式 (</code>) ...</p>
<p>我在 fiddle 中添加了调试功能。通过稍作修改,您可以输出生成的 SQL 字符串来检查它:</p>
<h3>SQL 函数</h3>
<p>这是一个纯粹的 SQL 替代方案。可能也更快:</p>
<pre><code>CREATE OR REPLACE FUNCTION f_make_expression_sql(_expr text)
RETURNS text AS
$func$
SELECT string_agg(CASE WHEN ~ '^\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(, '\D+') conceptid
, regexp_split_to_table(, '\d+') txt
) sub
) t
LEFT JOIN tbl_values v ON v.conceptid = NULLIF(t.conceptid, '')::int
$func$ LANGUAGE sql STABLE;
ROWS FROM
替换旧的(奇怪的)技术来同步 set-returning 函数WITH ORDINALITY
即时获取行号 可靠 :
CREATE OR REPLACE FUNCTION f_make_expression_sql(_expr text)
RETURNS text AS
$func$
SELECT string_agg(CASE WHEN ~ '^\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(, '\D+')
, regexp_split_to_table(, '\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"。将其与数千行的查找表一起使用。
您需要将以下 table 个名称和列更改为您的名称:
tbl_main、strsourcetext、strreplacedtext;
查找table、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