SQL Query Issue - Natural Join and table naming - sql

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

Related

What customers who bought the biggest amount of a product in one order?

I have 2 tables:
CUSTOMERS (ID, FIRSTNAME, LASTNAME, ADDRESS);
ORDERS (ID, PRODUCT_NAME, PRODUCT_PRICE, DATE_ORDER DATE, ID_CUSTOMER, AMOUNT);
Get the first and last names of the customers who bought the biggest amount of a product in one order.
The orders without customer should not be considered. Please sort by FIRSTNAME and LASTNAME
SELECT firstname, lastname
FROM customers
LEFT JOIN orders ON customers.id = orders.id_customer
GROUP BY customers.firstname
HAVING (SELECT MAX(orders.amount) FROM orders);
What I'm doing wrong? thx
If you want one customer with the largest value of amount, then simplest method is order by and then fetching only one row:
select c.firstname, c.lastname
from customers c join
orders o
on c.id = o.id_customer
order by o.amount desc
fetch first 1 row only;
If you want the biggest amount per product, then window functions are the right approach
select pc.*
from (select o.product, c.firstname, c.lastname, o.amount,
rank() over (partition by o.product order by o.amount desc) as seqnum
from customers c join
orders o
on c.id = o.id_customer
) pc
where seqnum = 1;

How could I use the select statement on feature from a subquery ? (Postgree)

I'm training for an interview and trying to solve a query, I would like to find for each city who is the client who spent the most. I got the good result the max spent by city but I get an error when I'm trying to retrieve the name and lastname of my customer who spent this amount. Is there an efficient way to do it ? Thank you!
select max(total_payment),X.city, X.firstname, X.lastname
from (
select sum(amount) as total_payment, c.customer_id, cit.city_id, cit.city as city, c.first_name as firstname, c.last_name as lastname
from payment p
inner join customer as c on p.customer_id=c.customer_id
inner join address as ad on c.address_id=ad.address_id
inner join city as cit on ad.city_id=cit.city_id
group by c.customer_id, cit.city_id
order by city
) as X
group by X.city
Target result column:
The name and last name of the customer who spent the most for each city.
120,Paris,Nicolas, Dupont
130, Madrid, Raul, Garcia
70, London,Dave, Goldman
You want window functions:
select cc.*
from (select sum(p.amount) as total_payment, c.customer_id, cit.city_id,
cit.city as city, c.first_name as firstname, c.last_name as lastname,
row_number() over (partition by cit.city order by sum(p.amount) desc) as seqnum
from payment p join
customer c
on p.customer_id = c.customer_id join
address ad
on c.address_id = ad.address_id join
city cit
on ad.city_id = cit.city_id
group by c.customer_id, cit.city_id
) cc
where seqnum = 1;
Note that your query has two errors that should fail any interview:
You are using ORDER BY in a subquery. According to the standard and most databases, ORDER BY is either not allowed or ignored.
In your outer query, the GROUP BY columns are inconsistent with the unaggregated SELECT columns. Once again, this violates the standard and most databases return a syntax error.

What is the valid SQL query for the following JOIN problem

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

how to select duplicated column value in sql

