用另一个 table 的数据替换占位符值

Replacing Placeholder values with another table's data

我有 2 个 table。第一个 table 包含带有占位符的行,第二个 table 包含那些占位符值。

我想要一个从第一个 table 中获取数据并用存储在第二个 table 中的实际值替换占位符的查询。

例如: 表 1 数据

id                                      value
608CB424-90BF-4B08-8CF8-241C7635434F    jdbc:postgresql://{POSTGRESIP}:{POSTGRESPORT}/{TESTDB}
CDA4C3D4-72B5-4422-8071-A29D32BD14E0    https://{SERVICEIP}/svc/{TESTSERVICE}/

表2数据

id                                      placeolder      value
201FEBFE-DF92-4474-A945-A592D046CA02    POSTGRESIP      1.2.3.4
20D9DE14-643F-4CE3-B7BF-4B7E01963366    POSTGRESPORT    5432
45611605-F2D9-40C8-8C0C-251E300E183C    TESTDB          mytest
FA8E2E4E-014C-4C1C-907E-64BAE6854D72    SERVICEIP       10.90.30.40
45B76C68-8A0F-4FD3-882F-CA579EC799A6    TESTSERVICE     mytest-service

要求的输出是

id                                      value
608CB424-90BF-4B08-8CF8-241C7635434F    jdbc:postgresql://1.2.3.4:5432/mytest
CDA4C3D4-72B5-4422-8071-A29D32BD14E0    https://10.90.30.40/svc/mytest-service/

如果你想使用 Python-like 命名占位符,那么你需要写在 plpythonu 上的辅助函数:

create extension plpythonu;

create or replace function formatpystring( str text, a json ) returns text immutable language plpythonu as $$
import json
d = json.loads(a)
return str.format(**d)
$$;

然后简单测试:

select formatpystring('{foo}.{bar}', '{"foo": "win", "bar": "amp"}');
 formatpystring 
----------------
 win.amp

最后,您需要从您的表格中组合这些参数。很简单:

select t1.id, formatpystring(t1.value, json_object_agg(t2.placeholder, t2.value)) as value
from table1 as t1, table2 as t2
group by t1.id, t1.value;

(查询未测试,但您有方向)

(笨拙)动态 SQL 实现,具有外部连接,但生成递归函数调用:

这个功能效率不会很高,但可能翻译table比较少


CREATE TABLE xlat_table (aa text ,bb text);
INSERT INTO xlat_table (aa ,bb ) VALUES( 'BBB', '/1.2.3.4/')
    ,( 'ccc', 'OMG') ,( 'ddd', '/4.3.2.1/') ;

CREATE FUNCTION dothe_replacements(_arg1 text) RETURNS text
AS
$func$
DECLARE
        script  text;
        braced text;
        res text;
        found record; -- (aa text, bb text, xx text);
BEGIN

script := '';
res := format('%L', _arg1);

for found IN SELECT xy.aa,xy.bb
        , regexp_matches(_arg1, '{\w+}','g' ) AS xx
        FROM xlat_table xy
        LOOP
        -- RAISE NOTICE '#xx=%', found.xx[1];
        -- RAISE NOTICE 'aa=%', found.aa;
        -- RAISE NOTICE 'bb=%', found.bb;

        braced := '{'|| found.aa || '}';
        IF (found.xx[1] = braced  ) THEN
                -- RAISE NOTICE 'Res=%', res;

                script := format ('replace(%s, %L, %L)'
                        ,res,braced,found.bb);
                res := format('%s', script);
        END IF;
        END LOOP;

if(length(script) =0) THEN return res; END IF;

script :='Select '|| script;
-- RAISE NOTICE 'script=%', script;

EXECUTE script INTO res;

return res;
END;

$func$
LANGUAGE plpgsql;

SELECT dothe_replacements( 'aaa{BBB}ccc{ddd}eee' );
SELECT dothe_replacements( '{AAA}bbb{CCC}DDD}{EEE}' );

结果:


CREATE TABLE
INSERT 0 3
CREATE FUNCTION
     dothe_replacements      
-----------------------------
 aaa/1.2.3.4/ccc/4.3.2.1/eee
(1 row)

    dothe_replacements    
--------------------------
 '{AAA}bbb{CCC}DDD}{EEE}'
(1 row)

上述方法具有二次行为(wrt xlat-entries 的数量);这是可怕的

但是,我们可以动态创建一个函数(一次)并多次调用它 (穷人的发电机)

仅从 xlat table 中选择 相关的 条目可能应该被添加。

而且,您当然应该 re-create 每次 xlat table 更改时的函数。


CREATE FUNCTION create_replacement_function(_name text) RETURNS void
AS
$func$
DECLARE
        argname text;
        res text;
        script text;
        braced text;
        found record; -- (aa text, bb text, xx text);
