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

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;

Related

How can i get all the MAX values from a certain column in a dataset in PostgreSQL

I'm asked to find the top user for different countries, however, one of the countries has 2 users with the same amount spent so they should both be the top users, but I can't get the max value for 2 values in this country.
Here is the code:
WITH t1 AS (
SELECT c.customerid,SUM(i.total) tot
FROM invoice i
JOIN customer c ON c.customerid = i.customerid
GROUP BY 1
ORDER BY 2 DESC
),
t2 AS (
SELECT c.customerid as CustomerId ,c.firstname as FirstName,c.lastname as LastName, i.billingcountry as Country,MAX(t1.tot) as TotalSpent
FROM t1
JOIN customer c
ON c.customerid = t1.customerid
JOIN invoice i ON i.customerid = c.customerid
GROUP BY 4
ORDER BY 4
)
SELECT *
FROM t2
BILLINGCOUNTRY is in Invoice, and it has the name of all the countries.
TOTAL is also in invoice and it shows how much is spent for each purchase by Customer (so there are different fees and taxes for each purchase and total shows the final price payed by the user at each time)
Customer has id,name,last name and from its' ID I'm extracting the total of each of his purchases
MAX was used after finding the sum for each Customer and it was GROUPED BY country so that i could find the max for each country, however I can't seem to find the max of the last country that had 2 max values
Use rank() or dense_rank():
SELECT c.*, i.tot
FROM (SELECT i.customerid, i.billingCountry, SUM(i.total) as tot,
RANK() OVER (PARTITION BY i.billingCountry ORDER BY SUM(i.total) DESC) as seqnum
FROM invoice i
GROUP BY 1, 2
) i JOIN
customer c
ON c.customerid = i.customerid
WHERE seqnum = 1;
The subquery finds the amount per customer in each country -- and importantly calculates a ranking for the combination with ties having the same rank. The outer query just brings in the additional customer information that you seem to want.
here is how it worked for me since i was restricted from using many Commands such RIGHT JOIN and RANK() (As what Gordon Linoff suggessted) so i had to create a 3rd case for the anamoly and join it using union. this solution works only on this case, the good solution is the one posted by Gordon Linoff:
WITH t1 AS (
SELECT c.customerid,SUM(i.total) tot
FROM invoice i
JOIN customer c ON c.customerid = i.customerid
GROUP BY 1
ORDER BY 2 DESC
),
t2 AS (
SELECT c.customerid as CustomerId ,c.firstname as FirstName,c.lastname as LastName, i.billingcountry as Country,MAX(t1.tot) as TotalSpent
FROM t1
JOIN customer c
ON c.customerid = t1.customerid
JOIN invoice i ON i.customerid = c.customerid
GROUP BY 4
ORDER BY 4
) ,
t3 AS (
SELECT DISTINCT c.customerid as CustomerId ,c.firstname as FirstName,c.lastname as LastName, i.billingcountry as Country,t1.tot as TotalSpent
FROM t1
JOIN customer c
ON c.customerid = t1.customerid
JOIN invoice i ON i.customerid = c.customerid
WHERE i.billingcountry = 'United Kingdom'
ORDER BY t1.tot DESC
LIMIT 2
)
SELECT *
FROM t2
UNION
SELECT * FROM t3
ORDER BY t2.country

Find the highest amount although there are more than one achieving the same amount

I want to find the best customer for each country despite there is one country has two customers the same amount, I want them both to appear.
select customerid,firstname,lastname,country, max(total_amt)
from (select invoice.customerid, customer.firstname,lastname,
sum(invoice.total)total_amt,customer.country
from invoice
join customer
on customer.customerid= invoice.customerid
group by invoice.customerid,customer.country)t2
group by country;
Use window functions:
select c.*
from (select c.country, c.customerid, c.firstname c.lastname, sum(i.total) as total,
dense_rank() over (partition by c.country order by sum(i.total) desc) as seqnum
from customer c join
invoice i
on c.customerid = i.customerid
) c
where seqnum = 1;
Note that I also introduced window functions so the query is easier to write and to read.

How return two equal max values for the same country when the query is grouped by the country?

For example I have to write a query that shows the customer who had spent the most in each country but if a country has two customers with same max value i have to show them both in the output.
I have wrote the query that return the maximum value for each customer in each country but the last country in my example which is 'United Kingdom' has two customers with same maximum values and i couldn't show them both.
SELECT c1.CustomerId, c1.FirstName,c1.LastName,c1.Country,
MAX(c1.TotalSpent) as TotalSpent
FROM
(SELECT c.CustomerId,c.FirstName, c.LastName,i.BillingCountry
Country, SUM(i.Total) totalspent
FROM Customer c
JOIN Invoice i
ON c.CustomerId = i.CustomerId
GROUP BY 1
ORDER BY totalspent
) c1
GROUP BY 4
ORDER BY Country
Use window functions!:
SELECT c.*
FROM (SELECT c.CustomerId, c.FirstName, c.LastName, i.BillingCountry as Country,
SUM(i.Total) as totalspent,
DENSE_RANK() OVER (PARTITION BY i.BillingCountry ORDER BY SUM(i.Total) DESC) as seqnum
FROM Customer c JOIN
Invoice i
ON c.CustomerId = i.CustomerId
GROUP BY c.CustomerId, c.FirstName, c.LastName, i.BillingCountry
) c
WHERE seqnum = 1
ORDER BY Country;
This also fixes your GROUP BY clauses so they are consistent with the columns being selected.

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