I have a database with 2 tables (primary keys are in italic) :
ROOM (ROOM_ID, DAY, HOUR)
RESERVATION (CUSTOMER_ID, ROOM_ID, DATE)
The first table describes room availability and the second reservations made by the customers.
Now let's say that I've inserted 3 rows into ROOM :
3898 WEDNESDAY 2.00
3454 MONDAY 1.00
3563 MONDAY 1.00
and two rows into RESERVATION:
0898 3454 05-09-2019
0567 3898 05-08-2019
Now I'd like to create a constraint which doesn't allow a customer who has already reserved a room to reserve another room which is available at the same time!
Then if I try to insert:
0567 3563 05-10-2019
I would get an error because according to the first table rooms 3454 and 3563 are both available at 1.00.
Thanks !
Now I'd like to create a constraint which doesn't allow a customer who has already reserved a room to reserve another room which is available at the same time.
I interpret this as saying that a customer can reserve only one room on each date. If so, you can do this with a unique index/constraint in reservation on (customer_id, date):
create unique index unq_reservation_customer_date on reservation(customer_id, date);
This guarantees that a given customer can only reserve one room on each date.
I can see two ways to create true constraint:
You create materialized view of both tables and add unique index on this view.
You add ROOM_DAY and ROOM_HOUR columns into RESERVATION table and
add a composite foreign key constraint to enforce data integrity:
ALTER TABLE RESERVATION WITH CHECK ADD CONSTRAINT FK_RESERVATION_ROOM
FOREIGN KEY(ROOM_ID, ROOM_DAY, ROOM_HOUR)
REFERENCES ROOM (ROOM_ID, DAY, HOUR)
beware you have to enable it too. Then you can add unique index on RESERVATION table.
Related
Say I have a column in Postgresql DB like this to represent booking system for desk in an office. In a day, a person can only book a desk.
ID
Booked_Date
Seat_ID
Employee_ID
1
2022-07-08
10C
id1
2
2022-07-08
20C
id2
When booking a desk, the system need to check whether that person has booked any other desk or other person has booked that desk before inserting the booking data. So, something like this for example:
select count(*) as today_book from booking where (booked_date=today and seat_id='10C')
or (booked_date=today and employee_id='id1')
if(today_book == 0) {
insert into booking values(today, book_seat, current_employee_id)
}
To avoid duplicate data (a desk booked by more than one person), how can I do it in Postgresql? I am using Postgresql 11.xx version.
I am thinking about using Plpgsql or stored procedure and wrapped around above logic in it.
You need two unique constraints:
ALTER TABLE booking ADD CONSTRAINT seat_unique_per_day
UNIQUE (booked_date, seat_id);
ALTER TABLE booking ADD CONSTRAINT employee_unique_per_day
UNIQUE (booked_date, employee_id);
Then you simply try to INSERT and catch and handle constraint violation errors in your application.
I'm trying to insert the values into the tables that I created.
This is the values that I'm trying to insert.
INSERT INTO DDR_Rental (customer_ID, rental_date, rent_fee, film_title, start_date, expiry_date, rating)
VALUES (12345, '12-Mar-19', '4.99', 'Peppermint', '12-Mar-19', '22-Mar-19', 4);
This is the datatypes and the constraints.
CREATE TABLE DDR_Rental
(customer_ID NUMBER(5),
rental_date DATE,
rent_fee NUMBER(3,2) CONSTRAINT SYS_RENTAL_FEE_NN NOT NULL,
film_title VARCHAR2(20),
start_date DATE,
expiry_date DATE,
rating NUMBER(5),
CONSTRAINT SYS_RENTAL_PK PRIMARY KEY ((customer_ID), (rental_date), (film_title)),
CONSTRAINT SYS_RENTAL_CUS_ID_FK1 FOREIGN KEY (customer_ID) REFERENCES
DDR_CUSTOMER(CUSTOMER_ID),
CONSTRAINT SYS_RENTAL_FILM_TITLE_FK2 FOREIGN KEY (film_title) REFERENCES
DDR_MOVIE_TITLE(FILM_TITLE),
CONSTRAINT SYS_RENTAL_EXP_DATE_CK CHECK (expiry_date >= start_date),
CONSTRAINT SYS_RENTAL_START_DATE_CK CHECK (start_date >= rental_date),
CONSTRAINT SYS_RENTAL_RATING_CK CHECK (REGEXP_LIKE(rating,('[12345]'))));
The error says unique constraint (CPRG250.SYS_RENTAL_PK) violated
It seems like you are trying to add a duplicate rental event for the same film by the same customer on exact one day. That can obviously happen, if you allow in your business logic the situation that a customer can rent a movie, give it back on the same day and rent it back again.
Knowing your business, you have 2 ways to deal with this situation:
Your business model don't allow that. This means that this is a duplicate record and you shouldn't add currently existing record, in which case showing that error is perfectly fine and doesn't allow for duplicates, since this event happened only once.
Your business model allows that. In this case, you should modify your rental_date column to store time along with the date, instead of only storing date, so that you know when the rental event actually happen. You could use datetime type for example to store date with time. This can be done when creating your table, just replace rental_date date with rental_date datetime. If the table is already created you will need to drop and recreate PRIMARY KEY and then after that you could change type of your column using ALTER TABLE ddr_rental ALTER COLUMN rental_date datetime and re-create the primary key. Check values stored in your table after that, since 2019-01-01 will now be represented as 2019-01-01 00:00:00.000 appending the time which wasn't specified before.
In addition to (1) you could wrap your code and handle this exception to return a clear message when this happens, showing that the movie has already been rented.
Moreover, since you don't have a table for storing movies in your inventory, this can lead to a possible mistake, since you may have more than 1 copy of a movie. In this case I suggest that you create separate film and film_copy tables to properly identify which copy of a film has been rented, so that you can rent another copy.
You have a unique constraint in your table. Your table already has a record with customer_id, rental_date and film_title that you want to insert.
Try this query and you will see that there already is a record
select * from DDR_Rental
where customer_id=12345 and rental_date='12-Mar-19' and film_title='Peppermint'
I have a main large table which I have had to put into 3rd normal form and into smaller tables (with primary and foreign keys linking them). The table is about renting books.
I have a customer table which I need to create a primary key for. In the main large table there are duplicates of the customer_id, as the table as a whole is for renting the books, so one customer may have more than one renting.
The table I am currently trying to add a primary key for will not have any nulls or duplicates, however i am unsure how to create the primary key for this without the error- unsure how to make it unique.
CREATE TABLE customer AS
SELECT cust_id, country_id, name, address, postcode
FROM BOOKS
WHERE cust_id != 0;
ALTER TABLE customer
ADD PRIMARY KEY (cust_id);
Is anyone able to help me in how to create the primary key on my customer table, but just taking each unique cust_id from the main table.
In SQL Server the straightforward way to add unique keys is to use IDENTITY. Identity fields are integer fields that auto populate successive values by a specified start value and interval. If you don't specify the interval it will start at 1 and increase the value by 1 each time a value is assigned.
While it's usually done when creating a table, you can do it in your ALTER TABLE step, and it will assign values when added to an existing table. I've explicitly specified the start value and interval that matches the default to show the syntax :
ALTER TABLE customer
ADD cust_id int not null PRIMARY KEY IDENTITY(1,1)
I Have table three tables:
The first one is emps:
create table emps (id number primary key , name nvarchar2(20));
The second one is cars:
create table cars (id number primary key , car_name varchar2(20));
The third one is accounts:
create table accounts (acc_id number primary key, woner_table nvarchar2(20) ,
woner_id number references emps(id) references cars(id));
Now I Have these values for selected tables:
Emps:
ID Name
-------------------
1 Ali
2 Ahmed
Cars:
ID Name
------------------------
107 Camery 2016
108 Ford 2012
I Want to
Insert values in accounts table so its data should be like this:
Accounts:
Acc_no Woner_Table Woner_ID
------------------------------------------
11013 EMPS 1
12010 CARS 107
I tried to perform this SQL statement:
Insert into accounts (acc_id , woner_table , woner_id) values (11013,'EMPS',1);
BUT I get this error:
ERROR at line 1:
ORA-02291: integrity constraint (HR.SYS_C0016548) violated - parent key not found.
This error occurs because the value of woner_id column doesn't exist in cars table.
My work require link tables in this way.
How Can I Solve This Problem Please ?!..
Mean: How can I reference tables in previous way and Insert values without this problem ?..
One-of relationships are tricky in SQL. With your data structure here is one possibility:
create table accounts (
acc_id number primary key,
emp_id number references emps(id),
car_id number references car(id),
id as (coalesce(emp_id, car_id)),
woner_table as (case when emp_id is not null then 'Emps'
when car_id is not null then 'Cars'
end),
constraint chk_accounts_car_emp check (emp_id is null or car_id is null)
);
You can fetch the id in a select. However, for the insert, you need to be explicit:
Insert into accounts (acc_id , emp_id)
values (11013, 1);
Note: Earlier versions of Oracle do not support virtual columns, but you can do almost the same thing using a view.
Your approach should be changed such that your Account table contains two foreign key fields - one for each foreign table. Like this:
create table accounts (acc_id number primary key,
empsId number references emps(id),
carsId number references cars(id));
The easiest, most straightforward method to do this is as STLDeveloper says, add additional FK columns, one for each table. This also bring along with it the benefit of the database being able to enforce Referential Integrity.
BUT, if you choose not to do, then the next option is to use one FK column for the the FK values and a second column to indicate what table the value refers to. This keeps the number of columns small = 2 max, regardless of number of tables with FKs. But, this significantly increases the programming burden for the application logic and/or PL/SQL, SQL. And, of course, you completely lose Database enforcement of RI.
Suppose I have a table patients and a table tests. Patients can take many tests and tests are given to many patients. so it's a many to many relationship. I create a junction table between them. I also want to store the date the patient wants to take the tests so I do that in the junction table. Also, suppose a patient takes more than 1 test on a single day and I want to create a specific billing account against all the test on that date.
I create an autogenerated billing no. I add that to the junction table as well.
So far, my junction table record the tests a patient may have taken on a date and the billing no.
Now I create a table billingaccounts. In this table I want to store all the billing information against 1 billing No. such as totalamount, paid amount and so on. I also want to include the billing no here, since it will help me identify the patient and tests the billing account field refers to.
I was trying to make the billing no in this billing account table as a foreign key to the billing no in the previous junction table.
But I can't do that since the billing no in the junction table cannot be kept unique since it's an m:n junction table.
What can I do to make this work or any other alternatives?
Just add a primary key column to the "junction" table. Your junction table is richer than simply matching two tables. Because you want a foreign key relationship, it is worthy of its own primary key. For example (using MySQL syntax):
create table PatientTests (
PatientTestId int primary key auto_increment,
PatientId int not null,
TestId int not null,
TestDate date,
BillingAccount int,
. . .
constraint fk_patienttests_patientid references Patients(PatientId),
. . .
);
create table
Make the billing no in the junction table a foreign key to the primary key of the billingaccounts table. So, you would create a new row in billingacounts first, including an autogenerated id/billing no, then use that number in the junction table.