Postgres视图中的默认值,触发代替更新

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Postgres视图中的默认值,触发代替更新相关的知识,希望对你有一定的参考价值。

我打算用instead of insert触发器查看视图。但是插入默认值似乎存在问题。

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

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

这会返回一个

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

即使底层的data.usersnot 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();
答案

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

如果您有一个具有NULL值的字段,并且您希望在DEFAULT上获得INSERT值,则应在执行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;
另一答案

我知道,在比赛的后期,我有额外的洞察力,我想分享。

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

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

但是,我想指出帕特里克的不同解决方案。确实,你可以在plpgsql函数中手工编写IF检查代码(基本上是重新实现默认值逻辑!)。但从DRY的角度来看,这可能会感觉不是最佳的。当我遇到你的问题时,这是我的关键:

TL / DR

构建NEW变量时,将遵循视图本身的默认值。因此,如果你做ALTER TABLE <view_name> ALTER is_admin SET DEFAULT false它应该导致false传入NEW.is_admin,而不是NULL

希望有所帮助!

以上是关于Postgres视图中的默认值,触发代替更新的主要内容,如果未能解决你的问题,请参考以下文章

使用 javascript/jquery 触发 onchange 事件时更新 DOM 中的哈希值

检测 postgres 更新触发器中的列更改

Hibernate - 在 postgres 中带有触发器的 saveAndFlush

基于 Postgres 触发器的分区,插入 NEW.* 给出值 (10,,,)

SQL 触发器更新视图中的多个列

索引视图更新时