2016-12-19 7 views
1

Я пытаюсь построить функцию, где я смог бы сделать что-то вроде этого:Создать функцию, заменяющую varchar таблицей словаря?

replace_with_dict(<this_table_to_replace_column, <dictionary_table>, <dictionary_from_field>, <dictionary_to_field>)

Словаря таблицы будет иметь от и к колонку, а функция может заменить все вхождения любого словаря из поля в его поле.

Я пытался сам, но не добился успеха. Вот что я делал до сих пор.

CREATE OR REPLACE FUNCTION replace_with_dict(to_replace VARCHAR, 
dict_table regclass, from_field VARCHAR, to_field VARCHAR) 
RETURNS VARCHAR AS $$ 
    DECLARE 
     replaced VARCHAR; 
     dict_entry RECORD; 
     from_replace_pattern VARCHAR; 
     to_replace_pattern VARCHAR; 
     dictionary CURSOR FOR SELECT from_field AS "in", to_field AS "out" FROM basf_dict; 
    BEGIN 
     replaced := to_replace; 
--  EXECUTE(format('SELECT %S, %S FROM %S;', from_field, to_field, dict_table)) IN dictionary; 
     FOR dic_entry IN dictionary LOOP 
      from_replace_pattern := ' ' || dic_entry."in" || ' '; 
      to_replace_pattern := ' ' || dic_entry."out" || ' '; 
      replaced := REPLACE(replaced, from_replace_pattern, to_replace_pattern); 
     END LOOP; 
     RETURN replaced; 
    END; 
$$ LANGUAGE plpgsql 

Когда я пытаюсь запустить описанную выше функцию в запросе, как это, replace_with_dict(p.nom_produto, "basf_dict", "de", "para"),. Я получаю эту ошибку:

SQL Error [42703]: ERROR: column "basf_dict" does not exist 
    Posição: 86 
    org.postgresql.util.PSQLException: ERROR: column "basf_dict" does not exist 
    Posição: 86 

EDIT 1:

Просто обратите внимание, что для переменной была опечатка. Я исправил, теперь моя функция декларации выглядит следующим образом:

CREATE OR REPLACE FUNCTION replace_with_dict(to_replace VARCHAR, dict_table VARCHAR, from_field VARCHAR, to_field VARCHAR) 
RETURNS VARCHAR AS $$ 
    DECLARE 
     replaced VARCHAR; 
     dict_entry RECORD; 
     from_replace_pattern VARCHAR; 
     to_replace_pattern VARCHAR; 
--  dictionary CURSOR FOR SELECT from_field AS d_in, to_field AS d_out FROM basf_dict; 
     query text; 
    BEGIN 
     query := format('SELECT %I, %I FROM %I;', from_field, to_field, dict_table); 
     replaced := to_replace; 

     FOR dict_entry IN EXECUTE query LOOP 
      from_replace_pattern := ' ' || dic_entry.d_in || ' '; 
      to_replace_pattern := ' ' || dic_entry.d_out || ' '; 
      replaced := REPLACE(replaced, from_replace_pattern, to_replace_pattern); 
     END LOOP; 
     RETURN replaced; 
    END; 
$$ LANGUAGE plpgsql 

до сих пор не работает, теперь я получаю ошибку последующих при попытке выполнить запрос, который использует эту функцию:

SQL Error [42P01]: ERROR: missing FROM-clause entry for table "dic_entry" 
    Onde: PL/pgSQL function replace_with_dict(character varying,character varying,character varying,character varying) line 14 at assignment 
    org.postgresql.util.PSQLException: ERROR: missing FROM-clause entry for table "dic_entry" 
    Onde: PL/pgSQL function replace_with_dict(character varying,character varying,character varying,character varying) line 14 at assignment 

EDIT 2:

для лучшего объяснения моей мотивации для создания функции, вот что я пытаюсь не делать:

SELECT 
     p.id, 
     p.nom_produto, 
     string_ranking_by_array( 
      REPLACE(
       REPLACE(
        p.nom_produto, 
        'FOS', 
        'FO'), 
       'S B', 
       'S_B'), 
      string_to_array(pe.nom_produto, ' ') 
     ) AS ranking, 
     pe.nom_produto AS nom_pe, 
     pe.ean_produto, 
     pe.id AS id_pe 
    FROM 
     produto p, produto_empresa pe 
    WHERE 1 = 1 
     AND p.id_loja = 23 
     AND(p.ean_produto IS NULL OR p.ean_produto = '') 
     AND CHAR_LENGTH(cod_produto)= 12 
     AND cod_produto LIKE 'SC%' 
    ORDER BY 
     ranking DESC, 
     p.nom_produto 

Я не хочу делать больше внутренних замен для каждого нового улучшения, которое я могу найти.

+0

Первая: строковые константы нужны одинарные кавычки, а не двойные кавычки: https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS, а во-вторых: вы не можете использовать переменную в виде таблицы имя подобное. Для этого вам нужен динамический SQL. –

ответ

1

Две проблемы с вашей функции:

  • название цикла переменная равна dict_entry, пока вы пытаетесь использовать dic_entry;
  • столбцы d_in и d_out неизвестны, если вы не определили их в запросе как псевдонимы.

