Getting invalid use of group function - mysql

Here's my query
Select email_address, count(order_id) AS order_count, (sum(item_price - discount_amount) * (quantity)) AS order_total,
(avg(item_price - discount_amount) * (quantity)) AS avg_order_total
from customers join orders
using(customer_id)
join order_items
using(order_id)
group by customer_id > 1, email_address
Output of my query
Output Expected
I'm trying to produce the wanted output but I'm not sure how to only display only 3 of them. Tried a where statement but it made it worst.
Another way to display output without the use of limit?
Here's my REVISED query
Select email_address, count(customer_id) AS order_count, sum((item_price - discount_amount) * (quantity)) AS order_total,
round(avg((item_price - discount_amount) * (quantity)),2) AS avg_order_total
from customers join orders
using(customer_id)
join order_items
using(order_id)
group by customer_id
order by count(customer_id) desc limit 3
I'm trying to produce the expected output without using limit but I can't figure how. Above is my query that I used limit on to display the result and it shows the output I wanted. I tried using a where statement, but it doesn't run my query when I did.

Could you try this query?
SELECT c.email_address,
count(o.order_id) AS order_count,
(sum(i.item_price - i.discount_amount) * (i.quantity)) AS order_total,
(avg(sum(i.item_price - i.discount_amount) * (i.quantity))) AS avg_order_total
from customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
INNER JOIN order_items i ON o.order_id = i.order_id
GROUP BY email_address;
A few things to note:
You need to include any field in the SELECT clause that isn't being aggregated into your GROUP BY. So, I've added email_address.
It's far better to specify the actual columns being joined, instead of the USING keyword. This is because it's easier to maintain if any of the columns change.
I've made some assumptions about which table your fields belong to and prefixed them with the table alias (c, o, or i). If this is wrong they can be changed. But it's better to use table aliases as it improves the maintainability of the code, and it's easier to read & write.

There are several guesses in this suggestion:
SELECT
c.email_address
, COUNT(DISTINCT o.order_id) AS order_count
, (SUM(oi.item_price - oi.discount_amount) * (oi.quantity)) AS order_total
, (AVG(oi.item_price - oi.discount_amount) * (oi.quantity)) AS avg_order_total
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
WHERE c.customer_id > 1
GROUP BY
c.email_address
When joining several tables it is good practice to nominate which table a column comes from. To make this brief each table can be given an alias and a common method for that is to use first letters e.g. for this set of tables: c, o, oi
I may have guessed incorrectly when placing the aliases in some places and I cannot fix that as I don't know your tables.
+EDIT
By the way, I also included DISTINCT into the count of orders, if you don't do that you are counting the number of order items instead.
SELECT
c.email_address
, COUNT(DISTINCT o.order_id) AS order_count
, (SUM(oi.item_price - oi.discount_amount) * (oi.quantity)) AS order_total
, (SUM(oi.item_price - oi.discount_amount) * (oi.quantity))
/ COUNT(DISTINCT o.order_id) AS avg_order_total
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
WHERE c.customer_id > 1
GROUP BY
c.email_address

Related

Why SQL Displays Two Rows Instead of One

Here is the Query
SELECT o.OrderID, o.CNIC,
(SELECT FullName FROM Customer WHERE CNIC=o.CNIC) Customer,
o.Date
FROM orders o
JOIN ordersproduct op ON op.OrderID=o.OrderID
WHERE o.OrderID=1;
Here is the result
1 - 15604-5566123-2 - Shaiz Mehran - 2020-09-30
1 - 15604-5566123-2 - Shaiz Mehran - 2020-09-30
If the purpose is to only show customers who has an order, but no details of the orderproduct itself you could use the following :
SELECT DISTINCT o.OrderID, o.CNIC,
(SELECT FullName FROM Customer WHERE CNIC=o.CNIC) Customer,
o.Date
FROM orders o
WHERE o.OrderID=1;
OR
SELECT DISTINCT o.OrderID, o.CNIC,
c.Customer, o.Date
FROM orders o
INNER JOIN Customer c ON c.CNIC = o.CNIC
WHERE o.OrderID=1;
If this is only the start and you will show the product which the customer ordered it will show two rows as the customer has two products (orderproducts). This will return two rows with different orderproducts
SELECT DISTINCT o.OrderID, o.CNIC,
c.Customer, o.Date op.ColumnTosShow
FROM orders o
INNER JOIN Customer c ON c.CNIC = o.CNIC
INNER JOIN ordersproduct op ON op.OrderID=o.OrderID
WHERE o.OrderID=1;
SELECT Distinct Orders.OrderID, Orders.CNIC, Customer.FullName
FROM (Orders LEFT JOIN Customer ON Orders.CNIC = Customer.CNIC)
LEFT JOIN OrdersProduct ON Orders.OrderID = OrdersProduct.OrderID;
The reason you are getting multiple rows is because an order can have multiple products . . . and you are getting one row per product.
The join to the product table seems quite unnecessary, so just use:
SELECT o.OrderID, o.CNIC,
(SELECT FullName FROM Customer WHERE CNIC=o.CNIC) as Customer,
o.Date
FROM orders o
WHERE o.OrderID = 1;
I am guessing you have a reasonable data model so there are not multiple rows in orders for a given id. SELECT DISTINCT is not necessary.
I should add that JOIN is very reasonable for this query, but like this:
SELECT o.OrderID, o.CNIC, c.FullName as Customer,
o.Date
FROM orders o LEFT JOIN
customer c
ON o.CNIC = c.CNIC
WHERE o.OrderID = 1;

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;

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;

