I'm doing some past question papers and came across the following:
A company uses the following database:
customers - CustNo, CustName, CustAddress
Reservations - CustNo, TourID
Tours - TourID, Destination, Date, Time, Price
From these tables I have to create an SQL statement for finding the customer numbers of passengers who are booked on both any tour to London and also any tour to Edinburgh...
The attempt is this:
SELECT
R.custNo
FROM
Reservations R, Tours T
WHERE
T.tourID = R.tourID
AND T.destination = 'London'
AND T.destination = 'Edinburgh';
I have been told this is not correct and to find an alternative, correct, SQL statement. So if anyone can help me out with this question it'd be great.
You can try the following:
select R.custNo
from Reservations R
join Tours T on T.tourID = R.tourID
where T.destination in ('London', 'Edinburgh')
group by R.custNo
having count(distinct T.destination) = 2
This will look for customers that have 'London' or 'Edinburgh' destinations and retrieve only those, that have both.
SELECT R1.custNo
FROM Reservations R1 inner join Tours T1 on T1.tourID = R1.tourID
WHERE T1.destination = 'London'
INTERSECT
SELECT R2.custNo
FROM Reservations R2 inner join Tours T2 on T2.tourID = R2.tourID
WHERE T2.destination = 'Edinburgh'
ps: this will work only on sql server DBMS
Related
I have 5 SQL tables
store
staff
departments
sold_items
staff_rating
I created a view that JOINs this four of the tables together. The last table (staff_rating),I want to get the rating column at a time close to when items was sold (sold_items.date) for the view rows.
I have tried the following SQL Queries which works but have performance issues.
SQL QUERY 1
SELECT s.name,
s.country,
d.name,
si.item,
si.date,
(SELECT rating
FROM staff_ratings
WHERE staff_id = s.id
ORDER BY DATEDIFF(date, si.date) LIMIT 1) AS rating,
st.name,
st.owner
FROM store st
LEFT OUTER JOIN staff s ON s.store_id = st.id
LFET JOIN departments d ON d.store_id = st.id
LEFT JOIN sold_items si ON si.store_id = st.id
SQL QUERY 2
SELECT s.name,
s.country,
d.name,
si.item,
si.date,
si.rating ,
st.name,
st.owner
FROM store st
LEFT OUTER JOIN staff s ON s.store_id = st.id
LFET JOIN departments d ON d.store_id = st.id
LEFT JOIN (SELECT *,
(SELECT rating
FROM staff_ratings
WHERE staff_id = si.staff_id
ORDER BY DATEDIFF(date, si.date) LIMIT 1) AS rating
FROM sold_items) si ON si.store_id = st.id
SQL Query 2 is faster than SQL Query 1. But Both still have performance issue. Appreciate help for a query with better performance. Thanks in advance.
Your query doesn't look right to me (as mentioned in a comment on the original post; lacking staff_id in the join on the sales, etc)
Ignoring that, one of your biggest performance hits is likely to be this...
ORDER BY DATEDIFF(date, si.date) LIMIT 1
That order by can only be answered by comparing EVERY record for that staff member to the current sales record.
What you ideally want to be able to do is find the appropriate staff rating from an index, and not to have to run computations that involve dates from both the ratings table and the sales table.
If, for example, you wanted "the most recent rating BEFORE the sale", the query can be substantially improved...
SELECT
s.name,
s.country,
d.name,
si.item,
si.date,
(
SELECT sr.rating
FROM staff_ratings sr
WHERE sr.staff_id = s.id
AND sr.date <= si.date
ORDER BY sr.date DESC
LIMIT 1
)
AS rating,
st.name,
st.owner
FROM store st
LEFT JOIN staff s ON s.store_id = st.id
LFET JOIN departments d ON d.store_id = st.id
LEFT JOIN sold_items si ON si.store_id = st.id
Then, with an index for staff_ratings(staff_id, date, rating) the optimiser can very quickly look up which rating to use, without having to scan Every Single Rating for that staff member.
Why DATEDIFF? Would something like this work better? If so, the given index will make it work much faster.
WHERE staff_id = s.id
AND s.date >= s1.date
ORDER BY s.date
LIMIT 1
And INDEX(staff_id, date)
Do you need LEFT JOIN? Perhaps plain JOIN?
d may benefit from INDEX(store_id, name)
I have 3 tables in my database
companies{
id,
name,
address
}
stores{
id,
name,
address,
company_id
}
invoices{
id,
total,
date_time,
store_id
}
As you can see, each store is connected to a company via foreign key, also each invoice is connected to a store.
My question is, how can i write a SQL query which will return all stores by a company and order them by their turnover?
If i use the query:
SELECT s.*,
sum(i.total) as turnover FROM store s
JOIN invoices i
ON i.store_id = s.id
WHERE YEAR(i.date_time) = 2019;
I can see the turnover for one store for a year 2019 for example, but i'm struggling to find a way to get a list of store ordered by their turnover for a certain period.
You're going to need to join all 3 tables:
SELECT *
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
That's your entire raw data in detailed list. Then you say you want it for a certain company only:
SELECT *
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
Then you only want the stores and the invoices amounts:
SELECT s.name, i.total
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
Then you want a row set where each line is a single store and the sum of all invoices for that store:
SELECT s.name, SUM(i.total)
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
GROUP BY s.name
Lastly you want them in descending order, highest total first:
SELECT s.name as storename, SUM(i.total) as turnover
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
GROUP BY s.name
ORDER BY turnover DESC
The order of evaluation in sql is FROM(with joins), WHERE, GROUP BY, SELECT, ORDER BY which is why I use different names in eg the order by than I do in the group by. Conceptually your db only sees the names of things as output by the immediately previous operation. Mysql isn't actually too picky but some db are - you couldn't say GROUP BY storename in sql server because the SELECT that creates the storename alias hasn't been run at the time the group by is done
Note: I wasn't really sure on what you were looking for in a WHERE - you started by saying "all stores turnover for a certain company" and finished saying you were "struggling to get turnover for a period"
If you want a period, use eg WHERE somedatecolumn BETWEEN '2000-01-01' AND '2000-12-31' (Between is inclusive) or WHERE somedatecolumn >= '2000-01-01' AND somedatecolumn < '2001-01-01' (A good pattern to use if the date includes a time too). It is almost never wise to call a function on a column you're searching with, ie do not do WHERE YEAR(somedatecolumn) = 2000 because it disables indexing on the column and makes the search very slow
I am having trouble to answer this question :
Display the last name and client company (COMPANY) for the employees who made a sale for clients of Paris" sorted on the Company Name.
Here is my code :
select NAME from EMPLOYEES inner join SALES on EMPLOYEES.NO_EMPLOYEES = SALE.NO_EMPLOYEES
union
select COMPANY from SALES inner join CLIENTS on SALES.CODE_CLIENT = CLIENTS.CODE_CLIENT where CLIENTS.CITY = 'Paris'
Problem is that union gives me only one column... How to solve this issue ?
Thanks for your help !
I think you just want two joins:
select e.NAME, c.COMPANY
from EMPLOYEES e join
SALES s
on e.NO_EMPLOYEES = s.NO_EMPLOYEES join
CLIENTS c
on s.CODE_CLIENT = c.CODE_CLIENT
where c.CITY = 'Paris';
I am trying to build a query to get the latest record which could be used in MySQL, Oracle 10 and 12.
Scenario:
I have 4 tables - customer, address, loan, application
I wanted to join these 4 tables to get the customer name and address for an application.
customer, loan and application have 1 to 1 relationship
while customer and address has 1 to many relationship.
select count(c.name)
from application
left join loan on (loan.id = application.id)
inner join customer on (loan.cust_num = customer.id);
This gives 100 rows;
select count(c.name)
from application
left join loan on (loan.id = application.id)
inner join customer on (loan.cust_num = customer.id)
inner join address a1 on (loan.cust_num = address.cust_num)
inner join (
select max(date) as max_date, cust_num
from address
where address_type = 'studio'
group by cust_num
) a2
on a1.cust_num = a2.cust_num
and a1.date = a2.max_date;
This gives 200 records since in address for a date there are more than 1 records whose address_type is 'studio' for a customer.
How to get the latest record.
At the end I wanted address columns from address table and name from customer table for a application.
Thanks.
For code that works in both Oracle and MySQL, I would recommend a correlated subquery:
select count(c.name)
from application a join
loan l
on o.id = a.id join
customer c
on l.cust_num = c.id left join
address ad
on ad.cust_num = l.cust_num and
ad.address_type = 'studio' and
ad.date = (select max(ad2.date)
from address ad2
where ad2.cust_num = ad.cust_num and
ad2.address_type = ad.address_type
);
If you are using MySQL 8+ or don't need MySQL, then you should go with row_number() as Tim suggests.
Note that I removed the left join. The join to customer is using a field from loan, which turns the outer join into an inner join. You might as well express the logic actually being implemented.
I am trying to retrieve names and address of all guests with bookings for a hotel in London, alphabetically ordered by name in MySQL using subqueries and getting this Error:
Error Code: 1242. Subquery returns more than 1 row
Here's the query that I run:
select * from guest
where guest_no =
(
select guest_no
from booking
where hotel_no = (select hotel_no
from hotel
where city = 'London')
);
and here's the schema for hotel, booking and guest:
hotel (hotel_no, hotel_name, city)
booking (hotel_no, guest_no, date_from, date_to, room_no)
guest (guest_no, g_name, g_address)
additionally, here's the schema for room:
room (room_no, hotel_no, type, price)
Please help me with the above mentioned error and possible solutions.
Thanks and regards.
why not use join as
select
g.guest_no,
g.g_name,
g.g_address
from guest g
inner join booking b on b.guest_no = g.guest_no
inner join hotel h on h.hotel_no = b.hotel_no
where h.city = 'London'
When you use '=', it means that the result of your subquery is exactly 1 row. If you expect multiple results, you need to use the IN keyword, like so:
select * from guest where guest_no IN (select guest_no from booking where hotel_no IN (select hotel_no from hotel where city = 'London'));
EDIT: As #flaschenpost mentions, the performance could be degraded in case there is no proper indexing on the columns involved in the subqueries. You would probably do well to use JOIN rather than such nested subqueries.
Change you query to
select * from guest
where guest_no IN
(select guest_no from booking where hotel_no
IN (select hotel_no from hotel where city = 'London'));
you need joins !
try this !
select a.g_name,a.g_address from guest a inner join booking b on a.guest_no=b.guest_number inner join hotel h on b.hotel_no=h.hotel_no inner join rooms r on b.room_no=h.room_no
where h.city='London'