Кроме того, вы должны выбрать из таблицы словаря только строки, соответствующие отдельным словам входной строки, чтобы уменьшить количество циклов. Используйте также regexp_replace() вместо replace(), чтобы заменить только целые слова (ваша попытка с пробелами не будет работать должным образом). Выходы \m и \M означают начало и конец слова, см. the documentation.

CREATE OR REPLACE FUNCTION replace_with_dict 
    (to_replace VARCHAR, dict_table VARCHAR, from_field VARCHAR, to_field VARCHAR) 
RETURNS VARCHAR AS $$ 
    DECLARE 
     dict_entry RECORD; 
     query text; 
     pattern text; 
     words text[]; 
    BEGIN 
     words := string_to_array(to_replace, ' '); 
     query := format(
      'SELECT %I AS d_in, %I AS d_out FROM %I WHERE %I = ANY(%L);', 
      from_field, to_field, dict_table, from_field, words 
      ); 
     FOR dict_entry IN EXECUTE query LOOP 
      pattern := format('\m%s\M', dict_entry.d_in); 
      to_replace := regexp_replace(to_replace, pattern, dict_entry.d_out, 'g'); 
     END LOOP; 
     RETURN to_replace; 
    END; 
$$ LANGUAGE plpgsql; 

See this working example.

Если вы не заботитесь о целых слов и хотите заменить любую подстроку (возможно с пробелами внутри), используйте простой replace() без дополнительных пространств:

CREATE OR REPLACE FUNCTION replace_with_dict_simple 
    (to_replace VARCHAR, dict_table VARCHAR, from_field VARCHAR, to_field VARCHAR) 
RETURNS VARCHAR AS $$ 
    DECLARE 
     dict_entry RECORD; 
     query text; 
    BEGIN 
     query := format(
      'SELECT %I AS d_in, %I AS d_out FROM %I;', 
      from_field, to_field, dict_table, from_field 
      ); 
     FOR dict_entry IN EXECUTE query LOOP 
      to_replace := replace(to_replace, dict_entry.d_in, dict_entry.d_out); 
     END LOOP; 
     RETURN to_replace; 
    END; 
$$ LANGUAGE plpgsql; 
+0

Прежде всего, большое спасибо за ваш ответ. У меня были случаи, когда они работали так, как предполагалось. http://rextester.com/AEE28978 Я верю, что у него есть что-то со словами с пробелами. Не могли бы вы помочь мне улучшить его? – wviana

+1

Да, первая функция (которая разбивает входную строку на слова) не работает должным образом в вашем примере. Если в искомых словах есть пробелы, то второй вариант ('replace_with_dict_simple()') подходит, как указано в ответе. [См. Этот тест] (http://rextester.com/TCFWB56806). – klin

+0

спасибо. Я пропустил набрав при тестировании второго. Огромное спасибо. Это упростит мой запрос. – wviana

0

-л, как это:

t=# create table so6(d_from text,d_to text); 
CREATE TABLE 
t=# insert into so6 select 'street', 'calle'; 
INSERT 0 1 
t=# CREATE OR REPLACE FUNCTION replace_with_dict(to_replace VARCHAR, dict_table regclass, from_field VARCHAR, to_field VARCHAR) 
t-# RETURNS VARCHAR AS $$ 
t$#  DECLARE 
t$#   _r text; 
t$#   _l record; 
t$#  BEGIN 
t$#  _r = to_replace; 
t$#  for _l in (select unnest(string_to_array(to_replace, ' ')) w) loop 
t$#   execute (format('SELECT coalesce(replace($s$%s$s$,%I,%I),$s$%s$s$) FROM %I WHERE %I = $v$%s$v$;', to_replace,from_field, to_field, _r,dict_table, from_field,_l.w)) into _r; 
t$#   if _r is not null then 
t$#   to_replace = _r; 
t$#   end if; 
t$#  end loop; 
t$#  return to_replace; 
t$#  END; 
t$# $$ LANGUAGE plpgsql 
t-# ; 
CREATE FUNCTION 
Time: 7.404 ms 
t=# select replace_with_dict('go to street "LA Palma"','so6'::regclass,'d_from','d_to') 
t-# ; 
    replace_with_dict 
------------------------ 
go to calle "LA Palma" 
(1 row) 

Time: 0.938 ms 

Но я думаю, что вы должны думать, что если вы действительно хотите, чтобы делать такие вещи

+0

В моем случае я пытаюсь сделать что-то, где вы бы передали 'replace_with_dict ('test street', so6 :: regclass, d_from, d_to)' (используя ваш пример 'so6'), что приведет к' test calle 'выход. Разве я не поняла? Пожалуйста, если не сказать мне, я попытаюсь улучшить свой вопрос. – wviana

+0

Пояснение, почему использовать% I вместо% s [Postgre FUNCTIONS-STRING-FORMAT] (https://www.postgresql.org/docs/9.3/static/functions-string.html#FUNCTIONS-STRING-FORMAT) – wviana

+0

I «Я уже делаю что-то вроде этого« REPLACE »(p.nom_produto,« S B »,« S_B ») в моем запросе, и мне придется сделать больше. Вот почему я делаю эту замену. Если у вас есть предложение для нескольких заметок, я был бы рад услышать. – wviana

 Смежные вопросы

  • Нет связанных вопросов^_^