我计划使用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();
答案 0 :(得分:3)
在触发器函数中,NEW
和OLD
隐式参数始终包含基础表或视图的所有字段,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;
答案 1 :(得分:2)
我知道,在比赛的后期,但我想要分享更多的洞察力。
请注意,正如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
。
希望有所帮助!