Calculating the average of order value without using a WITH statement - sql

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;

Related

Max after group by with sum

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;

Include the sales revenue amount into the select query

I also have a (somewhat) similar scenario as from this guy.
This is my current code:
SELECT Vendor_Name, Product
FROM (
SELECT v.Vendor_Name, p.Description AS Product, ROW_NUMBER() OVER (PARTITION BY v.Vendor_Key ORDER BY SUM(sf.Price * sf.Quantity) DESC) AS seqnum
FROM SalesFacts sf JOIN Vendor v
ON sf.Vendor_Key = v.Vendor_Key JOIN Product p
ON sf.Product_Key = p.Product_Key
GROUP BY v.Vendor_Key, v.Vendor_Name, p.Product_Key, p.Description
) vp
WHERE vp.seqnum = 1
The result of the query is show as below:
What the above query did was to extract the top-grossing product for each vendor from the entire database, or in other words, the query obtained the highest-revenue product per vendor.
I wanted to add in a new column, which is Sales Revenue, which calculation can be derived as such:
price of item * quantity. I wanted to add in the new column so that i can see see how much revenue the vendor earned from their respective best-selling products.
How do i obtain the same result with the inclusion of sales revenue column?
The way your question is asked, you just want to return this column from the subquery so it can be accessed in the outer query:
SELECT Vendor_Name, Product, Sales_Revenue
FROM (
SELECT
v.Vendor_Name,
p.Description AS Product,
SUM(sf.Price * sf.Quantity) Sales_Revenue
ROW_NUMBER() OVER (PARTITION BY v.Vendor_Key ORDER BY SUM(sf.Price * sf.Quantity) DESC) AS seqnum
FROM SalesFacts sf JOIN Vendor v
ON sf.Vendor_Key = v.Vendor_Key JOIN Product p
ON sf.Product_Key = p.Product_Key
GROUP BY v.Vendor_Key, v.Vendor_Name, p.Product_Key, p.Description
) vp
WHERE vp.seqnum = 1

SQL 2 Rows into 1

Below is a SQL script to see how many customers purchased both products, as well as how many customers purchased either of the two products.
I would like to be able to return a result set with one column for the first product, one column for the second, one column for Count with Both, and one column for Count with Either.
Instead it returns a column for each of the counts and a single NULL column for the ProductID.
SELECT
COUNT(DISTINCT b.CustomerID) AS "Count with Both",
COUNT(DISTINCT c.CustomerID) AS "Count with Either",
b.ProductID
FROM
LocationCode z
LEFT JOIN
(SELECT DISTINCT ProductID, CustomerID
FROM LocationCode a
WHERE a.ProductID IN ('MP040') AND a.ProductID IN ('OG010')) b ON z.CustomerID = b.CustomerID
LEFT JOIN
(SELECT DISTINCT ProductID, CustomerID
FROM LocationCode b
WHERE b.ProductID IN ('MP040', 'OG010')) c ON z.CustomerID = c.CustomerID
GROUP BY
b.ProductID
If I understand correctly, you can use a self-join and aggregation. The following does what you want for all pairs of products:
with cp as (
select customerid, productid,
count(*) over (partition by customerid) as num_customers
from locationcode
group by customerid, productid
)
select cp1.productid, cp2.productid, cp1.cnt as cnt1, cp2.cnt as cnt2,
count(*) as both
from cp cp1 join
cp cp2
on cp1.customerid = cp2.customerid and
cp1.productid < cp2.productid
group by cp1.productid, cp2.productid, cp1.cnt, cp2.cnt ;
You can of course add where filters if you only want results for certain products.

Order sum aggregate in output

I've 2 tables, purchases and customers.
Customers has cid, cname...
Purchases has total_price of purchase, one cid has multiple purchases.
I need to find cname and maximum total price spent by top 3 customers.
I'm doing this
select c.cname,
sum(p.total_price)
from purchases p
inner join
customers c
on p.cid=c.cid
where p.total_price <= (select max(total_price)
from purchases
)
group by p.cid,
c.cname
order by c.cname;
I'm getting the sum total but I can't order it and get the top 3 spending customers.
Where am I going wrong?
All you need is a ROWNUM <= 3 condition and an appropriate ORDER BY:
SELECT
cname
, total_purchase
FROM (SELECT
c.cname
, sum(p.total_price) as total_purchase
FROM customers c
INNER JOIN purchases p on p.cid = c.cid
GROUP BY c.cname
ORDER BY total_purchase DESC) RS
WHERE ROWNUM <= 3;
In Oracle you need to order the result set before limiting it to the top 3 customers, which is why ROWNUM <= 3 is in the outer query.

query with subquery with 1 result(max) for each year

I have to make a query where I show for each year wich shipper had the maximum total cost.
My query now show for each year the total cost of each shipper. So in the result i must have a list of the years, for each year the shipper and the total cost.
Thanks in advance.
select year(OrderDate), s.ShipperID, sum(freight)
from orders o
join shippers s on o.ShipVia = s.ShipperID
group by year(OrderDate),s.ShipperID
Select a.FreightYear, a,ShipperID, a.FreightValue
from
(
select year(OrderDate) FreightYear, s.ShipperID, sum(freight) FreightValue
from orders o
join shippers s on o.ShipVia = s.ShipperID
group by year(OrderDate),s.ShipperID
) a
inner join
(
select FreightYear, max(FrieghtTotal) MaxFreight
from
(
select year(OrderDate) FreightYear, s.ShipperID, sum(freight) FreightTotal
from orders o
join shippers s on o.ShipVia = s.ShipperID
group by year(OrderDate),s.ShipperID
) x
group by FreightYear
) max on max.FreightYear = a.FreightYear and max.MaxFreight = a.FreightValue
order by FreightYear
Inner query a is your original query, getting the value of freight by shipper.
Inner query max gets the max value for each year, and then query max is joined to query a, restricting the rows in a to be those with a value for a year = to the max value for the year.
Cheers -
It's marginally shorter if you use windowing functions.
select shippers_ranked.OrderYear as OrderYear,
shippers_ranked.ShipperId as ShipperId,
shippers_ranked.TotalFreight as TotalFreight
from
(
select shippers_freight.*, row_number() over (partition by shippers_freight.OrderYear order by shippers_freight.TotalFreight desc) as Ranking
from
(
select year(OrderDate) as OrderYear,
s.ShipperID as ShipperId,
sum(freight) as TotalFreight
from orders o
inner join shippers s on o.ShipVia = s.ShipperID
group by year(OrderDate), s.ShipperID
) shippers_freight
) shippers_ranked
where shippers_ranked.Ranking = 1
order by shippers_ranked.OrderYear
;
You need to decide what you would like to happen if two shippers have the same TotalFreight for a year - as the code above stands you will get one row (non-deterministically). If you would like one row, I would add ShipperId to the order by in the over() clause so that you always get the same row. If in the same TotalFreight case you would like multiple rows returned, use dense_rank() rather than row_number().