PostgreSQL Custom not null constraint - sql

I want to add a NOT NULL contstraint to one of my table fields but only when another field has a certain value.
So I have a services table:
services:
- id
- professional_id
- is_master
I want to write a SQL constraint that whenever is_master is false professional_id cannot be null. I've tried things like:
CREATE TABLE "services" (
"id" text NOT NULL,
"professional_id" text REFERENCES professionals ON DELETE CASCADE ON UPDATE CASCADE,
EXCLUDE USING gist (
professional_id WITH =,
) WHERE (NOT is_master),
"is_master" boolean NOT NULL DEFAULT false,
PRIMARY KEY ("id")
);
What is the correct way to write this SQL?

You can do this with a check constraint. A simple one is:
constraint chk_services_master_professions
check (is_master or professional_id is not null)
Note: This version assumes that is_master is never NULL -- or that NULL is equivalent to "false".

check (not is_master and professional_id is not null or is_master)

You could use CHECK constraint:
CREATE TABLE "services" (
"id" text NOT NULL,
"professional_id" text
CHECK (CASE WHEN "is_master" IS FALSE AND "professional_id" IS NULL
THEN FALSE ELSE TRUE END), -- and rest of FK
"is_master" boolean NOT NULL DEFAULT false,
PRIMARY KEY ("id")
);
Rextester Demo
INSERT INTO services
VALUES (1,1, FALSE);
INSERT INTO services
VALUES (2,1, true);
INSERT INTO services
VALUES (3, null, true);
INSERT INTO services
VALUES (4, null, false);
23514: new row for relation "services" violates check constraint "services_check"

Related

How can I ensure that Column_A can only have a value if Column_B is NULL? And vice versa

I'm attempting to create a table that has three columns:
id
paid_at
failed_at
How can I make sure that paid_at can only have a value if failed_at is NULL?
Here is my current code:
CREATE TABLE charges(
id TEXT NOT NULL PRIMARY KEY,
paid_at TEXT,
failed_at TEXT
);
ALTER TABLE charges
ADD CONSTRAINT paid_at CHECK (failed_at IS NULL);
ALTER TABLE charges
ADD CONSTRAINT failed_at CHECK (paid_at IS NULL);
I also want to make sure that BOTH cannot be null.
How can I do this?
Thanks!
You can use num_nonnulls() in the check constraint:
alter table charges
add constraint only_one_not_null
check (num_nonnulls(paid_at, failed_at) = 1);
That ensure that exactly one of the columns is not null and the other is null.
If you consider a string with only spaces to be "null" as well, you could extend that to:
alter table charges
add constraint only_one_not_null
check (num_nonnulls(nullif(trim(paid_at),''), nullif(trim(failed_at),'')) = 1);
I am inclined to do this with addition. To check that one of a group of columns is not null, count the number of not-null values:
check ( (paid_at is not null)::int + (failed_at is not null)::int) > 0 )
You can use the following predicate:
alter table charges add constraint exclusive_rule check (
paid_at is null and failed_at is not null or
paid_at is not null and failed_at is null
);

sql- not null with default, check constraint

I have created a table and I'm asked to add a constraint to a column called no_semesters. the condition is that the no has to be >= 0 and it should be not null with default, and the default value is 6. When I run this statement:
INSERT INTO PREMIERD08/PGM (PGM_ID, PGM_NAME)
VALUES('CPD', 'COMPUTER PROGRAMMING')
I get an error message saying column - no_semesters cant be null. why wont it set to the default value which is 6?
this was the statement -
CREATE TABLE PREMIERD08/PGM (
PGM_ID CHAR(3) NOT NULL,
PGM_NAME CHAR(40) NOT NULL,
NO_SEMESTERS NUMERIC ( 1) NOT NULL WITH DEFAULT 6,
CONSTRAINT PGM_PGM_NAME_UK UNIQUE (PGM_NAME),
CONSTRAINT PGM_PGM_ID_CK CHECK (PGM_ID IN ('CPA', 'CPD') ),
CONSTRAINT PGM_PGM_ID_PK PRIMARY KEY (PGM_ID), CONSTRAINT PGM_NO_SEMESTERS_CK CHECK (NO_SEMESTERS >= 0)
)
Your code works in Db2 (for LUW) - the only thing to change is the table name needs to be in "" because of the /
CREATE TABLE "PREMIERD08/PGM" .....
INSERT INTO "PREMIERD08/PGM" (PGM_ID, PGM_NAME)
VALUES('CPD', 'COMPUTER PROGRAMMING')

