neg*_*nbe 6 postgresql triggers primary-key
我是 Postgresql 新手,所以如果我的问题没有意义,请原谅。
我正在尝试找到一种方法将我的数据库结构迁移到Postgresql,特别是,我发现函数非常方便,并且希望使我的众多触发器更容易编写。
在我的数据库中,我使用标准last_modified和last_modified_by字段来跟踪更改。我还使用带有增量序列的标准主键。
有内置语法可将序列链接到主键 ID,但由于last_modified无论如何我都必须为字段编写触发器,所以我想知道是否可以有一个通用函数来一次更新所有内容。
示例:表ANIMAL有字段AMIMAL_ID(主键,带有序列SEQ_ANIMAL)、字段LAST_MODIFIED和LAST_MODIFIED_BY。
同样,我有一个PLANT包含字段PLANT_ID(主键,带有序列SEQ_PLANT)、字段LAST_MODIFIED等等的表LAST_MODIFIED_BY。
我想创建一个通用函数,在我需要创建的 4 个触发器中调用。我希望得到这样的东西:
插入函数之前:
CREATE FUNCTION TRIGGER_BI(p_pkField text, p_Sequence text) RETURNS TRIGGER AS $$
DECLARE
curtime timestamp := now();
BEGIN
NEW.LAST_UPDATED := curtime;
NEW.LAST_UPDATED_BY := current_user;
NEW.p_pkField := nextval(p_Sequence);
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE
SECURITY DEFINER;
Run Code Online (Sandbox Code Playgroud)
更新前功能:
CREATE FUNCTION TRIGGER_BU() RETURNS TRIGGER AS $$
DECLARE
curtime timestamp := now();
BEGIN
NEW.LAST_UPDATED := curtime;
NEW.LAST_UPDATED_BY := current_user;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE
SECURITY DEFINER;
Run Code Online (Sandbox Code Playgroud)
现在,表的触发器ANIMAL:插入之前:
CREATE TRIGGER ANIMAL
BEFORE INSERT
FOR EACH ROW EXECUTE PROCEDURE TRIGGER_BI("ANIMAL_ID", "SEQ_ANIMAL");
Run Code Online (Sandbox Code Playgroud)
更新前:
CREATE TRIGGER ANIMAL
BEFORE UPDATE
FOR EACH ROW EXECUTE PROCEDURE TRIGGER_BU();
Run Code Online (Sandbox Code Playgroud)
表的触发器PLANT:插入之前:
CREATE TRIGGER PLANT
BEFORE INSERT
FOR EACH ROW EXECUTE PROCEDURE TRIGGER_BI("PLANT_ID", "SEQ_PLANT");
Run Code Online (Sandbox Code Playgroud)
更新前:
CREATE TRIGGER PLANT
BEFORE UPDATE
FOR EACH ROW EXECUTE PROCEDURE TRIGGER_BU();
Run Code Online (Sandbox Code Playgroud)
是否有可能以任何方式获得通用的东西?
是的!正确的语法是什么?额外奖励:很可能有一个函数来完成所有工作,并且默认为空参数,如果为空,则不会更新序列。
是的,但是等等!这种方法有哪些缺点?(性能、安全性、还有其他需要考虑的因素)?
不!那么我真的需要每个触发器的功能吗?
更新: 我显式创建序列,因为我可能希望在多个表之间共享序列。这个想法是使用共享序列作为唯一的父表,其中多个子表的主键上有一个外键到父表。请毫不犹豫地评论这种方法,但我的基本理解是访问序列的下一个值比管理外键要有效得多......
更新 2:
我发现了一些非常有趣的东西,几乎让我到达那里 - 只是我的setValue功能不起作用......
这里是通用触发器:
CREATE OR REPLACE FUNCTION TRIGGER_FUNC() RETURNS TRIGGER AS $$
DECLARE
p_pkField text;
p_Sequence text;
pkValue int;
BEGIN
EXECUTE format('SELECT ($1).%I::int', TG_ARGV[0]) USING NEW INTO pkValue;
p_Sequence := quote_ident(TG_ARGV[1]);
IF pkValue IS NULL THEN
SELECT setfieldValue(pg_typeof(NEW), TG_ARGV[0], nextval(p_Sequence));
END IF;
NEW.LAST_UPDATED := curtime;
NEW.LAST_UPDATED_BY := current_user;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE
SECURITY DEFINER;
Run Code Online (Sandbox Code Playgroud)
我在这里setValue找到了该函数解决方案的提示并尝试对其进行调整,但它不起作用 - 我只是使用了错误的调用吗?或者我可以在方法中使用一些额外的知识来使其更简单吗?(我已经使用了我正在设置一个值的事实,但我也许可以做得更好?!)bigint
这是(非工作)代码:
CREATE OR REPLACE FUNCTION public.setfieldValue(anyelement, text, bigint)
RETURNS anyelement
LANGUAGE plpgsql
AS $function$
DECLARE
_name text;
_values text[];
_value text;
_attnum int;
BEGIN
FOR _name, _attnum
IN SELECT a.attname, a.attnum
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = (SELECT typrelid
FROM pg_type
WHERE oid = pg_typeof($1)::oid)
LOOP
IF _name = $2 THEN
_value := $3;
ELSE
EXECUTE 'SELECT (($1).' || quote_ident(_name) || ')::text' INTO _value USING $1;
END IF;
_values[_attnum] := COALESCE('"' || replace(replace(_value, '"', '""'), '''', '''''') || '"', '');
END LOOP;
EXECUTE 'SELECT (' || quote_ident(pg_typeof($1)::text) || ' ''(' || array_to_string(_values,',') || ')'').*' INTO $1;
RETURN $1;
END;
$function$;
Run Code Online (Sandbox Code Playgroud)
否用于手动设置主键的默认值。将主键声明为serial( 或bigserial) 并使用内置机制来处理此类列。不必担心主键的值不连续。主键仅用于明确标识行,仅此而已。
除此之外,您不能以这种方式执行此操作,因为触发器函数不能声明参数。
是,用于设置许多表中公共列的值。您可以使用相同的触发器函数进行插入和更新。例子:
CREATE OR REPLACE FUNCTION generic_trigger() -- function without arguments
RETURNS TRIGGER AS $$
BEGIN
NEW.last_modified := now();
NEW.last_modified_by := current_user;
RETURN NEW; -- important!
END;
$$
LANGUAGE 'plpgsql';
create table table_a
(a_id serial primary key, last_modified timestamp, last_modified_by text);
create table table_b
(b_id serial primary key, last_modified timestamp, last_modified_by text);
create trigger table_a_trigger
before insert or update on table_a
for each row execute procedure generic_trigger();
create trigger table_b_trigger
before insert or update on table_b
for each row execute procedure generic_trigger();
insert into table_a default values;
select * from table_a;
a_id | last_modified | last_modified_by
------+-------------------------+------------------
1 | 2015-10-26 19:14:34.642 | postgres
(1 row)
Run Code Online (Sandbox Code Playgroud)
也许您有非常重要的理由在触发器中设置主键的值(请参阅 jpmc26 的评论)。在这种情况下,主键应声明为integer( bigint) 且不default expression带触发器函数应如下所示:
create or replace function generic_trigger()
returns trigger as $$
begin
new.last_modified := now();
new.last_modified_by := current_user;
if tg_op = 'INSERT' then
if tg_table_name = 'table_a' then
new.a_id:= nextval('table_a_a_id_seq');
elsif tg_table_name = 'table_b' then
new.b_id:= nextval('table_b_b_id_seq');
end if;
end if;
return new;
end;
$$
language 'plpgsql';
insert into table_a (a_id) values (15);
select * from table_a;
a_id | last_modified | last_modified_by
------+-------------------------+------------------
1 | 2015-10-26 19:14:34.642 | postgres
2 | 2015-10-26 21:15:49.243 | postgres
(2 rows)
Run Code Online (Sandbox Code Playgroud)
阅读有关触发程序的更多信息。
| 归档时间: |
|
| 查看次数: |
5517 次 |
| 最近记录: |