【问题标题】:How to avoid "blank" insert when inserting data into a view with a trigger & insert procedure?使用触发器和插入过程将数据插入视图时如何避免“空白”插入?
【发布时间】:2017-04-29 00:14:08
【问题描述】:

我正在尝试通过 PostgreSQL 视图上的 insertupdate 调用更新表。这是我所做的一个简化示例:

[Person] table:
id | lastname | firstname | city | age

[Person_View] table:
id | lastname | firstname | city

这里是触发器和相关程序:

CREATE TRIGGER tg_update_person_view
    INSTEAD OF INSERT OR UPDATE OR DELETE ON
       Person_View FOR EACH ROW EXECUTE PROCEDURE update_person_view_table(); 

CREATE OR REPLACE FUNCTION update_person_view_table()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $function$
   BEGIN
      IF TG_OP = 'INSERT' THEN
       INSERT INTO Person (id, lastname, firstname)
        VALUES(NEW.id, NEW.lastname, NEW.firstname);
       RETURN NEW;
      ELSIF TG_OP = 'UPDATE' THEN
       UPDATE Person
        SET id=NEW.id, lastname=NEW.lastname, firstname=NEW.firstname
        WHERE id=OLD.id;
       RETURN NEW;
      END IF;
      RETURN NEW;
    END;
$function$;

如果我这样做:

INSERT INTO Person_View (id, city) VALUES ('3', 'Berlin')

将只有 ID 的行添加到视图和父表中。

如何在过程中检查插入值的列是否在过程中定义了“映射”,如果没有任何映射的列,它不会继续?

【问题讨论】:

  • 在 Postgres 9.3+ 中你不需要规则也不需要触发器,基于单个表的视图是自动更新的。
  • 我知道,我应该说这个简化的例子是自动更新的。我的真实情况是我有基于多个表的视图,我不希望出现这些空记录。

标签: sql postgresql stored-procedures views plpgsql


【解决方案1】:

你可以在表上定义一个检查约束,例如:

create table person(
    id int primary key, 
    lastname text, 
    firstname text, 
    city text, 
    age int,
    check(coalesce(lastname, firstname, city, age::text) is not null)
);

insert into person (id)
values (1);

ERROR:  new row for relation "person" violates check constraint "person_check"
DETAIL:  Failing row contains (1, null, null, null, null).

无论是否创建了任何基于表的视图,该解决方案都有效。

【讨论】:

    【解决方案2】:

    ON DELETE 提供单独的触发和触发功能以简化操作。 (无论如何ON DELETE你什么都没做?)

    klin suggested 这样的CHECK 约束似乎是个好主意。不过,您不需要COALESCE 和铸造。检查 row 值是否为 NULL。

    CHECK (NOT ROW(lastname, firstname) IS NULL)  -- ROW keyword is noise
    

    这会强制行中至少有一个非空值。适用于任意数量的列和任意数据类型。

    请特别注意ROW(lastname, firstname) IS NOT NULL 不一样,不会起作用。详细解释:

    如果CHECK 约束不是一个选项,您可以在触发器中使用相同的表达式 - 这应该比将其添加到触发器函数中更快。 The manual on CREATE TRIGGER:

    此外,触发器定义可以指定布尔 WHEN 条件,该条件 将测试是否应该触发触发器。在 行级触发WHEN 条件可以检查旧的和/或新的 行列的值。

    CREATE TRIGGER tg_update_person_view
    INSTEAD OF INSERT OR UPDATE ON Person_View
    FOR EACH ROW 
    WHEN (NOT (NEW.lastname, NEW.firstname) IS NULL)  -- more columns?
    EXECUTE PROCEDURE update_person_view_table(); 
    

    如果WHEN 表达式的计算结果不为TRUE,则触发函数甚至不会被调用——所以does not proceed 就像请求的那样。


    但是,我错过了你的触发器INSTEAD OFThe manual:

    INSTEAD OF 触发器不支持WHEN 条件。

    在这种情况下,您必须将检查移动到函数体中:

    IF NOT (NEW.lastname, NEW.firstname) IS NULL THEN
      --  do stuff
    END IF;
    

    【讨论】:

    • 我收到此错误:ERROR: INSTEAD OF triggers cannot have WHEN conditions ********** Error **********.
    • @kaycee:哦,对不起。不适用于INSTEAD 触发器。在这种情况下,您必须将支票移动到函数中。 (您考虑过CHECK 约束吗?)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-17
    • 1970-01-01
    • 2013-09-07
    • 2014-08-28
    • 2014-02-02
    • 2011-03-08
    • 1970-01-01
    相关资源
    最近更新 更多