Having an issue with the last part of my Oracle SQL query - sql

I'm having trouble with a problem (and yes it is homework).. The question is:
Write a SELECT statement that returns one row for each customer that has orders with these columns:
The email_address from the Customers table
A count of the number of orders
The total amount for each order (Hint: First, subtract the discount amount from the price. Then, multiply by the quantity.)
Return only those rows where the customer has more than 1 order.
Sort the result set in descending sequence by the sum of the line item amounts.
My query for this part works. It is:
SELECT email_address, COUNT(o.order_id) as number_of_orders, sum((item_price-discount_amount)*quantity) As Total
FROM Customers c JOIN Orders o
ON c.customer_id = o.customer_id
JOIN order_items oi on oi.order_id =o.order_id
GROUP BY email_address
HAVING COUNT (o.order_id) > 1
ORDER BY number_of_orders DESC;`
After that, I'm supposed to modify that query so that it only counts and totals line items that have an item_price value greater than 400. I can't seem to figure it out. Can someone please point me in the right direction?
BTW, it's the My Guitar Shop database.

You will have to add a filter for item_price greater than 400 assuming item_price is part of order_items table.
SELECT email_address, COUNT(o.order_id) as number_of_orders, sum((item_price-discount_amount)*quantity) As Total
FROM Customers c JOIN Orders o
ON c.customer_id = o.customer_id
JOIN order_items oi on oi.order_id =o.order_id
where oi.item_price > 400
GROUP BY email_address
HAVING COUNT (o.order_id) > 1
ORDER BY number_of_orders DESC;

SELECT email_address, COUNT(o.order_id) as number_of_orders, sum((item_price-discount_amount)*quantity) As Total
FROM Customers c JOIN Orders o
ON c.customer_id = o.customer_id
JOIN (
SELECT order_id,
CASE WHEN item_price < 400 THEN 0 ELSE item_price END,
--other columns you need
FROM order_items ) oi on oi.order_id =o.order_id
GROUP BY email_address
HAVING COUNT (o.order_id) > 1
ORDER BY number_of_orders DESC;
This is should work.
Also, you should always use table aliases when addressing a coulmn- even if there are no name clashed. It improves readability and prevent you from making nasty mistakes.

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;

SQL JOIN, GROUP BY, DATES

I have 3 tables and I have trouble with the quantity. I will apply my solution down. SUM and GROUP BY are not working.
PRODUCT_INFORMATION
PRODUCT_ID
PRODUCT_NAME
PRODUCT_DESCRIPTION
PCATEGORY_ID
PRODUCT_STATUS
WARRANTY_PERIOD
ORDERS
ORDER_ID
ORDER_DATE
ORDER_MODE
CUSTOMER_ID
ORDER_STATUS
ORDER_TOTAL
ORDER_ITEMS
ORDER_ID
LINE_ITEM_ID
PRODUCT_ID
UNIT_PRICE
QUANTITY
• whose name (PRODUCT_NAME) does not contain the characters ‘_’ and ‘<’.
• the total number of products sold (QUANTITY column of table oe.ORDER_ITEMS) of a given type to be greater than 200.
The list should contain the following columns:
• PRODUCT_NAME (by oe.PRODUCT_INFORMATION)
• TOTAL_QUANTITY - the total number of products sold of a given type
SELECT p.PRODUCT_NAME, sum(oi.QUANTITY), p.WARRANTY_PERIOD AS WARRANTY, o.ORDER_MODE
FROM PRODUCT_INFORMATION p
LEFT JOIN ORDER_ITEMS oi ON p.PRODUCT_ID=oi.PRODUCT_ID
LEFT JOIN ORDERS o ON o.ORDER_ID = oi.ORDER_ID
WHERE (p.PRODUCT_NAME NOT LIKE '%<%'
AND p.PRODUCT_NAME NOT LIKE '%\_%' ESCAPE '\')
AND oi.QUANTITY>200
group by p.PRODUCT_NAME;
What does "... are not working" mean? Did you get any error? If so, which one?
What is obvious, is that GROUP BY clause has to contain all non-aggregated columns. As problem doesn't require them to be displayed, I removed them from the select column list (both p.WARRANTY_PERIOD and o.ORDER_MODE).
Furthermore, you should move the "quantity > 200" condition from WHERE into the HAVING clause.
Finally, in order to avoid characters to be escaped, I switched from NOT LIKE to INSTR.
Something like this:
SELECT p.product_name,
SUM (oi.quantity) sum_quantity
FROM product_information p
LEFT JOIN order_items oi ON p.product_id = oi.product_id
LEFT JOIN orders o ON o.order_id = oi.order_id
WHERE INSTR (p.product_name, '<') = 0
AND INSTR (p.product_name, '_') = 0
GROUP BY p.product_name
HAVING SUM (oi.quantity) > 200;
Your query selects columns which are not in the GROUP BY clause, which is usually not a problem with MySQL, but would be with any other vendor.
Since you don't require the two last columns in your output, I suggest you just get rid of them.
Also, MySQL supports regexes, which are more suited to your problem than LIKE in my opinion.
SELECT p.PRODUCT_NAME, SUM(oi.QUANTITY)
FROM PRODUCT_INFORMATION p
LEFT JOIN ORDER_ITEMS oi ON p.PRODUCT_ID=oi.PRODUCT_ID
LEFT JOIN ORDERS o ON o.ORDER_ID = oi.ORDER_ID
WHERE p.PRODUCT_NAME REGEXP '[_<]' = 0
GROUP BY p.PRODUCT_NAME
HAVING SUM(oi.QUANTITY) > 200;
Edit : As pointed out by #Littlefoot, the quantity condition should be tested after the grouping and not before, and should therefore be in the HAVING clause rather than in the WHERE clause.

Query returning individuals most recent order, date of the order, number of products in the order and the total amount

Need help with a query to returns each individuals most recent order, date of the order, number of products in the order and the total amount. I am kind of stuck trying to get the number of products and total.
Here are the table diagrams
Not sure if I should be using multiple joins or subqueries:
SELECT FirstName, LastName, MAX(O.OrderDate), O.OrderDate
FROM Customer C
INNER JOIN Order O ON C.CustomerID = O.CustomerID
It is always good practice to start from assumed dim tables such as product.This Query should help. It is better to aggregate quantity and amount to get results at aggregate level
SELECT FirstName
,LastName
,max(o.orderdate) Orderdate
,Sum(Quantity) Quantity
,Sum(TotalAmount) TotalAmount
FROM Product p
INNER JOIN Orderitem oi
ON Oi.product_id = p.id
INNER JOIN
ORDER o
ON o.id = oi.order_id
INNER JOIN Customer c
ON c.id = o.Customer_id
GROUP BY FirstName
,LastName
not sure if you want aggregations but here you go:
SELECT customer.firstname, customer.lastname, COUNT(DISTINCT orderitem.productid), [order].totalamount
FROM [order] LEFT JOIN customer ON [order].customerid=customer.id LEFT JOIN orderitem ON order.id=orderitem.orderid
WHERE [order].date=MAX([order].date)
GROUP BY customer.firstname,customer.lastname, [order].totalamount
Still don't know why you are applying a where clause for the last order, it's up to you to keep or not the where condtion.

Select all orders by one customer

I have three tables, orders, orders_details and customers. I need to select orders by one customer for the orders table so I did this
orders columns:
id
customer_id
created
vat
discount
amount
paid
orders_details columns:
id
order_id
cost
qty
product
The SQL I used
SELECT
orders.*,
SUM(orders_details.qty*orders_details.cost) as amount,
SUM(orders_details.qty) AS qty
FROM
orders,
orders_details,
customers
WHERE
orders.customer_id = customers.id
AND orders_details.order_id = orders.id
AND orders.customer_id = 1
but I am getting a wrong qty of 30 instead of 20 and the amount is wrong
If you want to aggregate per order you need a GROUP BY clause. Also you should use proper JOIN syntax, and might consider using aliases to make the query more compact.
SELECT
o.*,
SUM(od.qty * od.cost) AS amount,
SUM(od.qty) AS qty
FROM orders o
INNER JOIN orders_details od ON od.order_id = o.id
INNER JOIN customers c ON o.customer_id = c.id -- not used, might be excluded
WHERE o.customer_id =1
GROUP BY o.id
Depending on what database system you are using you might need to include all columns referenced in o.* in the GROUP BY:
GROUP BY o.id, o.customer_id, o.created, o.vat, o.discount, o.amount, o.paid
Last note: as you don't seem to use any data from the customers table you probably could exclude that table altogether.
You're missing a GROUP BY clause which I'm guessing should be on orders.id.

Compare a table with a count result from another table and add the names that have a zero count

I am counting how many times a company has ordered. Then I am only showing if a company has ordered less than 5 times. I am then checking it against the table with all company names to see what company has not ordered, which would not show up on the order table, then add their name on the displayed list.
What I have tried:
Select Orders.CustomerID, Count(Orders.CustomerID) AS OrderCount
From Orders Left Join Customers
On Orders.CustomerID = Customers.CustomerID
Group By Orders.CustomerID
Having Count(Orders.CustomerID) <5
This is totally wrong:
Select CustomerID
From Customers
Where EXISTS
(Select CustomerID, Count(CustomerID) AS 'OrderCount'
From Orders
Group BY CustomerID
Having Count(Orders.CustomerID) < 5)
I need to somehow compare the list of names before I ask it to see which ones have ordered less than 5 times.
Thanks
If you want to use LEFT JOIN, interchange the table names since you want to show values from Customers, otherwise use RIGHT JOIN instead.
SELECT Customers.CustomerID,
COUNT(Orders.CustomerID) AS OrderCount
FROM Customers
LEFT JOIN Orders
ON Orders.CustomerID = Customers.CustomerID
GROUP BY Customers.CustomerID
HAVING COUNT(Orders.CustomerID) < 5
using EXISTS()
SELECT CustomerID
FROM Customers c
WHERE EXISTS
(
SELECT 1
FROM Orders o
WHERE o.CustomerID = c.CustomerID
GROUP BY CustomerID
HAVING COUNT(CustomerID) < 5
)
Try this:
SELECT C.CustomerID, C.CustomerName, COUNT(O.CustomerID) AS OrderCount
FROM Customers C
LEFT JOIN Orders O ON O.CustomerID = C.CustomerID
GROUP BY C.CustomerID
HAVING OrderCount < 5
ORDER BY OrderCount, C.CustomerName