Setting value of row to NULL if another value is not present in Oracle

I am using an Oracle database and trying to add a constraint that sets the chemical_value to NULL if there is a chemical_outlier present vice versa. I have been searching the Oracle docs for a possible solution but stuck
Create table chemical
(
chemical_id char(3) not null,
chemical_name varchar2(50) not null,
chemical_value numeric,
checmical_outlier varchar(50),
constraint checkChemical <DONT KNOW HOW TO APPROACH THIS>
)
Just need some kind of direction is approaching this. I know NULL values can be bad but just want them there instead of an empty row
A check constraint won't set anything to null, but it will prevent invalid values being entered:
create table chemical
( chemical_id varchar2(3) not null
, chemical_name varchar2(50) not null
, chemical_value numeric
, chemical_outlier varchar(50)
, constraint chem_value_or_outlier_chk
check (not (chemical_value is not null and chemical_outlier is not null)
and (chemical_value is not null or chemical_outlier is not null))
);
Now these fail:
insert into chemical (chemical_id, chemical_name, chemical_value, chemical_outlier)
values ('x', 'xyz', 123, 'outlier');
ORA-02290: check constraint (XXX.CHEM_VALUE_OR_OUTLIER_CHK) violated
insert into chemical (chemical_id, chemical_name, chemical_value, chemical_outlier)
values ('x', 'xyz', null, null);
ORA-02290: check constraint (XXX.CHEM_VALUE_OR_OUTLIER_CHK) violated
But these succeed:
insert into chemical (chemical_id, chemical_name, chemical_value, chemical_outlier)
values ('x', 'xyz', null, 'outlier');
insert into chemical (chemical_id, chemical_name, chemical_value, chemical_outlier)
values ('x', 'xyz', 123, null);
If you call an INSERT without a column, that column is set with NULL by default. So, if you can control the INSERT's, just call them with 3 columns, the 2 mandatory and 1 of the other ones. In this case you need a check to guarante that anyone will call the INSERT with the 4 columns like that:
CONSTRAINT "CONSTRAINT_NAME"
CHECK ((chemical_value IS NULL AND checmical_outlier IS NOT NULL)
OR (chemical_value IS NOT NULL AND checmical_outlier IS NULL)) ENABLE
So, if someone try to define both columns, the constraint don't accept the INSERT.
But, if you have a form or something else that generate the INSERT query, and even when you don't fill a column, the INSERT is generated with that column with value "" (empty string), you need a trigger to convert this to a NULL value like that:
CREATE OR REPLACE TRIGGER "TRIGGER_NAME"
BEFORE INSERT OR UPDATE ON chemical
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
IF :new.chemical_value = ''
THEN :new.chemical_value := NULL;
END IF;
IF :new.checmical_outlier = ''
THEN :new.checmical_outlier := NULL;
END IF;
END;
Please note that this trigger don't check the condition "one is filled and the other one isn't", the trigger just convert the empty string to NULL. You can use both, trigger and constraint, to guarante the condition and the conversion.

PostgreSQL constraint - only one row can have flag set

