2016-08-02 3 views
3

У меня возникла проблема с созданием триггера PostgreSQL (9.3) для таблицы обновлений. Я хочу установить новые значения в цикле, какТриггер с именем динамического поля

EXECUTE 'NEW.'|| fieldName || ':=''some prepend data'' || NEW.' || fieldName || ';'; 

где FIELDNAME устанавливается динамически. Но эта строка поднять ошибка

ERROR: syntax error at or near "NEW" 

Как я могу идти о достижении этого?

+0

Было бы гораздо полезнее, чтобы обеспечить полную * * триггер функции и определение триггера, чтобы пойти с ним. –

ответ

0

Я нашел рабочее решение: триггер должен выполняться после вставки/обновления, а не раньше.Затем желаемая строка принимает вид

EXECUTE 'UPDATE ' || TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME || 
       ' SET ' || fieldName || '= ''prefix:'' ||''' || fieldValue || ''' WHERE id = ' || NEW.id; 

FIELDNAME и fieldValue я получаю следующий способ:

FOR fieldName,fieldValue IN select key,value from each(hstore(NEW)) LOOP 
     IF .... THEN 
END LOOP: 
1

Ваша проблема заключается в том, что EXECUTE может использоваться только для выполнения операторов SQL, а не для операторов PL/pgSQL, таких как задание в вашем вопросе.

Вы можете возможно обойти, что, как это:

Предположим, что таблица testtab определяется следующим образом:

CREATE TABLE testtab (
    id integer primary key, 
    val text 
); 

Тогда функция триггера, подобная следующей будет работать:

BEGIN 
    EXECUTE 'SELECT $1.id, ''prefix '' || $1.val' INTO NEW USING NEW; 
    RETURN NEW; 
END; 

В моем примере я использовал закодированные id и val, но это не обязательно.

+0

Спасибо, очень полезный пример. Но в моем случае имена полей меняются динамически, и я не могу скомпоновать их с триггером. Мне нужно подставить имя поля как NEW. $ 1 для образца. Могу ли я написать EXECUTE 'SELECT $ 1. *,' ​​'prefix' '|| $ 1. $ 2 'В НОВОЕ ИСПОЛЬЗОВАНИЕ NEW, fieldName; ? – FlyBot

+1

Нет, но вы можете использовать интроспекцию (например, с 'information_schema.columns') для построения строки, а затем использовать ее в' EXECUTE'. –

4

Вы можете осуществить это довольно удобно с hstore оператором #=:

Убедитесь, что дополнительный модуль правильно установлен (раз в базе данных), в схеме, которая включена в ваш search_path:

Trigger функция:

CREATE OR REPLACE FUNCTION tbl_insup_bef() 
    RETURNS TRIGGER AS 
$func$ 
DECLARE 
    _prefix CONSTANT text := 'some prepend data'; -- your prefix here 
    _prelen CONSTANT int := 17; -- length of above string (optional optimization) 
    _col text := quote_ident(TG_ARGV[0]); 
    _val text; 
BEGIN 
    EXECUTE 'SELECT $1.' || _col 
    USING NEW 
    INTO _val; 

    IF left(_val, _prelen) = _prefix THEN 
     -- do nothing: prefix already there! 
    ELSE 
     NEW := NEW #= hstore(_col, _prefix || _val); 
    END IF; 

    RETURN NEW; 
END 
$func$ LANGUAGE plpgsql; 

Trigger (повторно использовать ту же FUNC для нескольких таблиц):

CREATE TRIGGER insup_bef 
BEFORE INSERT OR UPDATE ON tbl 
FOR EACH ROW 
EXECUTE PROCEDURE tbl_insup_bef('fieldName'); -- unquoted, case-sensitive column name 

тесно связан с более подробным объяснением и советы:

+0

Да, это очень полезный образец. Расширение hstore существует, но оператор # = не найден в расширении. – FlyBot

+1

@FlyBot: Это странно, [оператор определенно включен в версию 9.3] (https://www.postgresql.org/docs/9.3/static/hstore.html#HSTORE-OP-TABLE). Вы уверены, что установили его в * вашей базе данных *, а схема включена в ваш 'search_path'? Я добавил ссылки с подробностями. –

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