Write a query that determines the customer that has spent the most on music for each country. Write a query that returns the country along with the top customer and how much they spent. For countries where the top amount spent is shared, provide all customers who spent this amount.
You should only need to use the Customer and Invoice tables.
i want to select the customer with the maximum money spent for each country and there is two customers have the same money spent and the same country so when using group by country i got only 1 customer what should i do ?
select c.CustomerId,c.FirstName,c.LastName, c.Country , max(c.Invoices) as TotalSpent
from
(select * , sum(i.Total) as 'Invoices'
from Customer d
join Invoice i on i.CustomerId = d.CustomerId
group by i.CustomerId
) c
group by c.Country
the table i got is the same expected table except 1 customer
Consider joining unit level with two aggregate queries: 1) first to calculate total amount by CustomerId and Country and 2) second to calculate max total amount by Country.
Below assumes your database supports Common Table Expression (CTE) using the WITH clause (nearly supported by all major commercial or open-source RDBMS's). CTE here avoids the need to repeat sum_agg as a subquery.
with sum_agg AS (
select i.CustomerId, sub_c.Country, sum(i.Total) as Sum_Amount
from Customer sub_c
join Invoice i on i.CustomerId = sub_c.CustomerId
group by i.CustomerId, sub_c.Country
)
select c.CustomerId, c.FirstName, c.LastName, c.Country, max_agg.Max_Sum
from Customer c
join sum_agg
on c.CustomerId = sum_agg.Customer_Id and c.Country = sum_agg.Country
join
(select Country, max(Sum_Amount) as Max_Sum
from sum_agg
group by Country
) max_agg
on max_agg.Country = sum_agg.Country and max_agg.Max_Sum = sum_agg.Sum_Amount
Your inner query is almost correct. It should be
select d.*, sum(i.Total) as Invoices
from Customer d
join Invoice i on i.CustomerId = d.CustomerId
group by d.CustomerId
It is allowed to use d.* here, as we can assume d.CustomerId to be the table's primary key, so all columns in the table are functionally dependent on it. If we grouped by d.country instead for instance, that would not be the case and d.* would be forbidden in the select clause (as well as d.firstname etc.). We can only select columns we grouped by (directly or indirectly) and aggregates such as MIN, MAX, SUM etc.
This query gives you the totals per customer along with the customers' countries.
But then you are taking this result and group by country. If you do this, you can only access country and its aggregates. Selecting c.CustomerId for instance is invalid, as there is no the customer ID per country. If your DBMS allows this, it it flawed in this regard and you get a kind of random result.
If your DBMS features window functions, you can get the maximum amounts per country on-the-fly:
select customerid, firstname, lastname, country, invoices
from
(
select
c.*,
sum(i.total) as invoices,
max(sum(i.total)) over (partition by c.country) as max_sum
from customer c
join invoice i on i.customerid = c.customerid
group by c.customerid
) per_customer
where invoices = max_sum
order by country, customerid;
Otherwise you'd have to use your inner query twice, once to get the country totals, once to get the customers matching these totals:
select
c.customerid, c.firstname, c.lastname, c.country,
sum(i.total) as invoices
from customer c
join invoice i on i.customerid = c.customerid
group by c.customerid
having (c.country, invoices) in
(
select country, max(invoices)
from
(
select
--c.customerid, <- optional, it may make this query more readable
c.country,
sum(i.total) as invoices
from customer c
join invoice i on i.customerid = c.customerid
group by c.customerid
) per_customer
);

Subquery "Finding customer who has the most purchases"

I'm having trouble creating a query with a sub query to find the one customer in my DB who has made the most purchases. I need to list his/her full name, product name, price and quantity. Here is what I have so far
select first_name ||' '|| last_name "FullName", pt.name p.price, sum(ps.quantity)
from customers c
join purchases ps on c.customer_id = ps.customer_id
join products p on p.product_id = ps.product_id
join product_types pt on p.product_type_id = pt.product_type_id;
I need to use these three tables
Customers Table
Customer_ID
First_Name
Last_Name
DOB
Phone
Purchases Table
Product_ID
Customer_ID
Quantity
Products Table
Product_ID
Product_Type_ID
Name
Description
Price
Product Types Table
Product_Type_ID
Name
I am confused as where I should place the sub query (in the select row, from, having or where clause), if the arithmetic function should be placed in the select outer query or sub query. I know there are Nested subqueries, Correlated subqueries, Multiple-column subqueries, Multiple-row subqueries, Single-row subqueries. By the way, I am trying to do this in Oracle.
Here is an image with my result, except I removed sum from quantity column. Also, updated link.
(http://i1294.photobucket.com/albums/b618/uRsh3RRaYm0nD/Capture100_zps1f951b07.jpg)
I'm sorry, I forgot to include a fourth table, as you can see there are two name columns, in products table and product type table. The difference is that in products table "Name" is the specific name of the product, as shown in my link. The product type "Name" column is the more general name of the product, such as books, dvds, cds, etc. I need to include the product type "Name column in my query not product's name column. Therefore, the end result should look something like this
FullName ProductTypeName Price Quantity
John Brown Book Sumof4books 4
John Brown DVD Sumof2DVDs 2
John Brown Magazine Sumof1Mag 1
Here's one way to do it. It uses an analytic function to order customers by the total quantity of purchases: row_number() over (order by sum(quantity) desc). If there's more than one person with the same quantity, this will pick out only one.
It then takes this customer id and joins the rest of the tables in the obvious way to get the break down by product type.
Select
c.FullName,
pt.name,
Sum(p.price * ps.quantity) price,
sum(ps.quantity) quantity
From (
Select
c.Customer_ID,
c.first_name ||' '|| c.last_name FullName,
row_number() over (order by Sum(Quantity) desc) r
From
Purchases ps
Inner Join
Customers c
On ps.Customer_ID = c.Customer_ID
Group By
c.Customer_ID,
c.first_name ||' '|| c.last_name
) c
Inner Join
Purchases ps
On c.Customer_ID = ps.Customer_ID
Inner Join
Products p
On ps.Product_ID = p.Product_ID
Inner Join
Product_Types pt
On p.Product_Type_ID = pt.Product_Type_ID
Where
c.r = 1
Group By
c.FullName,
pt.name
Example Fiddle
For the second problem (show the customer who has the highest quantity for each product type, together with what they've spent on that product type)
Select
c.FullName,
c.name,
c.price,
c.quantity
From (
Select
c.first_name ||' '|| c.last_name FullName,
pt.name,
sum(p.price * ps.quantity) price,
sum(ps.quantity) quantity,
row_number() over (partition by pt.name order by Sum(Quantity) desc) r
From
Purchases ps
Inner Join
Customers c
On ps.Customer_ID = c.Customer_ID
Inner Join
Products p
On ps.Product_ID = p.Product_ID
Inner Join
Product_Types pt
On p.Product_Type_ID = pt.Product_Type_ID
Group By
c.first_name ||' '|| c.last_name,
pt.name
) c
Where
c.r = 1
Example Fiddle
Here is the general idea. You can adapt it for your database tables.
select fred, barney, maxwilma
from bedrock join
(select max(wilma) maxwilma
from bedrock
group by fred ) flinstone on wilma = maxwilma
SELECT CLIENT.CLIENTNO, CLIENT.CNAME, SUM(PURCHASE.AMOUNT) AS AMOUNT
FROM CLIENT
INNER JOIN PURCHASE
ON CLIENT.CLIENTNO = PURCHASE.CLIENTNO
WHERE CLIENT.CLIENTNO IN (
SELECT CLIENTNO
FROM (
SELECT PURCHASE.CLIENTNO, SUM(PURCHASE.AMOUNT) AS AMOUNT
FROM PURCHASE
GROUP BY PURCHASE.CLIENTNO
ORDER BY AMOUNT DESC
)
WHERE ROWNUM = 1)
GROUP BY CLIENT.CLIENTNO, CLIENT.CNAME;
select first_name ||' '|| last_name "FullName",name,quantity from customers,purchases,products where products.product_id = purchases.product_id and purchases.customer_id = customers.customer_id order by quantity;
This is the query you want