Max after group by with sum - sql

I’m trying to get from this two tables customer and invoice this information: the total from invoice for every year for each country! So what I wrote is:
SELECT SUM(i.total) AS Total_invoice, strftime(‘%Y’, i.InvoiceDate) AS year, c.country
FROM invoice i JOIN
customer c
ON i.CustomerId = c.CustomerId
GROUP BY 2,3
ORDER BY 1 DESC;
So I get the total of invoice for each year! Now if I want the max(total) for each country how do you do? For example I’d like to have for each country the max( total), the year and the country! Could you help? Tha k you very much enter image description here

For example I’d like to have for each country the max( total), the year and the country!
For this, use window functions:
SELECT yc.*
FROM (SELECT SUM(i.total) AS Total_invoice, strftime(‘%Y’, i.InvoiceDate) AS year, c.country,
ROW_NUMBER() OVER (PARTITION BY c.country ORDER BY SUM(i.total) DESC) as seqnum
FROM invoice i JOIN
customer c
ON i.CustomerId = c.CustomerId
GROUP BY 2, 3
) yc
WHERE seqnum = 1
ORDER BY 1 DESC;

Related

Calculating the average of order value without using a WITH statement

I am trying to add a new column to my table which will be the average value calculated as the division of two existing columns. Therefore Average value = Total Sales / Number of Orders.
My data looks like this:click to view picture
I don't understand why Example Code A does not work but Example Code B does. Please can someone explain?
Example Code A
%%sql
SELECT
c.country,
count(distinct c.customer_id) customer_num,
count(i.invoice_id) order_num,
ROUND(SUM(i.total),2) total_sales,
order_num / total_sales avg_order_value
FROM customer c
LEFT JOIN invoice i ON c.customer_id = i.customer_id
GROUP BY 1
ORDER BY 4 DESC;
Example Code B
%%sql
WITH
customer_sales AS
(
SELECT
c.country,
count(distinct c.customer_id) customer_num,
count(i.invoice_id) order_num,
ROUND(SUM(i.total),2) total_sales
FROM customer c
LEFT JOIN invoice i ON c.customer_id = i.customer_id
GROUP BY 1
ORDER BY 4 DESC
)
SELECT
country,
customer_num,
order_num,
total_sales,
total_sales / order_num avg_order_value
FROM customer_sales;
Thank you!
Depending on the DBMS some allow you to reference the alias in the calculation (in the same select) and others require you to either bring it outside in an outer query or state your previous aggregation/functions, such as counts or sums.
SELECT
c.country,
count(distinct c.customer_id) customer_num,
count(i.invoice_id) order_num,
ROUND(SUM(i.total),2) total_sales,
count(i.invoice_id) / ROUND(SUM(i.total),2) avg_order_value
FROM customer c
LEFT JOIN invoice i ON c.customer_id = i.customer_id
GROUP BY 1
ORDER BY 4 DESC;

Finding MAX of SUM of sales while incorporating field from separate table

I really need some help regarding max and sum for two tables. So there are two tables.
Supplies
+-------------+------+----------+
|supplier_id | city | supplier |
+-------------+------+----------+
Orders
+----------+-------------+------------+----------+------+
| order_id | supplier_id | order_date | sale_amt | city |
+----------+-------------+------------+----------+------+
I need to find the top supplier (maximum combined sales amount) in each city in this format:
+------+----------+----------+
| city | supplier | sale_amt |
+------+----------+----------+
The ISSUE
I can find the sum of the sales amount along with city and supplier name(supplier) fine.
But cannot derive the MAXIMUM of those sales because using (group by) for BOTH city and supplier gives me multiple suppliers per city where I need only the TOP supplier for EACH city.
If just city name was needed it wouldn't be an issue, but needing the supplier name causes this problem.
This is the query I'm using right now and what I have so far.
SELECT s.city, s.supplier, SUM(sale_amt)
FROM Orders o
LEFT JOIN Supplies s
ON o.supplier_id = s.supplier_id
GROUP BY s.city, s.supplier
ORDER BY s.city
Your help and guidance would be much appreciated.
EDIT: After trying all your solutions and hitting snags here or there, came up with my own messy code below. This definitely has holes and could be written much better.
SELECT A.city, B.supplier, A.max_sale_amt
FROM
(
SELECT city, MAX(tot) as max_sale_amt
FROM (
SELECT supplier_id, SUM(sale_amt) as tot
FROM Orders
GROUP BY supplier_id ) as D
LEFT JOIN Supplies s
ON D.supplier_id = Supplies.id
GROUP BY city
) A,
(
SELECT s.city, s.supplier, SUM(sale_amt) as sum_amt
FROM Orders o, Supplies s
WHERE o.supplier_id = s.id
GROUP BY s.supplier, s.city
) B
WHERE A.city= B.city AND A.max_sale_amt= B.sum_amt
You may use ROW_NUMBER here:
SELECT TOP 1 WITH TIES s.city, s.supplier, SUM(sale_amt)
FROM Orders o
LEFT JOIN Supplies s ON o.supplier_id = s.supplier_id
GROUP BY s.city, s.supplier
ORDER BY ROW_NUMBER() OVER (PARTITION BY s.city ORDER BY SUM(sale_amt) DESC);
You can use the analytical function DENSE_RANK as follows:
select city, supplier, sm_sales_amount from
(SELECT s.city, s.supplier, SUM(sale_amt) as sm_sales_amount,
DENSE_RANK() OVER (PARTITION BY s.city ORDER BY SUM(sale_amt) DESC) as rn
FROM Orders o
LEFT JOIN Supplies s ON o.supplier_id = s.supplier_id
GROUP BY s.city, s.supplier) t
where rn = 1;
maybe this works
SELECT s.city
,s.supplier
,max(sale_amt)
FROM Orders o
,Supplies s
WHERE o.supplier_id = s.supplier_id(+)
GROUP BY s.city
,s.supplier
ORDER BY s.city
I haven't tested this because I am too lazy to make the tables but I'd be using a temp table for an interim step either using a desc sort and then select top records, or using a max selection from the temp. Approximately something like this
SELECT city, supplierid, SUM(sale_amt)
INTO #SumSupPerCity
FROM Orders o
GROUP BY city, supplierid
ORDER BY SUM(sale_amt) DESC;
SELECT Top 1 *
FROM #MaxSumSupPerCity;

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.