Postgres 视图中的默认值触发而不是更新
Default values in Postgres view with triggered instead-of update
我正计划使用 instead of insert
触发器查看。不过插入默认值似乎有问题。
触发器设置如下,以下查询失败
INSERT INTO v1.clients (foo) VALUES ('bar')
这个returns一个
null value in column "is_admin" violates not-null constraint
即使基础 data.users
(not null
) table 设置了默认值。
我想说的是视图将所有缺失值转换为 null
并应用 instead of
,试图将 null
插入到 not null default false
列.
我能否以某种方式设置触发器而不是尝试插入 null 来插入默认值?在触发器的相关插入中有 coalesce(NEW.is_admin, default)
是语法错误。我宁愿不在触发器中手动复制默认值。
postgres 支持吗? 在允许默认值的情况下,将两个 table 的视图拆分为那些 table 的最佳方法是什么?
定义:
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();
在触发器函数中,NEW
和 OLD
隐式参数始终包含基础 table 或视图的所有字段,NULL
分配给字段没有可用数据。这是正确的行为,否则您永远无法为没有数据的字段赋值。
如果您有一个具有 NULL
值的字段,并且您想在 INSERT
上获得 DEFAULT
值,您应该在执行 [=] 之前对其进行测试15=]:
CREATE FUNCTION data.separate_client_user_data() RETURNS trigger AS $$
DECLARE
<b>cid clients.id%TYPE; -- Don't use variable with same name as a column</b>
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 使用的新变量包含所有字段 - 为最初不属于 INSERT 语句的任何字段添加了 NULL。
正如您所指出的,基础 table 对 is_admin
字段有一个默认值,不幸的是,该默认值没有机会被看到,因为 NEW 变量已经有 NEW.is_admin = NULL。
但是,我想指出一个不同于 Patrick 的解决方案。的确,您可以在 plpgsql 函数中手动编写 IF 检查(本质上是重新实现默认值逻辑!)。但从 DRY 的角度来看,这可能感觉不是最佳的。当我遇到你的问题时,这是我的关键:
TL/DR
视图本身的默认值在构建新变量时得到尊重。因此,如果您执行 ALTER TABLE <view_name> ALTER is_admin SET DEFAULT false
,它应该会导致 false
传递给 NEW.is_admin
,而不是 NULL
。
希望对您有所帮助!
我正计划使用 instead of insert
触发器查看。不过插入默认值似乎有问题。
触发器设置如下,以下查询失败
INSERT INTO v1.clients (foo) VALUES ('bar')
这个returns一个
null value in column "is_admin" violates not-null constraint
即使基础 data.users
(not null
) table 设置了默认值。
我想说的是视图将所有缺失值转换为 null
并应用 instead of
,试图将 null
插入到 not null default false
列.
我能否以某种方式设置触发器而不是尝试插入 null 来插入默认值?在触发器的相关插入中有 coalesce(NEW.is_admin, default)
是语法错误。我宁愿不在触发器中手动复制默认值。
postgres 支持吗? 在允许默认值的情况下,将两个 table 的视图拆分为那些 table 的最佳方法是什么?
定义:
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();
在触发器函数中,NEW
和 OLD
隐式参数始终包含基础 table 或视图的所有字段,NULL
分配给字段没有可用数据。这是正确的行为,否则您永远无法为没有数据的字段赋值。
如果您有一个具有 NULL
值的字段,并且您想在 INSERT
上获得 DEFAULT
值,您应该在执行 [=] 之前对其进行测试15=]:
CREATE FUNCTION data.separate_client_user_data() RETURNS trigger AS $$
DECLARE
<b>cid clients.id%TYPE; -- Don't use variable with same name as a column</b>
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 使用的新变量包含所有字段 - 为最初不属于 INSERT 语句的任何字段添加了 NULL。
正如您所指出的,基础 table 对 is_admin
字段有一个默认值,不幸的是,该默认值没有机会被看到,因为 NEW 变量已经有 NEW.is_admin = NULL。
但是,我想指出一个不同于 Patrick 的解决方案。的确,您可以在 plpgsql 函数中手动编写 IF 检查(本质上是重新实现默认值逻辑!)。但从 DRY 的角度来看,这可能感觉不是最佳的。当我遇到你的问题时,这是我的关键:
TL/DR
视图本身的默认值在构建新变量时得到尊重。因此,如果您执行 ALTER TABLE <view_name> ALTER is_admin SET DEFAULT false
,它应该会导致 false
传递给 NEW.is_admin
,而不是 NULL
。
希望对您有所帮助!