PostgreSQL:在UPSERT上的唯一性约束之前检查约束验证
目的:我想在唯一性约束验证之后触发检查约束验证 考虑下表定义:PostgreSQL:在UPSERT上的唯一性约束之前检查约束验证,postgresql,unique-constraint,upsert,check-constraints,postgresql-11,Postgresql,Unique Constraint,Upsert,Check Constraints,Postgresql 11,目的:我想在唯一性约束验证之后触发检查约束验证 考虑下表定义: 如果不存在计数器,则创建表( id串行主键, 名称文本不为空且唯一, val INT非空检查(val>=0) ); 此表包含唯一的名称和非负计数器值。 在创建表时,定义了两个约束: counters_val_check - a CHECK constraint that asserts the values of `val` are non negative. counters_name_key - uniqueness cons
如果不存在计数器,则创建表(
id串行主键,
名称文本不为空且唯一,
val INT非空检查(val>=0)
);
此表包含唯一的名称和非负计数器值。
在创建表时,定义了两个约束:
counters_val_check - a CHECK constraint that asserts the values of `val` are non negative.
counters_name_key - uniqueness constraint that asserts the values of `name` are unique.
以下是我的UPSERT查询:
插入计数器(名称、val)值('alex',2)
关于“计数器\u名称\u键”DO约束的冲突
更新集val=counters.val+excluded.val;
当val
的值为正值时-没有问题。但是,当我尝试向上插入一个现有名称的负值时,约束计数器\u val\u check
在约束计数器\u name\u键
之前被验证,查询失败:
INSERT INTO counters (name, val) VALUES ('alex', -2)
ON CONFLICT ON CONSTRAINT "counters_name_key" DO
UPDATE SET val = counters.val + excluded.val;
--------------------------------------------------------------------------------
Query 1 ERROR: ERROR: new row for relation "counters" violates check constraint "counters_val_check"
因为我知道负数val
更新应该只对现有名称进行,所以这种负数更新是完全正确的。我想要实现的是翻转验证顺序,这样只有在验证通过后,counters\u val\u check
才会被验证
笔记
- 最初我想在
块中捕获这两个约束,但我没有找到任何方法INSERT
- 我能想到的最好方法是延迟约束触发器,而不是检查约束:
CREATE FUNCTION howl_on_negative_val() RETURNS trigger
LANGUAGE plpgsql AS
$$BEGIN
IF NEW.val < 0 THEN
RAISE EXCEPTION '"val" cannot be negative';
END IF;
RETURN NEW
END;$$;
CREATE CONSTRAINT TRIGGER howl_on_negative_val
AFTER INSERT OR UPDATE ON counters
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
EXECUTE FUNCTION howl_on_negative_val();
CREATE FUNCTION howl\u on\u negative\u val()返回触发器
语言plpgsql AS
$$开始
如果NEW.val<0,则
引发异常“val”不能为负;
如果结束;
还新
完(元);
在负值上创建约束触发器嚎叫
在计数器上插入或更新后
可延期最初延期
每行
执行函数howl_on_negative_val();
这样的触发器将在事务结束时触发,因此将在主键之后检查条件。是否有任何理由不使用upsert进行递增,而使用单独的
UPDATE
语句进行递减?主要原因是在执行upsert之前,我不知道行是否存在。我希望避免选择获取存在的行,然后插入和更新,因为这是对性能敏感的代码,我希望避免三次查询。此外,我考虑使用子查询获取存在的任何行(依赖于唯一性约束),然后使用此信息更新存在的行并插入不存在的行。在我的例子中,这也是有问题的,因为这样我只能看到上一个子查询中受影响的行数,无法有效地验证受影响的行数是否正确。您编写了“负val
更新应该只对现有名称进行”,因此您似乎知道这些名称。或者val
是在查询内动态计算的吗?val
的值是在查询外动态计算的。我想找到一种优雅的方法,不使用依赖于val
值的自定义逻辑来进行upsert。