【问题标题】:Default values in Postgres view with triggered instead-of updatePostgres 视图中的默认值,触发而不是更新
【发布时间】:2015-07-25 20:47:31
【问题描述】:

我正计划使用instead of insert 触发器查看视图。不过插入默认值似乎有问题。

触发器设置如下,以下查询失败

INSERT INTO v1.clients (foo) VALUES ('bar')

这会返回一个

“is_admin”列中的空值违反非空约束

即使底层data.users (not null) 表设置了默认值。

我要说的是视图将所有缺失值转换为null,并应用instead of,试图将null 插入not null default false 列。

我可以以某种方式设置触发器而不是尝试插入 null 来插入默认值吗?在触发器的相关插入中包含coalesce(NEW.is_admin, default) 是语法错误。我宁愿不要在触发器中手动复制默认值。

postgres 支持吗? 将两个表的视图拆分为这些表的最佳方法是什么,同时允许使用默认值?

定义:

CREATE OR REPLACE VIEW v1.clients AS
    SELECT
        c.id, c.foo,
        u.id user_id, u.is_admin
    FROM data.clients c
    INNER JOIN data.users u ON u.client_id = c.id;

CREATE FUNCTION data.separate_client_user_data()
RETURNS TRIGGER AS $$
DECLARE
    client_id clients.id%TYPE;
BEGIN
    INSERT INTO data.clients (foo) VALUES (NEW.foo) RETURNING id INTO client_id;
    INSERT INTO data.users (client_id, is_admin)
        VALUES (client_id, NEW.is_admin);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;


CREATE TRIGGER user_data_trigger
INSTEAD OF INSERT ON v1.clients
FOR EACH ROW EXECUTE PROCEDURE data.separate_client_user_data();

【问题讨论】:

    标签: postgresql triggers plpgsql


    【解决方案1】:

    我知道,我玩得晚了,但我想分享一些额外的见解。

    请注意,正如 Patrick 所说,TRIGGER PROCEDURE 使用的 NEW 变量包含所有字段 - 为最初不属于您的 INSERT 语句的任何字段添加了 NULL。

    正如您所指出的,基础表具有 is_admin 字段的默认值,不幸的是,该默认值没有机会被看到,因为 NEW 变量已经具有 NEW.is_admin = NULL。

    但是,我想指出与 Patrick 不同的解决方案。确实,您可以在 plpgsql 函数中手动编码 IF 检查(本质上是重新实现默认值逻辑!)。但从 DRY 的角度来看,这可能感觉不太理想。当我遇到你的问题时,这是我的关键:

    TL/DR

    视图本身的默认值在构建 NEW 变量时得到尊重。因此,如果您执行ALTER TABLE <view_name> ALTER is_admin SET DEFAULT false,它应该会导致false 被传递给NEW.is_admin,而不是NULL

    希望有帮助!

    【讨论】:

    • 太棒了。谢谢!
    【解决方案2】:

    在触发器函数中,NEWOLD 隐式参数始终包含基础表或视图的所有字段,NULL 分配给没有可用数据的字段。这是正确的行为,否则您永远无法为没有数据的字段赋值。

    如果您有一个具有NULL 值的字段,并且您想在INSERT 上获得DEFAULT 值,则应在执行INSERT 之前对其进行测试:

    CREATE FUNCTION data.separate_client_user_data() RETURNS trigger AS $$
    DECLARE
        cid clients.id%TYPE; -- Don't use variable with same name as a column
    BEGIN
        INSERT INTO data.clients (foo) VALUES (NEW.foo) RETURNING id INTO cid;
        IF NEW.is_admin IS NULL THEN
            INSERT INTO data.users (client_id)
                VALUES (cid);   -- Use default value for is_admin
        ELSE    
            INSERT INTO data.users (client_id, is_admin)
                VALUES (cid, NEW.is_admin);
        END IF;
        RETURN NEW;
    END;
    $$ LANGUAGE plpgsql;

    【讨论】:

      猜你喜欢
      • 2012-10-18
      • 2012-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-07
      相关资源
      最近更新 更多