PostgreSQL plpgsql - 可变列名
PostgreSQL plpgsql - variable column names
我正在创建一个触发器,它使用列的动态名称
NEW.name:=2222; -- works fine !
但是
dynamic_column:='name';
EXECUTE '.'||dynamic_column||':=2222 ' USING NEW; -- raises error
报错:
ERROR: syntax error at or near "" LINE 1: .name:=2222
问题是这不是一个有效的 SQL 语句。
您可以像这样使用动态 SQL 访问新的列:
EXECUTE 'SELECT .id' INTO v_id USING NEW;
在 NEW
.
中,没有像那种更改单个列的舒适方式table
您可以使用TG_RELID
获取table的OID,查询pg_attribute
的列,组成一个由NEW
中的值组成的行文字字符串和您的新值,将其转换为 table 类型并将结果分配给 NEW
。挺麻烦的。
这是执行此操作的示例代码(我对其进行了测试,但可能存在错误):
CREATE OR REPLACE FUNCTION dyntrig() RETURNS trigger
LANGUAGE plpgsql AS
$$DECLARE
colname text;
colval text;
newrow text := '';
fieldsep text := 'ROW(';
BEGIN
/* loop through the columns of the table */
FOR colname IN
SELECT attname
FROM pg_catalog.pg_attribute
WHERE attrelid = TG_RELID
AND attnum > 0
AND NOT attisdropped
ORDER BY attnum
LOOP
IF colname = 'name' THEN
colval = '2222';
ELSE
/* all other columns than 'name' retain their value */
EXECUTE 'SELECT CAST(.' || quote_ident(colname) || ' AS text)'
INTO colval USING NEW;
END IF;
/* compose a string that represents the new table row */
IF colval IS NULL THEN
newrow := newrow || fieldsep || 'NULL';
ELSE
newrow := newrow || fieldsep || '''' || colval || '''';
END IF;
fieldsep := ',';
END LOOP;
newrow := newrow || ')';
/* assign the new table row to NEW */
EXECUTE 'SELECT (CAST(' || newrow || ' AS '
|| quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME)
|| ')).*'
INTO NEW;
RETURN NEW;
END;$$;
我在这里找到了信息:Assign to NEW by key in a Postgres trigger
如果我们通过以下方式启用模块 hstore:
CREATE EXTENSION hstore;
我们可以这样做:
dynamic_column:='name';
temp_sql_string:='"'||dynamic_column||'"=>"2222"';
NEW := NEW #= temp_sql_string::hstore;
并且 RECORD NEW.name 现在设置为值 2222。
感谢您努力寻找解决方案@Laurenz Albe
您已经找到 my answer recommending the hstore
operator #=
on dba.SE。您可能还对 SO 上相应的参考答案感兴趣:
- How to set value of composite variable field using dynamic SQL
因为你从变量构造辅助 hstore
值我建议简单函数 hstore()
:
CREATE OR REPLACE FUNCTION dyn_trigger_func()
RETURNS TRIGGER AS
$func$
DECLARE
dyn_col_name text := 'name';
dyn_col_val text := '2222';
BEGIN
NEW := NEW #= hstore(dyn_col_name, dyn_col_val);
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
这种方式更快/更简单/更清晰/更安全。
或者,由于它显然是一个触发器函数,您可能想要在CREATE TRIGGER
语句中传递列名和值:
CREATE OR REPLACE FUNCTION dyn_trigger_func()
RETURNS TRIGGER AS
$func$
BEGIN
NEW := NEW #= hstore(TG_ARGV[0], TG_ARGV[1]);
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
并且:
CREATE TRIGGER ins_bef
BEFORE INSERT ON tbl
FOR EACH ROW EXECUTE PROCEDURE dyn_trigger_func('name', '2222');
提供不带引号且区分大小写的列名。
相关:
- Get values from varying columns in a generic trigger
我正在创建一个触发器,它使用列的动态名称
NEW.name:=2222; -- works fine !
但是
dynamic_column:='name';
EXECUTE '.'||dynamic_column||':=2222 ' USING NEW; -- raises error
报错:
ERROR: syntax error at or near "" LINE 1: .name:=2222
问题是这不是一个有效的 SQL 语句。
您可以像这样使用动态 SQL 访问新的列:
EXECUTE 'SELECT .id' INTO v_id USING NEW;
在 NEW
.
您可以使用TG_RELID
获取table的OID,查询pg_attribute
的列,组成一个由NEW
中的值组成的行文字字符串和您的新值,将其转换为 table 类型并将结果分配给 NEW
。挺麻烦的。
这是执行此操作的示例代码(我对其进行了测试,但可能存在错误):
CREATE OR REPLACE FUNCTION dyntrig() RETURNS trigger
LANGUAGE plpgsql AS
$$DECLARE
colname text;
colval text;
newrow text := '';
fieldsep text := 'ROW(';
BEGIN
/* loop through the columns of the table */
FOR colname IN
SELECT attname
FROM pg_catalog.pg_attribute
WHERE attrelid = TG_RELID
AND attnum > 0
AND NOT attisdropped
ORDER BY attnum
LOOP
IF colname = 'name' THEN
colval = '2222';
ELSE
/* all other columns than 'name' retain their value */
EXECUTE 'SELECT CAST(.' || quote_ident(colname) || ' AS text)'
INTO colval USING NEW;
END IF;
/* compose a string that represents the new table row */
IF colval IS NULL THEN
newrow := newrow || fieldsep || 'NULL';
ELSE
newrow := newrow || fieldsep || '''' || colval || '''';
END IF;
fieldsep := ',';
END LOOP;
newrow := newrow || ')';
/* assign the new table row to NEW */
EXECUTE 'SELECT (CAST(' || newrow || ' AS '
|| quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME)
|| ')).*'
INTO NEW;
RETURN NEW;
END;$$;
我在这里找到了信息:Assign to NEW by key in a Postgres trigger
如果我们通过以下方式启用模块 hstore:
CREATE EXTENSION hstore;
我们可以这样做:
dynamic_column:='name';
temp_sql_string:='"'||dynamic_column||'"=>"2222"';
NEW := NEW #= temp_sql_string::hstore;
并且 RECORD NEW.name 现在设置为值 2222。
感谢您努力寻找解决方案@Laurenz Albe
您已经找到 my answer recommending the hstore
operator #=
on dba.SE。您可能还对 SO 上相应的参考答案感兴趣:
- How to set value of composite variable field using dynamic SQL
因为你从变量构造辅助 hstore
值我建议简单函数 hstore()
:
CREATE OR REPLACE FUNCTION dyn_trigger_func()
RETURNS TRIGGER AS
$func$
DECLARE
dyn_col_name text := 'name';
dyn_col_val text := '2222';
BEGIN
NEW := NEW #= hstore(dyn_col_name, dyn_col_val);
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
这种方式更快/更简单/更清晰/更安全。
或者,由于它显然是一个触发器函数,您可能想要在CREATE TRIGGER
语句中传递列名和值:
CREATE OR REPLACE FUNCTION dyn_trigger_func()
RETURNS TRIGGER AS
$func$
BEGIN
NEW := NEW #= hstore(TG_ARGV[0], TG_ARGV[1]);
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
并且:
CREATE TRIGGER ins_bef
BEFORE INSERT ON tbl
FOR EACH ROW EXECUTE PROCEDURE dyn_trigger_func('name', '2222');
提供不带引号且区分大小写的列名。
相关:
- Get values from varying columns in a generic trigger