Mysql sub select with SUM() and group by

I am trying to create a query that does a calculation in a subquery that requires the SUM function and a group by. My query returns the error "Subquery returns more than 1 row". Essentially I am trying to return the amount "Due" for each order. If the order total is greater than the sum of total_collected (for that order_id) from the payments table there will be amount due. Here is the query:
SELECT o.order_id
, o.server
, o.subtotal
, o.discount
, o.tax, o.total
, (SELECT (o.total - SUM(p.total_collected))
from orders o
join payments p
on o.order_id = p.order_id
group by p.order_id) as 'Due'
FROM orders o
join payments p
on o.order_id = p.order_id
WHERE...;
I cannot include 'p.order_id' in the sub select because it should only contain one column. I understand why I am getting the error, I just don't know how to get the sub select to only perform the SUM on a per order_id basis.
Without changing the structure much, I think the subquery is looking at all of the data in the orders/payments tables. I think you need to filter it down to look only at the relevant order_id like so.
(I also added a SUM around the order total because I am pretty sure that would give a different error without it.)
SELECT o.order_id
, o.server
, o.subtotal
, o.discount
, o.tax
, o.total
, (SELECT (SUM(o2.total) - SUM(p.total_collected))
from orders o2
JOIN payments p
ON o2.order_id = p.order_id
WHERE o2.order_id = o.order_id) as 'Due'
FROM orders o
WHERE...;
Although, if you adjust this so that it uses a join instead of a subquery, I think you will get better performance. Something like this:
SELECT o.order_id
, o.server
, o.subtotal
, o.discount
, o.tax
, o.total
, o.total - c.Collected AS 'Due'
FROM orders o
JOIN (
SELECT p2.order_id, SUM(p2.total_collected) AS 'Collected'
FROM payments p2
GROUP BY p2.order_id) AS c
ON o.order_id = c.order_id
WHERE...;
You do not need sub-query:
SELECT
o.order_id,
o.server,
o.subtotal,
o.discount,
o.tax,
o.total,
o.total - ifnull(sum(p.total_collected),0) As Due
FROM orders AS o
LEFT JOIN payments AS p ON o.order_id = p.order_id
WHERE ...
GROUP BY o.order_id

Can I execute a COUNT() before GROUP BY

I am working on an mySQL assignment for school and I am stuck on a question. I am still new to mySQL. COUNT(o.customer_id) is not working the way I want. I want it to count the number of orders but it is counting all items. i.e. Customer 1 has 2 orders but it is returning 3 because one order has two items. I have three tables one with customers, another with orders than another with each item on each order. Ive posed my query below. Any help would be great.
SELECT email_address, COUNT(o.order_id) AS num_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 ot
ON o.order_id = ot.order_id
GROUP BY o.customer_id
HAVING num_of_orders > 1
ORDER BY total DESC;
As simple as use Distinct reserved word:
SELECT email_address, COUNT(distinct o.order_id) AS num_of_orders
Looks like you want to count the DISTINCT number of orders. Add a DISTINCT into the COUNT. Although MySQL allows you to use the SELECT expression in the HAVING clause, it's not good practice to do so.
SELECT email_address, COUNT(DISTINCT o.order_id) AS num_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 ot
ON o.order_id = ot.order_id
GROUP BY o.customer_id
HAVING COUNT(DISTINCT o.order_id) > 1
ORDER BY total DESC;
Just take out the join to items. All it is doing is duplicating rows when there are multiple items.
SELECT email_address, COUNT(o.order_id) AS num_of_orders,
SUM(((item_price - discount_amount) * quantity)) AS total
FROM customers c JOIN orders o
ON c.customer_id = o.customer_id
GROUP BY o.customer_id
HAVING COUNT(o.order_id) > 1
ORDER BY total DESC;