What is the valid SQL query for the following JOIN problem - sql

I have the following tables
Customer (CID, name, address)
Orders (CID, BID, onDate, quantity)
Device (DID, title, IMEI, price, MID)
Manufacturer (MID, name, address)
What SQL statement would allow me to retrieve the ID's and names of all
the customers who have spent atleast X amount (lets say 350 for example)
on devices made my the manufacturer "Sony". The list should include the total amount of money spent by each customer on those devices.

I assume that the table Orders contains a column DID (BID is a typo, right?) that relates it to the table device.
Join the tables, group by customer to aggregate and set the condition in the HAVING clause:
select c.CID, c.name,
sum(o.quantity * d.price) total_amount
from customer c
inner join orders o on o.CID = c.CID
inner join device d on d.DID = o.DID
inner join manufacturer m on m.MID = d.MID
where m.name = 'Sony'
group by c.CID, c.name
having sum(o.quantity * d.price) >= 350

Related

Display 2 columns from one table having max count in column 3 and display computed sum of values from another table

I've a customer table and purchases table,
need to show cname, cid with max(customer_visits) from customer table
and sum of total_purchases by customer in purchases table.
I'm doing something like this
select p.cid, c.cname, sum(p.total_price)
from customers c where exists
(select max(visits_made) from customers having visits_made=max(visits_made)
and cid=p.cid)
inner join purchases p on p.cid=c.cid
group by p.cid,c.cname
and
select p.cid, c.cname, sum(p.total_price)
(select max(visits_made) from customers c where c.cid=p.cid)
from purchases p
inner join customers c on c.cid=p.cid
group by p.cid,c.cname
What's going wrong with these queries?
Found the solution, had to include where clause after inner join :D
I think this is just an aggregation query:
select p.cid, c.cname, sum(p.total_price) as total_price,
max(visits_made) as visits_made
from purchases p inner join
customers c
on c.cid = p.cid
group by p.cid, c.cname;

SQL Server Aggregation using Max() and obtaining details from max() line.

I just took a final exam and one of the questions asked me to double join three tables, and report the max sale payout for each salesperson.
The tables have the following variables:
Salesperson(id, name)
Order(orderid, order_date, Cust_id, Saleperson_id, amount)
Customer(id, name)
After joining:
select salesperson.Name, Orders.Number, customer.Name, Orders.Amount
from Orders
join salesperson
on orders.Salesperson_id = salesperson.ID
join Customer
on customer.ID = orders.cust_id
What the instructed wanted was for me to find each salesperson's maximum sell (as found by order.amount). He also wanted the salesperson (salesperson.name), the order number of the max sale (orders.number), the customer the sale was with (customer.name), and the max sale amount. What is the most efficient way to do this problem? I have tried to use "group by salesperson.name", but I cannot because the orders.number and customer.name are never held in the aggregation.
I finished the problem this way
select
salesperson.name as Sales_Person,
orders.number as Order_Number,
customer.Name as Customer_Name,
orders.Amount as Sale_Amount
from salesperson
left join Orders
on salesperson.ID = orders.Salesperson_id
left join Customer
on orders.cust_id = customer.ID
where orders.Amount in (select max(orders.Amount)
from salesperson
join Orders
on salesperson.ID = orders.Salesperson_id
join Customer
on orders.cust_id = customer.ID
group by salesperson.name)
I know this is a bad way to do it. For instance, what if two different salesperson's max sale was equivalent? Max and min are not like count and sum because it is picking out one line from a aggregation, but the rules still apply. Also, you might notices that there is no real unique identifier in the joined table other than order.number which is not useful. Therefore, I would have to use some composite of salesperson.name and order.number.
Also, what do I do if I have to pick the top three sales for each salesperson? Should such an output be totally different code-wise than what would be required from just the first sale?
I keep bumping me head against this problem, and I would love to have a more professional approach to this problem.
SELECT
M.max_amount,
S.Name,
O.Number,
C.Name
FROM orders O
JOIN salesperson S
ON S.Salesperson_id = O.Salesperson_id
JOIN customer C
ON C.Customer_id = O.Customer_id
JOIN (
SELECT Salesperson_id, MAX(amount) max_amount
FROM Order
GROUP BY Salesperson_id
) M
ON M.Salesperson_id = O.Salesperson_id AND M.max_amount = O.amount
For the top 3:
SELECT
M.Amount,
S.Name,
O.Number,
C.Name
FROM orders O
JOIN salesperson S
ON S.Salesperson_id = O.Salesperson_id
JOIN customer C
ON C.Customer_id = O.Customer_id
CROSS APPLY (
SELECT TOP 3 Amount
FROM Order
WHERE Salesperson_id = O.Salesperson_id
ORDER BY Amount DESC
) M

SQL: How to display a column not in group by expression?

