基于另一列中的值的列约束

Rud*_*lan 6 postgresql trigger database-design constraint check-constraints

我有一个 PostgreSQL 表phase_steps,包含以下示例行:

phase_step_id|step_type|step_status|
-------------+---------+-----------+
            1| RESEARCH|           |
            2|   SURVEY|           |
        
Run Code Online (Sandbox Code Playgroud)

更新step_status列的值取决于该值 step_type是什么。
step_type为“RESEARCH”时,只能输入“COMPLETE”或“INCOMPLETE”值step_status
step_type为“SURVEY”时,只能输入“ASSIGNED”或“NOT ASSIGNED”值step_status

step_status我尝试通过以下过程管理“研究”约束:

create or replace function insert_step_status_value() returns trigger as $$
    begin
        if (new.step_status != 'COMPLETE') or (new.step_status != 'INCOMPLETE')
        from phase_steps where step_type = 'RESEARCH'
        then
            raise exception 'Status value not in range for this phase step';
        end if;
        return new;
    end;
$$ language plpgsql;

create trigger check_step_status_value before update on phase_steps
for each row execute procedure insert_step_status_value();
Run Code Online (Sandbox Code Playgroud)

然而,像这样的插入

update jobs.phase_steps
set step_status_lu = 'INCOMPLETE'
where phase_step_id = 1;
Run Code Online (Sandbox Code Playgroud)

给出错误:

SQL Error [P0001]: ERROR: Status value not in range for this phase step
Where: PL/pgSQL function insert_step_status_value() line 6 at RAISE
Run Code Online (Sandbox Code Playgroud)

想法?

Erw*_*ter 7

约束CHECK可以完成这项工作。比触发器更简单、更便宜、更可靠。

要强制执行列出的组合,您的表定义可能如下所示:

CREATE TABLE jobs.phase_steps (
  phase_step_id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, step_type     text
, step_status   text
, CONSTRAINT    step_status_for_step_type CHECK (
      step_type = 'RESEARCH' AND step_status IN ('COMPLETE', 'INCOMPLETE')
   OR step_type = 'SURVEY'   AND step_status IN ('ASSIGNED', 'NOT ASSIGNED')
      )
);
Run Code Online (Sandbox Code Playgroud)

或者更简单的等价形式:

, CONSTRAINT step_status_for_step_type CHECK (
       (step_type, step_status)
   IN (('RESEARCH', 'COMPLETE')
     , ('RESEARCH', 'INCOMPLETE')
     , ('SURVEY'  , 'ASSIGNED')
     , ('SURVEY'  , 'NOT ASSIGNED')))
Run Code Online (Sandbox Code Playgroud)

小提琴

手册:

定义约束有两种方式:表约束和列约束。列约束被定义为列定义的一部分。表约束定义不依赖于特定列,并且它可以包含多个列。

运算符优先级对我们有利,不需要额外的括号。
不允许(step_type, step_status)-除了nullvalue之外的任何其他组合值。CHECK如果表达式的计算结果为trueor ,则传递约束null

在这种情况下,您可能想要向 添加NOT NULL约束step_type,那么step_status仍然可以是null,并且step_type可以是任何内容(但是)。null但如果step_status有值,则组合必须通过检查。

或者,也不允许 的任何其他值step_type

ALTER TABLE phase_steps
  DROP CONSTRAINT step_status_for_step_type
, ADD  CONSTRAINT step_status_for_step_type CHECK (
         CASE step_type WHEN 'RESEARCH' THEN step_status IN ('COMPLETE', 'INCOMPLETE')
                        WHEN 'SURVEY'   THEN step_status IN ('ASSIGNED', 'NOT ASSIGNED')
                        ELSE false END);
Run Code Online (Sandbox Code Playgroud)

现在step_type(包括null)的任何其他值都会到达ELSE分支并进行检查false。(但null仍然step_status过去了。)

小提琴