Замена значений заполнителей на данные другой таблицы - PullRequest
1 голос
/ 08 июня 2019

У меня есть 2 таблицы. Первая таблица содержит строки с заполнителями, а вторая таблица содержит значения этих заполнителей.

Я хочу запрос, который выбирает данные из первой таблицы и заменяет заполнители фактическими значениями, которые хранятся во второй таблице.

Ex: Данные таблицы 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 ]

1 голос
/ 10 июня 2019

Я создал простой запрос для этого решения, и он работает как требуется.

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
1 голос
/ 08 июня 2019

(неуклюжая) реализация динамического 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)
1 голос
/ 09 июня 2019

Ниже предлагается решение plpgsql в a с одной функцией.Вы заметите, что я «переименовал» столбец значений.Плохо практиковать использование зарезервированных / ключевых слов в качестве имен объектов.Также soq - это схема, которую я использую для всего кода SO.
Процесс сначала берет значения держателя из таблицы 2 и генерирует набор пар ключ-значение (в данном случае 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;
1 голос
/ 08 июня 2019

Если вы хотите использовать Python-подобные именованные заполнители, вам нужна вспомогательная функция, написанная на 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;

(Запрос не был проверен, но у вас есть направление)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...