I have the following code:
select O.ONO
from Customers C, Odetails Od, Orders O, Parts P
where P.PNO=Od.PNO and Od.ONO=O.ONO and O.CNO=C.CNO
group by O.ONO
order by sum(Od.QTY*P.PRICE)desc;
However, instead of selecting the O.ONO column, I need to select the C.CNAME from a different table. How would I go about doing that?
If what you want is a list of Customer Names ordered by the sum of their orders then just change your query from
select O.ONO
...
group by O.ONO
to
select C.CName
...
group by C.CName
This will display the customer name and the order number. NOTE: This will repeat the customer name as many times as there are orders for that customer
Also, my preferred style of SQL queries is
select C.CName
from Customers C
INNER JOIN Orders O ON C.CNO = O.CNO
INNER JOIN Odetails OD ON O.ONO = OD.ONO
INNER JOIN Parts P ON OID.PNO = P.PNO
where P.PNO=Od.PNO and Od.ONO=O.ONO and O.CNO=C.CNO
group by C.CName
order by sum(Od.QTY*P.PRICE)desc;
An additional suggestion to clarify your code. (please note: I'm not an oracle person), in my company (SqlServer) generally you would have the table named after the entity, with the primary key being just Id, and then any property named without a prefix. For instance does OID refer to OrderId or OfficeId?
EG:
Customer:
Id
Name
Order:
Id
CustomerId
Then your queries are much more readable:
SELECT Name
FROM Customer
INNER JOIN Order ON Customer.Id = Order.CustomerId
WHERE ...
ORDER BY Customer.Name
Please do like this,
select C.CNAME from Customers C, Odetails Od, Orders O, Parts P where P.PNO=Od.PNO and Od.ONO=O.ONO and O.CNO=C.CNO group by C.CNAME order by sum(Od.QTY*P.PRICE) desc;
The select statement will select the column specified
SELECT C.CNAME
FROM Customers C, Odetails Od, Orders O, Parts P
WHERE P.PNO=Od.PNO and Od.ONO=O.ONO and O.CNO=C.CNO
GROUP BY C.CNAME
ORDER BY sum(Od.QTY*P.PRICE)desc;
If you wish to still keep O.ONO column selected as well
SELECT C.CNAME, O.ONO
FROM Customers C, Odetails Od, Orders O, Parts P
WHERE P.PNO=Od.PNO and Od.ONO=O.ONO and O.CNO=C.CNO
GROUP BY C.CNAME
ORDER BY sum(Od.QTY*P.PRICE)desc;

SQL Query Issue - Natural Join and table naming

I'm running into some difficulty with a query for my databases class. Given the following schema:
Customers (customerid, first_name, last_name, address, city, state, phone, status)
Branches (branchno, address, city, state, phone, manager_name)
Employees (empno, firstname, lastname, address, city, state, phone, emergency_contact, title, managerno)
Rooms (roomno, branchno, price, bed_size)
Bookings (roomno, branchno, customerid, checkin_date, checkout_date, empno)
I'd like to find the customer(s) that have rented the most expensive room. I gave this query a try...
SELECT customerid FROM bookings NATURAL JOIN rooms
EXCEPT
(SELECT customerid FROM (bookings NATURAL JOIN rooms) AS S, (bookings NATURAL JOIN
rooms) as T WHERE S.price < T.price)
The problem comes from the way I want to rename the tables. I'd like to use the natural join of bookings and rooms as components of the Cartesian product... How can I do this?
Thank you very much.
You could use this:
SELECT
customerid
FROM
Bookings
NATURAL JOIN
Rooms
NATURAL JOIN
( SELECT MAX(price) AS price
FROM Rooms
) AS MostExpensiveRoom
Your query seems valid, except that you need to clarify which customerid you want in the second subquery, the S. or the T. one. The comma , syntax means a CROSS JOIN between S and T so you have two customerids:
(SELECT customerid FROM bookings NATURAL JOIN rooms)
EXCEPT
(SELECT S.customerid
FROM
(bookings NATURAL JOIN rooms) AS S
CROSS JOIN
(bookings NATURAL JOIN rooms) AS T
WHERE S.price < T.price
)
SELECT * FROM customers as c
INNER JOIN bookings as b ON b.customerid = c.customerid
INNER JOIN rooms as r ON r.roomno = b.roomno
ORDER BY r.price DESC
LIMIT 1;
if you want only the names, or specified fields, you can use GROUP BY.
This query do the same (if the previous is good syntactically):
SELECT * FROM customers,bookings,rooms
WHERE bookings.customerid = customers.customerid
AND rooms.roomno = bookings.roomno
ORDER BY rooms.price DESC
LIMIT 1
so if you want id-s and names ordered by rent price desc:
SELECT customers.customerid, customers.fistname, customers.lastname FROM
customers,bookings,rooms
WHERE bookings.customerid = customers.customerid
AND rooms.roomno = bookings.roomno
ORDER BY rooms.price DESC
GROUP BY customers.customerid, customers.fistname, customers.lastname
LIMIT 10

question about SQL query

I'm working on a small project involving oracle database,
and I have the following tables:
CUSTOMER ( Cid, CName, City, Discount )
PRODUCT ( Pid, PName, City, Quantity, Price )
ORDERS ( OrderNo, Month, Cid, Aid, Pid, OrderedQuantity, Cost )
How can retrieve the names of all customers who ordered all the products?
For example if customer x ordered product1, product2 and product3 (which are all the products the company offers) he will be selected. And if customer y only ordered product 1 and 2 but not 3 he will not be selected.
How can I achieve this?
You want "relational division".
select *
from customer c
where not exists( -- There are no product
select 'x'
from product p
where not exists( -- the customer did not buy
select 'x'
from orders o
where o.cid = c.cid
and o.pid = p.id));
or
select c.cid
,c.name
from customer c
join orders o using(cid)
group
by c.id
,c.name
having count(distinct o.pid) = (select count(*) from product);
Here is a great article by Joe Celko that shows several ways of implementing relational division (and variations): Divided We Stand: The SQL of Relational Division
You can use group by and use a having clause to demand that the customer has ordered all products there are:
select c.CName
from Customers c
join Orders o
on o.Cid = c.Cid
group by
c.Cid
, c.CName
having count(distinct o.Pid) = (select count(*) from products)
IMHO more readable than the "relational divison" approach, but less efficient.