SUB TABLES WITH JOINS - mysql

What is wrong in this? I am not getting the currect Percentage.
Q. Compute the revenue generated by each customer based on their orders. -Also, show each customer's revenue as a percentage of total revenue. Sort by customer name.
SELECT C.customerName, P1.customerNumber, SUM(P1.amount) AS REVENUE,( SUM(P1.amount)/SUM(P.S))*100 AS Percentage
FROM Customers C
JOIN Payments P1
ON P1.customerNumber = C.customerNumber
JOIN
(
SELECT customerNumber AS C1, SUM(amount) AS S
FROM Payments
GROUP BY customerNumber
) P
ON P1.customerNumber=P.C1
GROUP BY P1.customerNumber, C.customerName
ORDER BY C.customerName;

Next time, please provide sample data and table structure, preferably in a SQl FIddle like this: https://www.db-fiddle.com/f/eKqDmiPuMaok7Uu3fwDKfD/1
Check if some of this queries suits your needs:
SELECT DISTINCT
Customers.CustomerNumber,
Customers.CustomerName,
SUM(Payments.Amount) OVER(PARTITION BY Customers.CustomerNumber) AS Revenue,
(SUM(Payments.Amount) OVER(PARTITION BY Customers.CustomerNumber)
/
SUM(Payments.Amount) OVER()) * 100 AS Percentage
FROM Customers
JOIN Payments ON Payments.CustomerNumber = Customers.CustomerNumber;
Or this:
SELECT Customers.CustomerNumber,
Customers.CustomerName,
SUM(Payments.Amount) Revenue,
(SUM(Payments.Amount)
/
(SELECT SUM(AMOUNT)
FROM Payments)) * 100 Percentage
FROM Customers
JOIN Payments ON Payments.CustomerNumber = Customers.CustomerNumber
GROUP BY Customers.CustomerNumber,
Customers.CustomerName

Related

Sub queries, Aliases and Stored Values- MySQL

