How to set constraints based on the values of columns? - sql

I'm working on a table of requests to link an user to another user and these requests must be approved to happen.
CREATE TABLE IF NOT EXISTS requests (
requested_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
approved_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
denied_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
user1_id int NOT NULL,
user2_id int NOT NULL,
UNIQUE(user1_id, user2_id),
CONSTRAINT fk_requests_user FOREIGN KEY (user1_id) REFERENCES user(id),
CONSTRAINT fk_requests_user FOREIGN KEY (user2_id) REFERENCES user(id), );
Right now I have a constraint that prevent further requests of one user to another through:
UNIQUE(user1_id, user2_id)
But I want to be able to use this as history, so I need a way to these foreign keys be UNIQUE if both approved_at and denied_at are '1970-01-01 00:00:00'(zero value of TIMESTAMP).
I thought of this logic, though SYNTACTICALLY WRONG:
UNIQUE(user1_id, user2_id, approved_at='1970-01-01 00:00:00', denied_at='1970-01-01 00:00:00')
How can I make it possible?

Use a partial unique index:
create unique index on requests (requester_id, requested_agency_id)
where approved_at='1970-01-01 00:00:00'
and denied_at='1970-01-01 00:00:00'
Having magic values like that is usually not a good choice. Use null to indicate the absence of a value. Or -infinity if you need range queries to work without checking for null.

Related

CUSTOM UNIQUE CHECK POSTGRES

I am working on a task where I need to store the interviewer's time slot in the table INTERVIEW_SLOT. The table schema is like this:
CREATE TABLE INTERVIEW_SLOT (
ID SERIAL PRIMARY KEY NOT NULL,
INTERVIEWER INTEGER REFERENCES USERS(ID) NOT NULL,
START_TIME TIMESTAMP NOT NULL, -- start time of interview
END_TIME TIMESTAMP NOT NULL, -- end time of interview
IS_BOOKED BOOL NOT NULL DEFAULT 'F', -- slot is booked by any candidate or not
CREATED_ON TIMESTAMP,
-- interviewer can't give the same slot twice
CONSTRAINT UNIQUE_INTERVIEW_SLOT UNIQUE (start_time, INTERVIEWER)
);
We want to ensure that the interviewer can not give the same slot twice but the problem is with second and millisecond values of start_time. I want the UNIQUE_INTERVIEW_SLOT constant like this:
UNIQUE_INTERVIEW_SLOT UNIQUE(TO_TIMESTAMP(start_time::text, 'YYYY-MM-DD HH24:MI'), INTERVIEWER)
Is there any way to add a unique constraint that ignores the second and millisecond value?
You are looking for an exclusion constraint
create table interview_slot
(
id integer primary key generated always as identity,
interviewer integer references users(id) not null,
start_time timestamp not null, -- start time of interview
end_time timestamp not null, -- end time of interview
is_booked bool not null default 'f', -- slot is booked by any candidate or not
created_on timestamp,
constraint unique_interview_slot
exclude using gist (interviewer with =,
tsrange(date_trunc('minute', start_time), date_trunc('minute', end_time), '[]') with &&)
);
This prevents rows with overlapping start/end ranges for the same interviewer. The timestamps are "rounded" to the full minute. You need the extension btree_gist in order to create that constraint.
You can use an UNIQUE INDEX to make this check for you and truncate the timestamp to minutes:
CREATE UNIQUE INDEX idx_interview_slot_ts
ON interview_slot (interviewer, date_trunc('minutes',start_time));
Demo: db<>fiddle

Postgres - range for 'time without time zone' and exclude constraint

I have the following table:
create table booking (
identifier integer not null primary key,
room uuid not null,
start_time time without time zone not null,
end_time time without time zone not null
);
I want to create an exclude constraint to enforce that there are no overlapping appointments for the same room.
I tried the following:
alter table booking add constraint overlapping_times
exclude using gist
(
cast(room as text) with =,
period(start_time, end_time) with &&)
);
This has two problems:
Casting room to text is not enough, it gives: ERROR: data type text has no default operator class for access method "gist". I know in v10 there is btree_gist, but I am using v9.5 and v9.6, so I have to manually cast the uuid to a text afaik.
period(...) is wrong, but I have no idea how to construct a range of time without time zone type.
After installing btree_gist, you can do the following:
create type timerange as range (subtype = time);
alter table booking add constraint overlapping_times
exclude using gist
(
(room::text) with =,
timerange(start_time, end_time) with &&
);
If you want an expression in the constraint you need to put that into parentheses. So either (room::text) or (cast(room as text))

Oracle Application Express - 'ORA-01843: not a valid month error' in one table only

