簡體   English   中英

如何根據pl / pgsql中其他表的值替換所有字符子集?

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

具有動態SQL的PL / pgSQL函數

循環始終是最后的手段。 即使在這種情況下,使用查詢連接查詢字符串並執行一次也要便宜得多:

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在這里使用兩種不同的方式,不要被誤導:

  1.  regexp_split_to_table( $1 , '\\D+') 

    這個引用了第一個函數參數_expr 您也可以使用參數名稱。

  2.  || ' $1 ,' 

    這會將對通過USING子句傳遞給EXECUTE的第一個表達式的引用連接到SQL字符串中。 外部函數的參數在EXECUTE內部不可見,您必須顯式傳遞它們。

純粹的巧合是,外部函數的$1_expr )作為$1傳遞給EXECUTE 最好在USING子句( $3 )中將$7作為第三個表達式移交...

我在小提琴中添加了調試功能。 通過較小的修改,您可以輸出生成的SQL字符串以對其進行檢查:

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中,這可以通過兩個新功能變得更加優雅:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM