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.
Related
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.
I am using PostgreSQL and am trying to restrict the number of concurrent loans that a student can have. To do this, I have created a CTE that selects all unreturned loans grouped by StudentID, and counts the number of unreturned loans for each StudentID. Then, I am attempting to create a check constraint that uses that CTE to restrict the number of concurrent loans that a student can have to 7 at most.
The below code does not work because it is syntactically invalid, but hopefully it can communicate what I am trying to achieve. Does anyone know how I could implement my desired restriction on loans?
CREATE TABLE loan (
id SERIAL PRIMARY KEY,
copy_id INTEGER REFERENCES media_copies (copy_id),
account_id INT REFERENCES account (id),
loan_date DATE NOT NULL,
expiry_date DATE NOT NULL,
return_date DATE,
WITH currentStudentLoans (student_id, current_loans) AS
(
SELECT account_id, COUNT(*)
FROM loan
WHERE account_id IN (SELECT id FROM student)
AND return_date IS NULL
GROUP BY account_id
)
CONSTRAINT max_student_concurrent_loans CHECK(
(SELECT current_loans FROM currentStudentLoans) BETWEEN 0 AND 7
)
);
For additional (and optional) context, I include an ER diagram of my database schema.
You cannot do this using an in-line CTE like this. You have several choices.
The first is a UDF and check constraint. Essentially, the logic in the CTE is put in a UDF and then a check constraint validates the data.
The second is a trigger to do the check on this table. However, that is tricky because the counts are on the same table.
The third is storing the total number in another table -- probably accounts -- and keeping it up-to-date for inserts, updates, and deletes on this table. Keeping that value up-to-date requires triggers on loans. You can then put the check constraint on accounts.
I'm not sure which solution fits best in your overall schema. The first is closest to what you are doing now. The third "publishes" the count, so it is a bit clearer what is going on.
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.
I have a requirement which is to keep the data of 2000 employees attendance.
So the attendance table should have design?
365 columns of each day in a year, 2000 pre-inserted records
And i will update it daily via java program
or
5 columns 2000 records daily insert for 365 days?
Or is there any better approach?
I think you're over-thinking things. Assuming you already have an employee table with the employees' details, all you really need is a another table with the employee's ID and the date (and possibly entrance and exit times, if you care about it), where the presence of a row indicates attendance on that date and the lack of if indicates lack of attendance. You can then find this absence days by left joining on the employee table.
CREATE TABLE employee (
id NUMBER PRIMARY KEY,
-- other details...
);
CREATE TABLE attendance (
employee_id NOT NULL REFERENCES employee(id),
attendance_date DATE NOT NULL,
PRIMARY_KEY(employee_id, attendance_date)
);