I have a PostgreSQL table
CREATE TABLE my_table
(
id serial NOT NULL,
name text,
actual boolean DEFAULT false,
CONSTRAINT my_table_pkey PRIMARY KEY (id),
);
How can I set a constraint that only one row can have actual flag set to TRUE?
You can create a unique partial index on that column only for true values:
create unique index on my_table (actual)
where actual = true;
SQLFiddle: http://sqlfiddle.com/#!15/91f62/1
My approach would add another feature to an index-based only solution: automatic deactivation of the current flag when setting the flag on another row.
That would involve, of course a trigger.
I would also recommand, as suggested by Frank Heikens, storing "not actual" state as a null instead of false. In postgresql, each null value is different from another null value, so the unicity constraint is quite easy to solve: we can allow for only one true value, and as many null value as necessary.
Here is my implementation:
CREATE TABLE my_table
(
id serial NOT NULL,
name text,
actual boolean,
CONSTRAINT my_table_pkey PRIMARY KEY (id),
CONSTRAINT actual_not_false CHECK(actual != false)
);
.
CREATE UNIQUE INDEX ON my_table USING btree(actual nulls LAST);
.
CREATE OR REPLACE FUNCTION ensure_only_one_enabled_state_trigger()
RETURNS trigger
AS $function$
BEGIN
-- nothing to do if updating the row currently enabled
IF (TG_OP = 'UPDATE' AND OLD.actual = true) THEN
RETURN NEW;
END IF;
-- disable the currently enabled row
EXECUTE format('UPDATE %I.%I SET actual = null WHERE actual = true;', TG_TABLE_SCHEMA, TG_TABLE_NAME);
-- enable new row
NEW.actual := true;
RETURN NEW;
END;
$function$
LANGUAGE plpgsql;
.
CREATE TRIGGER my_table_only_one_enabled_state
BEFORE INSERT OR UPDATE OF actual ON my_table
FOR EACH ROW WHEN (NEW.actual = true)
EXECUTE PROCEDURE ensure_only_one_enabled_state_trigger();
This should be doable with an exclusion constraint. For your case:
CREATE TABLE my_table
(
id serial NOT NULL,
name text,
actual boolean DEFAULT false,
CONSTRAINT my_table_pkey PRIMARY KEY (id),
EXCLUDE (actual WITH =) WHERE (actual)
);
Tested with:
INSERT INTO my_table VALUES (1, 'something', false);
INSERT INTO my_table VALUES (2, 'something_else', true);
Then the following is a constraint violation
INSERT INTO my_table VALUES (3, 'third_thing', true);

CHECK CONSTRAINT on multiple columns

I use SQL Server 2008
I use a CHECK CONSTRAINT on multiple columns in the same table to try to validate data input.
I receive an error:
Column CHECK constraint for column
'AAAA' references another column,
table 'XXXX'.
CHECK CONSTRAINT does not work in this way.
Any other way to implement this on a single table without using FK?
Thanks
Here an example of my code
CREATE TABLE dbo.Test
(
EffectiveStartDate dateTime2(2) NOT NULL,
EffectiveEndDate dateTime2(2) NOT NULL
CONSTRAINT CK_CmsSponsoredContents_EffectiveEndDate CHECK (EffectiveEndDate > EffectiveStartDate),
);
Yes, define the CHECK CONSTRAINT at the table level
CREATE TABLE foo (
bar int NOT NULL,
fred varchar(50) NOT NULL,
CONSTRAINT CK_foo_stuff CHECK (bar = 1 AND fred ='fish')
)
You are declaring it inline as a column constraint
...
fred varchar(50) NOT NULL CONSTRAINT CK_foo_fred CHECK (...)
...
Edit, easier to post than describe. Fixed your commas.
CREATE TABLE dbo.Test
(
EffectiveStartDate dateTime2(2) NOT NULL,
EffectiveEndDate dateTime2(2) NOT NULL, --need comma
CONSTRAINT CK_CmsSponsoredContents_EffectiveEndDate CHECK (EffectiveEndDate > EffectiveStartDate) --no comma
);
Of course, the question remains are you using a CHECK constraint where it should be an FK constraint...?
Check constraints can refer to a single column or to the whole record.
Use this syntax for record-level constraints:
ALTER TABLE MyTable
ADD CONSTRAINT MyCheck
CHECK (...your check expression...)
You can simply apply your validation in a trigger on the table especially that either way the operation will be rolled back if the check failed.
I found it more useful for CONSTRAINT using case statements.
ALTER TABLE dbo.ProductStock
ADD
CONSTRAINT CHK_Cost_Sales
CHECK ( CASE WHEN (IS_NOT_FOR_SALE=0 and SAL_CPU <= SAL_PRICE) THEN 1
WHEN (IS_NOT_FOR_SALE=1 ) THEN 1 ELSE 0 END =1 )