Add PostgreSQL column constraint based on value in other column(s) - sql

I have a table:
CREATE TABLE ProjectCreationTasks
(
Id text NOT NULL PRIMARY KEY,
ProjectName text,
ProjectCode text,
DenialReason text
);
An administrator can approve or deny a project creation request. To approve, the admin sets both a ProjectName and ProjectCode; to deny, the admin sets a DenialReason.
How can I add a constraint such that:
Name, Code, Reason can all be null simultaneously
If both Name and Code has a value then Reason must be null
If Reason has a value, then both Name and Code must be null
Thank you in advance.

You could use CHECK constaint to implement this kind of logic:
CREATE TABLE ProjectCreationTasks (
Id text NOT NULL PRIMARY KEY,
ProjectName text,
ProjectCode text,
DenialReason text,
CONSTRAINT my_constraint CHECK
((ProjectName IS NULL AND ProjectCode IS NULL AND DenialReason IS NULL)
OR(ProjectName IS NOT NULL AND ProjectCode IS NOT NULL AND DenialReason IS NULL)
OR(DenialReason IS NOT NULL AND ProjectName IS NULL AND ProjectCode IS NULL))
);
DBFiddle Demo

The answer from Lukasz Szozda is correct as modified in my comment (the problem statement is slightly ambiguous). A slightly shorter equivalent clause that might be harder to read is
CONSTRAINT my_constraint CHECK
((ProjectName IS NULL = ProjectCode IS NULL) -- go together
AND (ProjectCode IS NULL OR DenialReason IS NULL) -- allow both NULL but disallow both NOT NULL
);

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 constraint “at least one of two attributes”

I need to create a table User with telephone_number and e_mail_adress columns. Each row must have at least one of those columns set. It could have both or just one, but it must have at least one of them.
How can I express that constraint in SQL?
create table Users (
/* Whatever */
TelephoneNumber varchar(2000) null,
EmailAddress varchar(5) null,
constraint CK_AtLeastOneContact CHECK (
TelephoneNumber is not null or
EmailAddress is not null
)
)
You may want to adjust the data types :-)

Beginner with triggers

Im a beginner in database and i got this difficult auction database project.
Im using SQL Server Management Studio also.
create table user(
name char(10) not null,
lastname char(10) not null
)
create table item(
buyer varchar(10) null,
seller varchar(10) not null,
startprice numeric(5) not null,
description char(22) not null,
start_date datetime not null,
end_date datetime not null,
seller char(10) not null,
item_nummer numeric(9) not null,
constraint fk_user foreign key (buyer) references user (name)
)
Basically what the rule im trying to make here is:
Column buyer has NULL unless the time (start_date and end_date) is over and startprice didnt go up or increased. Then column buyer will get the name from table user who bidded on the item.
The rule is a bid too difficult for me to make, i was thinking to make a trigger, but im not sure..
Your model is incorrect. First you need a table to store the bids. Then when the auction is over, you update the highest one as the winning bid. Proably the best way is to have a job that runs once a minute and finds the winners of any newly closed auctions.
A trigger will not work on the two tables you have because triggers only fire on insert/update or delete. It would not fire because the time is past. Further triggers are an advanced technique and a db beginner should avoid them as you can do horrendous damage with a badly written trigger.
You could have a trigger that works on insert to the bids table, that updates the bid to be the winner and takes that status away from the previous winner. Then you simply stop accepting new bids at the time the auction is over. Your application could show the bidder who is marked as the winner as the elader if the auction is till open and teh winner if it is closed.
There are some initial problems with your schema that need addressed before tackling your question. Here are changes I would make to significantly ease the implementation of the answer:
-- Added brackets around User b/c "user" is a reserved keyword
-- Added INT Identity PK to [User]
CREATE TABLE [user]
(
UserId INT NOT NULL
IDENTITY
PRIMARY KEY
, name CHAR(10) NOT NULL
, lastname CHAR(10) NOT NULL
)
/* changed item_nummer (I'm not sure what a nummer is...) to ItemId int not null identity primary key
Removed duplicate Seller columns and buyer column
Replaced buyer/seller columns with FK references to [User].UserId
Add currentBid to capture current bid
Added CurrentHighBidderId
Added WinningBidderId as computed column
*/
CREATE TABLE item
(
ItemId INT NOT NULL
IDENTITY
PRIMARY KEY
, SellerId INT NOT NULL
FOREIGN KEY REFERENCES [User] ( UserId )
, CurrentHighBidderId INT NULL
FOREIGN KEY REFERENCES [User] ( UserId )
, CurrentBid MONEY NOT NULL
, StartPrice NUMERIC(5) NOT NULL
, Description CHAR(22) NOT NULL
, StartDate DATETIME NOT NULL
, EndDate DATETIME NOT NULL
)
go
ALTER TABLE dbo.item ADD
WinningBidderId AS CASE WHEN EndDate < CURRENT_TIMESTAMP
AND currentBid > StartPrice THEN CurrentHighBidderId ELSE NULL END
GO
With the additional columns a computed column can return the correct information. If you must return the winner's name instead of id, then you could keep the schema above the same, add an additional column to store the user's name, populate it with a trigger and keep the computed column to conditionally show/not show the winner..

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 )

Conditional SQLite check constraint?

I have a table defined by the following SQL:
CREATE TABLE test (
id integer PRIMARY KEY NOT NULL UNIQUE,
status text NOT NULL,
enddate date,
/* Checks */
CHECK (status IN ("Current", "Complete"))
);
I'd like to add a constraint using CHECK that requires enddate to be non-null if the status is "Complete".
Is this possible? I am using SQLite v3.6.16.
How about:
CHECK (status = "Current" or (status = "Complete" and enddate is not null))
There's nothing stopping you from having multiple CHECK constraints on a single table. IMO the simplest and most easily expandable solution:
CHECK (status IN ("Current", "Complete"))
CHECK (status <> "Complete" OR enddate IS NOT NULL)
This uses the fact that if A then B is logically equivalent to either not A or B.
CREATE TABLE test (
id integer PRIMARY KEY,
status text NOT NULL CHECK (status IN ('Current', 'Complete')),
enddate date NOT NULL
);
This will work in SQLite, with the CHECK constraint written inline. I changed double quotes to apostrophes so it can be used in PHP.