patternsqlMinor
Column constraint based on values in another column
Viewed 0 times
columnanotherbasedconstraintvalues
Problem
I have a PostgreSQL table
Update values to the
When
When
I tried to manage the 'RESEARCH'
However, an insert like
gives an error:
Thoughts?
phase_steps, with the following example rows:phase_step_id|step_type|step_status|
-------------+---------+-----------+
1| RESEARCH| |
2| SURVEY| |Update values to the
step_status column depend on what value thestep_type value is.When
step_type is 'RESEARCH', only values of 'COMPLETE' or 'INCOMPLETE' can be entered for step_status values.When
step_type is 'SURVEY', only values of 'ASSIGNED' or 'NOT ASSIGNED' can be entered for step_status values.I tried to manage the 'RESEARCH'
step_status constraints with this procedure: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();However, an insert like
update jobs.phase_steps
set step_status_lu = 'INCOMPLETE'
where phase_step_id = 1;gives an error:
SQL Error [P0001]: ERROR: Status value not in range for this phase step
Where: PL/pgSQL function insert_step_status_value() line 6 at RAISEThoughts?
Solution
A
To enforce the listed combinations, your table definition could look like this:
Or a simpler equivalent:
fiddle
The manual:
There are two ways to define constraints: table constraints and column
constraints. A column constraint is defined as part of a column
definition. A table constraint definition is not tied to a particular
column, and it can encompass more than one column.
Operator precedence works in our favor, no additional parentheses required.
Allows no other combined values for
You may want to add a
Or, to also disallow any other value for
Now any other value for
fiddle
CHECK constraint does the job. Simpler, cheaper and more reliable than a trigger.To enforce the listed combinations, your table definition could look like this:
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')
)
);Or a simpler equivalent:
, CONSTRAINT step_status_for_step_type CHECK (
(step_type, step_status)
IN (('RESEARCH', 'COMPLETE')
, ('RESEARCH', 'INCOMPLETE')
, ('SURVEY' , 'ASSIGNED')
, ('SURVEY' , 'NOT ASSIGNED')))fiddle
The manual:
There are two ways to define constraints: table constraints and column
constraints. A column constraint is defined as part of a column
definition. A table constraint definition is not tied to a particular
column, and it can encompass more than one column.
Operator precedence works in our favor, no additional parentheses required.
Allows no other combined values for
(step_type, step_status) - except for null values. A CHECK constraint is passed if the expression evaluates to true or null.You may want to add a
NOT NULL constraint to step_type, then step_status can still be null, and step_type can be anything (but null) in this case. But if step_status has a value, the combination has to pass the check.Or, to also disallow any other value for
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);Now any other value for
step_type (incl. null) reaches the ELSE branch and makes the check false. (But null in step_status still passes.)fiddle
Code Snippets
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')
)
);, CONSTRAINT step_status_for_step_type CHECK (
(step_type, step_status)
IN (('RESEARCH', 'COMPLETE')
, ('RESEARCH', 'INCOMPLETE')
, ('SURVEY' , 'ASSIGNED')
, ('SURVEY' , 'NOT ASSIGNED')))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);Context
StackExchange Database Administrators Q#297926, answer score: 7
Revisions (0)
No revisions yet.