I am given a problem as following:
Create a SQL statement that shows all shipped orders with a total of more than $14,000. The results must contain the customer's first_name, last_name, order_id, order shipped_date, number of items and total amount. Make sure any discount is taken into account when calculating the total amount.
Tables to draw from are:
Order Items: quantity, list_price, discount, order_id
Order: order_id, shipped_date, order_status, customer_id
Customers: first_name, last_name, customer_id.
I'm pretty new to SQL and sub-queries so I tried to build it from inside out and it kept giving me rows returned...until the last join ("sub2"). My code is below (inelegant, but I do need to figure out the problem before worrying about that).
select c.first_name, c.last_name, sub2.order_id, sub2.shipped_date, sub2.quantity, sub2.total_amount from customers AS c
INNER JOIN(
select o.order_id, o.shipped_date, o.order_status, o.customer_id, sub1.total_price FROM orders as o
INNER JOIN (
SELECT oi.order_id, SUM((oi.list_price * oi.quantity) - ((oi.list_price * oi.quantity) * (oi.discount))) AS total_price
FROM order_items AS oi
group by oi.order_id
) AS sub1
ON o.order_id = sub1.order_id
WHERE
sub1.total_price > 14000
AND
o.order_status = 4
) AS sub2
ON c.customer_id = sub2.customer_id
;
The error I'm getting is "Unknown column 'sub2.quantity' in 'field list'"
I imagine Mysql does not like this double nesting, so how can I go about solving this?
There's no problem with nested queries, your problem is that the names on your nested statements don't correlate with the ones used outside as the alias.
eg: ... INNER JOIN(select o.order_id, o.shipped_date, o.order_status, o.customer_id, sub1.total_price FROM ...
doesn't has a quantity casted, so when it's rows are called outside as sub2 there is an Unknown column 'sub2.quantity' in field list, it will fail also with sub2.total_amount for the same reason.
I'm guessing that since you are grouping by the total amount of an order, it should also give the sum of the items quantities. So the SQL should be like so:
select c.first_name, c.last_name, sub2.order_id, sub2.shipped_date, sub2.quantities, sub2.total_amount from customers AS c
INNER JOIN(
select o.order_id, o.shipped_date, o.order_status, o.customer_id, sub1.total_price as total_amount, sub1.total_quantities FROM orders as o
INNER JOIN (
SELECT oi.order_id,SUM(oi.quantity) as total_quantities, SUM((oi.list_price * oi.quantity) - ((oi.list_price * oi.quantity) * (oi.discount))) AS total_price
FROM order_items AS oi
group by oi.order_id
) AS sub1
ON o.order_id = sub1.order_id
WHERE
sub1.total_price > 14000
AND
o.order_status = 4
) AS sub2
ON c.customer_id = sub2.customer_id group by sub2.order_id;

SQL counting customers that have ordered the same product at least 7 times

I have the following tables
– Price (prodID, from, price)
– Product (prodID, name, quantity)
– PO (prodID, orderID, amount)
– Order (orderID, date, address, status, trackingNumber, custID, shipID)
– Shipping (shipID, company, time, price)
– Customer (custID, name)
– Address (addrID, custID, address)
I would like to find the names of customers who have bought the same item at least 7 times, if they bought the same item twice in one order I would like to count it as one. Here is my code:
SELECT C.name, COUNT(DISTINCT p.prodId) as prod_count
FROM Product P
INNER JOIN PO
ON PO.prodId = P.prodId
INNER JOIN "Order" O
ON O.orderId = PO.orderId
INNER JOIN Customer C
ON C.custId = O.custId
GROUP BY c.name
HAVING COUNT(DISTINCT p.prodId) > 6;
However, this is returning the number of unique products each customer has ordered which is not what I am looking for.
We can try using two levels of aggregation here. The first level of aggregation is by customer, order, and product, and removes duplicates should a given customer order the same product more than once within a single order. The next level of aggregation is only by customer and product, and it retains only customers who have at least one product which they purchased 7 or more times across different orders. Finally, we do a distinct select to retain each unique matching customer name.
WITH cte1 AS (
SELECT c.name, o.orderId, p.prodId
FROM Customer c
INNER JOIN "Order" o ON o.custId = c.custId
INNER JOIN PO po ON po.orderId = o.orderId
INNER JOIN Product p ON p.prodId = po.prodId
GROUP BY c.name, o.orderId, p.prodId
),
cte2 AS (
SELECT name, prodId
FROM cte1
GROUP BY name, prodId
HAVING COUNT(*) >= 7
)
SELECT DISTINCT name
FROM cte2;
As far as I see , there is no need of joining with Product table,unless you need Product name in your result.
;with CTE as(
SELECT orderId,prodId, COUNT(*) as OrderProd_Count
FROM dbo.PO
GROUP BY orderId,prodId
)
,CTE1 as(
SELECT prodId,count(*) as Prod_Count
from CTE
group by prodId
having count(*)>7
)
select c1.productid,ca.Name from CTE1 C1
inner join CTE C on c1.prodId=c1.prodId
inner join dbo.Order O on c.orderid=O.orderId
inner join Customer C on o.custid=C.custid

MySQL Sum from Alias Table

I have this query...
CREATE VIEW CustOrderItems AS
SELECT CustFirstName, CustLastName, OrderNumber, OrderDate, ShipDate,
QuantityOrdered * QuotedPrice AS ItemTotal
FROM Customers NATURAL JOIN Orders NATURAL JOIN Order_Details;
After grouping the orders like this...
SELECT CustFirstName, CustLastName, OrderNumber, OrderDate, ShipDate,
QuantityOrdered * QuotedPrice AS ItemTotal
FROM Customers NATURAL JOIN Orders NATURAL JOIN Order_Details
GROUP BY OrderNumber
ORDER BY OrderNumber ASC;
I know need to calculate the sum of all the ItemTotal's added together?
Any help is much appreciated!
As has been mentioned in a comment already, you get sums in SQL with SUM, which should not be much of a surprise. It seems strange you know about GROUP BY, but not about SUM.
At this point I'd like to recommend to always aggregate before joining, not afterwards. You want orders with their customer information and their total price. So select from orders, join with customers, join with total prices. Once you want aggregates from different tables, this approach will save you some trouble.
SELECT
c.CustFirstName,
c.CustLastName,
o.OrderNumber,
o.OrderDate,
o.ShipDate,
od.OrderTotal
FROM Orders o
JOIN Customers c ON c.CustomerNumber = o.CustomerNumber
JOIN
(
SELECT
OrderNumber,
SUM(QuantityOrdered * QuotedPrice) AS OrderTotal
FROM Order_Details
GROUP BY OrderNumber
) od ON od.OrderNumber = o.OrderNumber
ORDER BY o.OrderNumber ASC;

Getting total for Invoiced Paid and the amount owing for each client

I have tried many of the scripts that I found here but none did what I required.
I have 3 tables: clients, invoiced and payments.
I would like to:
sum all invoices per client within a date range
sum all payments for clients with in the date range then subtract payments from invoiced to show amount outstanding.
I need to add the date range and get the total if not zero.
So far my query is
select id,
(select sum(Total) from invoiced where invoiced.ClientId = clients.Id) AS Invoiced,
(select sum(Amount) from payments where payments.ClientId = clients.Id) AS Paid
FROM clients
The problem is the non existing relation between the invoice and the payments.
This makes it a bit "dirtier" ;)
Try this:
select p.id, sum(i.total), sum(p.amount), sum(i.total) - sum(p.amount) as outstanding
from
(
select c.id, coalesce(pay.amount,0) as amount
from
(select ClientId, Sum(Amount) amount from payments group by ClientId) as pay
right join clients c on c.id = pay.ClientId
) p
left join
(
select c.id, inv.total
from
(select ClientId, Sum(Total) total from invoiced group by ClientId) as inv
left join clients c on c.id = inv.ClientId
) i
on p.id = i.id
group by id
having sum(i.total) <> sum(p.amount)
;
Updated Sqlfiddle with results here

