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

时间:2019-06-08 11:46:43

标签: sql postgresql postgresql-9.6

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

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

例如: 表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/

4 个答案:

答案 0 :(得分:1)

如果要使用类似Python的命名占位符,则需要写在plpythonu上的辅助函数:

<div id="canvas">
  <div id="colors">&nbsp;</div>
  <div id="gradient-top-left">&nbsp;</div>
  <div id="gradient-top-right">&nbsp;</div>
  <div id="gradient-bottom-left">&nbsp;</div>
  <div id="gradient-bottom-right">&nbsp;</div>
</div>

然后进行简单测试:

nuget install <pkgname>

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

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)
$$;

(查询未经测试,但您有指导)

答案 1 :(得分:1)

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

该功能不是很有效,但是翻译表可能相对较小。


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)

上述方法具有二次行为(输入了xlat项的数量);这是可怕的

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

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

而且,当然,每次xlat表被更改时,您都应该重新创建该函数。


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)

答案 2 :(得分:1)

以下提供了具有单个功能的plpgsql解决方案。 您会注意到我已“重命名”值列。使用保留的/关键字作为对象名称是一种不好的做法。同样,soq是我用于所有SO代码的架构。
该过程首先从table2中获取holder-values,并生成一组键-值对(在这种情况下为hstore,但是jsonb也可以使用)。然后,它从值列(我的列名:val_string)构建一个包含该值的place_holder名称的数组。最后,使用数组值作为查找键,迭代该数组,将实际的所有者名称替换为键值中的值。
从任何一张表中选择更大的音量,性能都不会很好。如果您需要一次处理一个大容量的临时表,则可能会产生更好的性能。

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;

答案 3 :(得分:1)

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

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