How to stop a database result from re-appearing once value is taken - sql

I have a car rental project, when renting an available car from database table CAR with car ID as primary key, I insert data into table RESERVATION with reservation ID as primary key and car ID as foreign key from car table.
However, I don't want the car I just rented out to appear as an "available car" to rent. What query should I write in order to hide it?
Select cars.*
from cars, reservation
where ?

You should not need to update any tables to hide the car.
When selecting the list of available cars for display, use this SQL:
SELECT * FROM Car WHERE CarID NOT IN (SELECT CarID FROM Reservation)
This will exclude any cars that have already been reserved. That's the basic idea, anyway.
It's likely that reservations are only good for a certain period, and the user may be attempting to reserve a different period, so you might need something more complicated, like:
SELECT * FROM Car WHERE CarID NOT IN
(
SELECT CarID
FROM Reservation
WHERE StartDate < #DesiredEndDate
AND EndDate > #DesiredStartDate
)
This will provide a list of cars that do not appear in a reservation that overlaps the user's desired reservation time.

You can write a join query as:
Select C.carID
from #Car C
Left join #Reservation R on C.carID = R.carID
where R.reservationID is null

Related

Return everything from many-to-many relationship with only one query

I'll give an example to better clarify what I want:
Suppose I have the following classes in my programming language:
Class Person(
int id,
string name,
List<Car> cars
);
Class Car(
int id,
string name,
string brand
)
I want to save that in a PostgreSQL database, so I'll have the following tables:
CREATE TABLE person(
id SERIAL,
name TEXT
);
CREATE TABLE car(
id SERIAL,
name TEXT,
brand TEXT
)
CREATE TABLE person_car(
person_id int,
car_id int,
CONSTRAINT fk_person
FOREIGN KEY (person_id)
REFERENCES person(id),
CONSTRAINT fk_car
FOREIGN KEY (car_id)
REFERENCES car(id)
)
Then, I want to select all people with their cars from DB. I can select all people, then for each person, select their cars. But supposing I have 1000 people, I will have to query the DB 1001 times (one to select all people, and one for each person, to get their cars).
Is there an efficient way to bring all people, each with all their cars in a single query, so that I can fill my classes with the correct data without querying the DB a lot of times?
If you want to return a hierarchical dataset, you can use subqueries with COALESCE, for example :
SELECT
p.id
p.name,
COALESCE((SELECT
json_agg(json_build_object(
'id', c.id,
'name', c.name,
'brand', c.brand
))
FROM car AS c
JOIN person_car pc ON c.id = pc.car_id
WHERE pc.person_id = p.id), '[]'::json) AS cars
FROM person AS p;
You are joining person and car to person_car based on their respective ID’s.
SELECT
person.name,
person.id as person_id,
car.name,
car.brand,
car.id as car_id
FROM
person
JOIN
person_car
ON
person.id = person_car.person_id
JOIN
car
ON
car.id = person_car.car_id

Insert into new table and a join table at the same time

I have a table called buyers and a table called sellers and I want to introduce a table called contacts that holds contact details for both buyers and sellers.
Focusing on the buyers first, I want a join table buyers_contacts to join buyers to contacts.
I want to initially fill the contacts table with one entry for each buyer. Then for each contact, I want to create a row in buyers_contacts.
How should I do this?
So far I have come up with this query (with incorrect syntax).
with buyer as (
select name, id from buyers
)
, new_contact as (
insert into contacts (name) select name from buyer
returning id as contact_id, buyer.id as buyer_id
)
insert into buyers_contacts (buyer_id, contact_id) values
(new_contact.buyer_id, new_contact.contact_id);
I have thought about doing this update in two stage, first creating a contact for each buyer, and and then inserting into the buyers_contacts table but I cannot rely on the buyers to have unique names.
I feel like this should be a solved problem but, there are no examples that seem to fit this situation.
Postgres is the database I am using.
Assuming there is some sequence behind the scenes that generates the contact Id when to create a new record:
create sequence contact_id;
I think all you have to do to add the buyers is this:
insert into contacts (contact_id, buyer_id)
select
nextval ('contact_id'), b.id
from buyer b
where not exists (
select null
from contacts c
where c.buyer_id = b.id
)
This will create new contact records at any time for only the buyers who have not already been added to the table.
You can also just rely on the default value:
insert into contacts (buyer_id)
select
b.id
from buyer b
where not exists (
select null
from contacts c
where c.buyer_id = b.id
)

Selecting columns from different tables