How do I return only those customers who owe me money (in mySQL)?

I have a Customer table and a Payment table that are joined on a customers ID (CID). In the Payment table there is an Amount column; some rows have a positive amount (they owed money), but others have a negative amount (they paid money).
I want know which customers, when I sum their positive and negative amounts, still owe me money.
If I just wanted to see their balances, I could do that like so:
SELECT Customer.FirstName, Customer.LastName, Customer.AccountNumber, SUM(Amount) As Balance
FROM Customer
JOIN Payment
ON Customer.CID = Payment.CID
GROUP BY Customer.AccountNumber
How do I change this so that if the balance is 0 or less, I don't return a row for that customer?
Edit: HAVING was the keyword I did not know. Thanks for the many right answers!
The HAVING keyword allows you to select based on group by functions:
SELECT Customer.FirstName, Customer.LastName, Customer.AccountNumber, SUM(Amount) As Balance
FROM Customer
JOIN Payment
ON Customer.CID = Payment.CID
GROUP BY Customer.AccountNumber
HAVING SUM(Amount) > 0
SELECT Customer.FirstName, Customer.LastName, Customer.AccountNumber, SUM(Amount) As Balance
FROM Customer
JOIN Payment
ON Customer.CID = Payment.CID
GROUP BY Customer.AccountNumber
HAVING Balance>0
SELECT c.FirstName, c.LastName, c.AccountNumber, p.Amount AS Balance
FROM Customer c
JOIN Payment p
ON c.CID = p.CID
GROUP BY c.AccountNumber
HAVING SUM(p.Amount) > 0
select customers.name, sum(payments.amount) balance
from customers join payments on customers.id = payments.cid
group by customers.id having balance > 0