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。