EXCLUDE constraint with conditional statement - sql

I have a place table in postgres that contains the following columns
id | idplace | date_slot | type
-----+---------+-------------------------+------------
1 | 360 | [2023-02-20,2023-02-27) | 'zero'
The column type accepts only 3 values ('zero','one' or 'two')
I have created a constraint earlier to exclude any entry that is similar to another and it works. Here is the gist constraint
ALTER TABLE ONLY place ADD CONSTRAINT place_idplace_date_slot_excl1 EXCLUDE USING gist (idplace WITH =, type WITH =,date_slot WITH &&)
Now I want to modify it with the following rules:
-if the column type is equal to 'zero' do not accept any similar entry.
-if the column type is equal to 'one' accept only one another entry that contains 'two' (not zero or one).
-if the column type is equal to 'two' accept only one another entry that contains 'one' (not zero or two).

I cannot think of a better way than adding a generated column:
ALTER TABLE place
ADD col int4range GENERATED ALWAYS AS (
CASE WHEN type = 'zero' THEN INT4RANGE '[1,2]'
WHEN type = 'one' THEN INT4RANGE '[1,1]'
WHEN type = 'two' THEN INT4RANGE '[2,2]'
END
) STORED
NOT NULL;
Then you can use that column in your exclusion constraint with the && operator.

Related

PostgreSQL constraint to prevent overlapping ranges

I wonder if it's possible to write a constraint that would make ranges unique. These ranges are represented as two string-typed columns bottom and top. Say, If I have the following row in a database,
| id | bottom | top |
|----|--------|-------|
| 1 | 10000 | 10999 |
inserting the row (2, 10100, 10200) would immediately result in constraint violation error.
P.S
I can't switch to integers, unfortunately -- only strings
Never store numbers as strings, and always use a range data type like int4range to store ranges. With ranges, you can easily use an exclusion constraint:
ALTER TABLE tab ADD EXCLUDE USING gist (bottom_top WITH &&);
Here, bottom_top is a range data type.
If you have to stick with the broken data model using two string columns, you can strip # characters and still have an exclusion constraint with
ALTER TABLE tab ADD EXCLUDE USING gist (
int4range(
CAST(trim(bottom, '#') AS integer),
CAST(trim(top, '#') AS integer),
'[]'
) WITH &&
);

phpPgAdmin automatically creates null row during adding a column

I have a table, called "Route" (part of a production postgres database). Because of a missing column "arrival_time", I used the "Add column" function of phpPgAdmin. There, I added each important property (name, type). When I browse the column after it was created, there is automatically created a value:
arrival_time | count
--------------------
NULL | 9358
Because of this, I cannot set the constraint "NOT_NULL", but which is required. What is the reason for this automatically created value? And how can I avoid it or fix this issue?
This is not a phpPgAdmin issue. You must either add the new column with NULL (which is the default anyway), or add with NOT NULL constraint and a DEFAULT clause. If neither is present, PostgreSQL doesn't know what to do with existing rows.
When a column is added with ADD COLUMN, all existing rows in the table are initialized with the column's default value (NULL if no DEFAULT clause is specified). If there is no DEFAULT clause, this is merely a metadata change and does not require any immediate update of the table's data; the added NULL values are supplied on readout, instead.
If you already added the column, you can use a single UPDATE to set all existing rows to a starting value, f.ex.:
UPDATE table SET arrival_time = NOW();
After that, you can add a NOT NULL constraint to this column.

Oracle. Constrain one row to have a certain value

I want to add a constraint to a table where only one row can exist at any one time with a value in a certain column.
For example
create table MyTable
....
status varchar(1);
)
A check on status column would be 'O', 'C', 'P' for open , closed or pending.
There can be multiple Closed and Pending but only one (or none) row in the table can be Open.
Any pointers appreciated.
Create an unique function index:
CREATE UNIQUE INDEX myindex ON mytable( CASE status WHEN 'O' THEN 'O' END );
It will prevent from inserting two 'O' values, but will allow for other duplicate values.

SQL Not Empty instead of Not NULL

I am using postgreSQL. I have a column that:
NOT NULL
However when I want to insert a row with an empty string as like:
''
it doesn't give me an error and accepts. How can I check insert value should be not empty? (Neither empty nor null)
PS: My column defined as:
"ads" character varying(60) NOT NULL
Add a constraint to column definition. For example something like:
ads character varying(60) NOT NULL CHECK (ads <> '')
For more, see http://www.postgresql.org/docs/current/static/ddl-constraints.html
Found in the current documentation of postgreSQL you can do the following to achieve what you want:
CREATE TABLE distributors (
did integer PRIMARY KEY DEFAULT nextval('serial'),
name varchar(40) NOT NULL CHECK (name <> '')
);
From the documentation:
CHECK ( expression )
The CHECK clause specifies an expression producing a Boolean result
which new or updated rows must satisfy for an insert or update
operation to succeed. Expressions evaluating to TRUE or UNKNOWN
succeed. Should any row of an insert or update operation produce a
FALSE result an error exception is raised and the insert or update
does not alter the database. A check constraint specified as a column
constraint should reference that column's value only, while an
expression appearing in a table constraint may reference multiple
columns.
Currently, CHECK expressions cannot contain subqueries nor refer to variables other than columns of the current row.

SQL Server 2008: Check constraints that guarantees that only one value in all rows is set to 1 and others are 0

There is a need to build constraint on the column that guarantees that only one value in all rows is 1 and all the others are 0.
Solution with triggers exists but I would like to have something built in.
Is such thing possible at all?
Edit
Actually I just noticed you are on SQL Server 2008 you could use a filtered index for this
CREATE UNIQUE NONCLUSTERED INDEX UIX ON YourTable (col) where col = 1
Original Answer
The easiest way would probably be to store this one special pk in a separate one row table. The no more than one row aspect can be enforced with check constraints.
CREATE TABLE OneRowTable
(
lock CHAR(1) DEFAULT 'X' NOT NULL PRIMARY KEY CHECK (lock = 'X'),
OtherTablePK int
);
Otherwise assuming you might have an id field comprised of positive integers you could add a computed column with the following definition
case when col=1 then -1 else id end
and add a unique constraint to that.