BEGIN

script := '';
argname := '_arg1';
res :=format('%I', argname);

for found IN SELECT xy.aa,xy.bb
        FROM xlat_table xy
        LOOP
        -- RAISE NOTICE 'aa=%', found.aa;
        -- RAISE NOTICE 'bb=%', found.bb;
        -- RAISE NOTICE 'Res=%', res;
        braced := '{'|| found.aa || '}';
        script := format ('replace(%s, %L, %L)'
                        ,res,braced,found.bb);
                res := format('%s', script);
        END LOOP;

script :=FORMAT('CREATE FUNCTION %I (_arg1 text) RETURNS text AS
        $omg$
        BEGIN
        RETURN %s;
        END;
        $omg$ LANGUAGE plpgsql;', _name, script);

RAISE NOTICE 'script=%', script;

EXECUTE script ;

return ;
END;

$func$
LANGUAGE plpgsql;

SELECT create_replacement_function( 'my_function');

SELECT my_function('aaa{BBB}ccc{ddd}eee' );
SELECT my_function( '{AAA}bbb{CCC}DDD}{EEE}' );

结果:


CREATE FUNCTION
NOTICE:  script=CREATE FUNCTION my_function (_arg1 text) RETURNS text AS
    $omg$
    BEGIN
    RETURN replace(replace(replace(_arg1, '{BBB}', '/1.2.3.4/'), '{ccc}', 'OMG'), '{ddd}', '/4.3.2.1/');
    END;
    $omg$ LANGUAGE plpgsql;
 create_replacement_function 
-----------------------------

(1 row)

         my_function         
-----------------------------
 aaa/1.2.3.4/ccc/4.3.2.1/eee
(1 row)

      my_function       
------------------------
 {AAA}bbb{CCC}DDD}{EEE}
(1 row)

下面提供一个单一功能的plpgsql解决方案。 您会注意到我有 'renamed' 值列。使用 rserved/key 个词作为对象名称是不好的做法。 soq 也是我用于所有 SO 代码的架构。
该过程首先从 table2 中获取 holder-values 并生成一组 key-value 对(在本例中为 hstore,但 jsonb 也可以)。然后它从值列(我的列名:val_string)构建一个数组,其中包含值中的 place_holder 名称。最后,它使用数组值作为查找键,迭代该数组,用 key-value 中的值替换实际的 holder-name。
如果 table 中的任何一个的体积较大,性能都不会很好。如果您需要一次处理大量到单行温度 table 可能会产生更好的性能。

create or replace function soq.replace_holders( place_holder_line_in text)
 returns text
language plpgsql
as $$
declare 
    l_holder_values hstore;
    l_holder_line   text; 
    l_holder_array  text[];
    l_indx          integer;

begin
    -- transform cloumns to key-value pairs of holder-value
    select string_agg(place,',')::hstore
      into l_holder_values
      from (
             select concat( '"',place_holder,'"=>"',place_value,'"')  place 
               from soq.table2 
           ) p; 
   -- raise notice 'holder_array_in==%',l_holder_values;

    -- extract the text line and build array of place_holder names
    select phv, string_to_array (string_agg(v,','),',')  
      into l_holder_line,l_holder_array
      from (
            select replace(replace(place_holder_line_in,'{',''),'}','') phv
                 , replace(replace(replace(regexp_matches(place_holder_line_in,'({[^}]+})','g')::text ,'{',''),'}',''),'"','') v 
           ) s 
       group by phv;

    -- raise notice 'Array==%',l_holder_array::text;
    -- replace each key from text line with the corresponding value
    for l_indx in 1 .. array_length(l_holder_array,1)
    loop
        l_holder_line = replace(l_holder_line,l_holder_array[l_indx],l_holder_values -> l_holder_array[l_indx]);
    end loop;

    -- done
    return l_holder_line;       
end;
$$;

-- 试车手

select id, soq.replace_holders(val_string) result_value from soq.table1;

我为此解决方案创建了一个简单的查询,它按要求工作。

WITH RECURSIVE cte(id, value, level) AS (
        SELECT id,value, 0 as level
        FROM Table1 
      UNION 
        SELECT ts.id,replace(ts.value,'{'||tp.placeholder||'}',tp.value) as value, level+1
    FROM cte ts, Table2 tp WHERE ts.value LIKE CONCAT('%',tp.placeholder, '%')
)
SELECT id, value FROM cte c
 where  level =
(
  select Max(level)
  from cte c2  where c.id=c2.id
)  

输出为

id                                      value
CDA4C3D4-72B5-4422-8071-A29D32BD14E0    https://10.90.30.40/svc/mytest-service/
608CB424-90BF-4B08-8CF8-241C7635434F    jdbc:postgresql://1.2.3.4:5432/mytest