如何根据 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