I need to display two columns from my attendance table (MEMBER_ID & MEETING_ID) and one column from my meeting table and finally two columns from my member table which displays the names that match with MEETING_ID.
The attendance table has a composite key (MEMBER_ID*, MEETING_ID*)
The member table's primary key is MEMBER_ID
Meeting table's primary key is MEETING_ID
My attempt is not working, can someone please help?
SELECT MEMBER_ID, MEETING_ID, MEETING_NAME MEMBER_FIRSTNAME, MEMBER_LASTNAME
FROM ATTENDANCE, MEMBER, MEETING
WHERE MEETING.MEMBER_ID = MEETING.MEMBER_ID;
End result needs to be:
MEMBER_ID MEETING_ID MEETING_NAME FIRSTNAME LASTNAME
0001 MEET0004 SPORTS DAY JOHN SMITH
May be you need this.
SELECT A.MEMBER_ID, A.MEETING_ID, M2.MEETING_NAME, M1.MEMBER_FIRSTNAME, M1.MEMBER_LASTNAME
FROM ATTENDANCE A, MEMBER M1, MEETING M2
WHERE M1.MEMBER_ID = A.MEMBER_ID
AND A.MEETING_ID = M2.MEETING_ID;
SELECT
a.MEMBER_ID
,a.MEETING_ID
,mt.MEETING_NAME
,mb.MEMBER_FIRSTNAME
,mb.MEMBER_LASTNAME
FROM
ATTENDANCE a
INNER JOIN MEMBER mb
ON a.MEMBER_ID = mb.MEMBER_ID
INNER JOIN MEETING mt
ON a.MEETING_ID = mt.MEETING_ID
;
Use Explicit Join Syntax and then setup your relationships using the ON conditions and the keys between the tables. Note I also used table aliases to shorten typying.

SQL: How to speed up a query using indexing

I am trying to speed up a query to find all CUSTOMERs who have bought a MOTORCYCLE manufactured before 1970 AND bought another MOTORCYCLE manufactured after 2010. Since my query is running very slowly, I think that I need help with finding the better indexes. My attempts are documented below:
Tables
CREATE TABLE CUSTOMER (
id int PRIMARY KEY,
fname varchar(30),
lname varchar(30)
);
CREATE TABLE MOTORCYCLE (
id int PRIMARY KEY,
name varchar(30),
year int -- Manufactured year
);
CREATE TABLE SALES (
cid int,
mid int,
FOREIGN KEY(cid) REFERENCES CUSTOMER(id),
FOREIGN KEY(mid) REFERENCES MOTOCYCLE(id),
PRIMARY KEY(pid, mid, role)
);
Indexes
Here are my indexes (I am somewhat guessing with these, but this was my attempt):
CREATE UNIQUE INDEX customerID on CUSTOMER(id);
CREATE INDEX customerName on CUSTOMER(fname, lname);
CREATE UNIQUE INDEX motorcycleID on MOTORCYCLE(id);
CREATE INDEX motorcycleName on MOTORCYCLE(name);
CREATE INDEX motorcycleYear on MOTORCYCLE(year);
CREATE INDEX salesCustomerMotorcycleID on SALES(cid, mid);
CREATE INDEX salesCustomerID on SALES(cid);
CREATE INDEX castsMotorcycleID on SALES(mid);
Queries
My query to find the customers purchasing bikes manufactured before 1970 and after 2010 is here:
SELECT fname, lname
FROM (SALES INNER JOIN CUSTOMER ON SALES.cid=CUSTOMER.id) INNER JOIN MOTORCYCLE ON MOTORCYCLE.id=SALES.mid
GROUP BY CUSTOMER.id
HAVING MIN(MOTORCYCLE.year) < 1970 AND MAX(MOTORCYCLE.year) > 2010;
And here is another working query which avoids the GROUP BY and HAVING clauses:
SELECT DISTINCT C.id, fname, lname
FROM (CUSTOMER as C inner join (SALES as S1 INNER JOIN MOTORCYCLE as M1 ON M1.id=S1.mid) on C.id=S1.cid) inner join (SALES as S2 inner join MOTORCYCLE as M2 on S2.mid=M2.id) on C.id=S2.cid
WHERE (M1.year < 1970 AND M2.year > 2010);
Any suggestions on the kinds of indexes I can use to speed up my query? Or should I change my query?
UPDATE
I found another query that also works, but it is also too slow. It has been added above. Still, it might be helpful when finding an index to speed it up.
When you check out your queries with EXPLAIN QUERY PLAN, you see that in both cases, the database looks up many related records before it filters out unneeded records (with unwanted years).
The following queries look up the motorcycle IDs before matching; which one is faster depends on the details of your data and must be measured by you:
SELECT *
FROM Customer
WHERE EXISTS (SELECT 1
FROM Sales
WHERE cid = Customer.id
AND mid IN (SELECT id
FROM Motorcycle
WHERE year < 1970))
AND EXISTS (SELECT 1
FROM Sales
WHERE cid = Customer.id
AND mid IN (SELECT id
FROM Motorcycle
WHERE year > 2010));
SELECT *
FROM Customer
WHERE EXISTS (SELECT 1
FROM Sales AS s1
JOIN Sales AS s2 ON s1.cid = s2.cid
WHERE s1.cid = Customer.id
AND s1.mid IN (SELECT id
FROM Motorcycle
WHERE year < 1970)
AND s2.mid IN (SELECT id
FROM Motorcycle
WHERE year > 2010));
SQL Fiddle
Why using group by when there's no using of aggregation function in the query?
Use distinct instead if you don't want to see any duplication

Linkage Table: Technical name given to table storing associations?

Title pretty much sums it up.
Is there a technical name given to a table that stores primary key from two separate tables to create a linkage.
i.e.
car ( id, manufacturer, model, year, vin),
passenger ( id, name ),
linkage_table ( car, passenger )
Where car stores value of the id column from the car table and passenger stores the value of id column from the passenger table.
SELECT c.*, p.*
FROM car c, passenger p, linkage_table l
WHERE c.id = 15
AND c.id = l.car
AND p.id = l.passenger
It's called a junction table, and is used in a Many-to-Many relationship.