I am creating a database in Oracle Application Express and am having a problem inserting a date into one of the tables.
INSERT INTO VIEWING( VIEWING_ID, VIEWING_DATE, TIME, PROPERTY_ID, AGENT_ID)
VALUES('3', '12-07-2015' ,'10:00','1', '101');
I've tried every combination of date format, and trying to force the date to my correct format
to_date('12-07-2015','MM-DD-YYYY')
But nothing is working
CREATE TABLE Viewing (
Viewing_ID number(10) NOT NULL,
Viewing_Date date NOT NULL,
Time timestamp(7) NOT NULL,
Property_ID number(10) NOT NULL,
Agent_ID number(10) NOT NULL,
PRIMARY KEY (Viewing_ID));
ALTER TABLE Viewing ADD CONSTRAINT FK_Viewing_Agent_ID FOREIGN KEY (Agent_ID) REFERENCES Agent (Agent_ID);
ALTER TABLE Viewing ADD CONSTRAINT FK_Viewing_Property_ID FOREIGN KEY (Property_ID) REFERENCES Property (Property_ID);
Every Resource I have found suggests it is most likely a parsing or syntax error but so far nothing has helped.
I have a second table in the schema that I can insert dates into without a problem, the only difference on this table is that the date is required (I have tried making it nullable to test and I still get the same error)
I should point out that Im am completely new to Oracle and this is part of a study project. If I had I choice I would be using SQL Server! But Ive been at this for hours and think its time to admit defeat!
Thanks
It is due to TIME column, not VIEWING_DATE. This worked:
INSERT INTO VIEWING( VIEWING_ID, VIEWING_DATE, TIME, PROPERTY_ID, AGENT_ID)
VALUES(4, date '2015-12-07' , timestamp '2015-12-07 10:00:00',1, 101);

operator is not unique. Could not choose a best candidate operator. You might need to add explicit type casts

I have this query that just selects the difference between two dates:
select date_part('day', 'toDate' - 'fromDate') from "Reservation" where "reservationID" = 1;
but this is giving me this error:
operator is not unique. Could not choose a best candidate operator.
You might need to add explicit type casts.
How can I fix this?
P.S. This is how I created "Reservations" table:
CREATE TABLE "Reservation"
(
"reservationID" serial NOT NULL,
"fromDate" timestamp without time zone NOT NULL DEFAULT '- infinity'::timestamp without time zone,
"toDate" timestamp without time zone NOT NULL DEFAULT '-infinity'::timestamp without time zone,
"staffID" integer NOT NULL DEFAULT 0,
"customerID" integer NOT NULL DEFAULT 0,
"roomID" integer NOT NULL DEFAULT 0,
CONSTRAINT "PK_public.Reservation" PRIMARY KEY ("reservationID"),
CONSTRAINT "FK_public.Reservation_public.Customer_customerID" FOREIGN KEY ("customerID")
REFERENCES "Customer" ("customerID") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT "FK_public.Reservation_public.Room_roomID" FOREIGN KEY ("roomID")
REFERENCES "Room" ("roomID") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT "FK_public.Reservation_public.Staff_staffID" FOREIGN KEY ("staffID")
REFERENCES "Staff" ("staffID") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
)
You cant't use ' you need " instead for columns name
select date_part('day', "toDate" - "fromDate") from "Reservation" where "reservationID" = 1;

SQL Create Table Default value as specific date

I'm using Oracle's APEX and trying to set the default date of one of my columns to '31-dec-2013', but for the life of me, it's not happening. I've tried many syntax variations and gotten a number of errors such as "not a valid month" and "such a unique or primary key exists" something to that effect. Please help! here's my code:
Create Table Lease(
LeaseNo number(8) not null unique,
PropertyID number(6) not null,
ClientId varchar2(4) not null,
Leasestartdate date not null,
LeaseEndDate date dEFAULT ('31-12-2013'),
MonthlyRent number(8,2) check (MonthlyRent >1000),
Primary Key (LeaseNo),
Foreign key (propertyId) references property(Propertyid),
Foreign key (clientId) references client(clientid));
It threw the "not a valid month" error.
You can use to_date with an explicit date format model as ThorstenKettner shows, which means you won't be relying on the session's NLS_DATE_FORMAT. You can also use a date literal, which is always in YYYY-MM-DD format:
...
LeaseEndDate date default date '2013-12-31',
...
Largely a matter of personal preference between the two though; I happen to prefer this, partly because it's slightly less typing, but also because there is no possibility of ambiguity between DD-MM and MM-DD.
Use TO_DATE to convert a string to date:
...
LeaseEndDate date default to_date('31-12-2013','dd-mm-yyyy')
...
Here are 2 Corrections
First remove UNIQUE clause from LeaseNo, you cant make a cols primary key that has the unique Constraint already.
And, try this Format in default clause - '31-DEC-2013'