Can I execute a COUNT() before GROUP BY - mysql

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;

Related

MySQL display a sum as a column

There are 2 tables customer (id, name) and orders (id, customer_id, date, price) where customer_id is the foreign key.
How would one get the top 5 individuals with the most purchases in the last 6 months?
needed output format is (customer.id, customer.name, sum(price))
select c.id, c.name, SUM(o.price) as total
from customer c inner join orders o
on c.id = o.customer_id
order by total desc limit 5;
I'm not sure why this query I have doesn't display anything
SUM() is an aggregate function that calculates the sum after a grouping operation. Try using a GROUP BY clause like this:
select c.id, c.name, SUM(o.price) as total
from customer c inner join orders o
on c.id = o.customer_id
group by c.id, c.name
order by total desc limit 5;
DB-fiddle: https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=7f5e3726d6af5a9144a03d503da5ae37

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;

Getting invalid use of group function

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

MySql query to select total orders for customers

There title does not quite describe well what i need from the query.
#sgeddes helped me come up with the following query but the query needs some alteration to accomplish my needs. I also modified the query slightly to not Select deleted customers but i couldn't change much due to the way this query is written is out my SQL knowledge.
SELECT d.customer_id,d.fname,d.lname,d.isactive,
o.lastdate,
Count(o2.order_id) AS 'total_orders'
FROM customers d
LEFT JOIN (SELECT MAX(order_id) order_id, customer_id
FROM orders
GROUP BY customer_id) m on d.customer_id = m.customer_id
LEFT JOIN orders o on m.order_id = o.order_id
LEFT JOIN orders o2 on d.customer_id = o2.customer_id
AND o2.balance > 0 AND o2.isActive > -1
WHERE d.user_id =945766 AND d.isActive > -1
AND o2.customer_id IS NULL
GROUP BY d.customer_id
I need the three following requirements.
count orders for customer with isActive > -1 (-1 = deleted)
Customers not in orders table (customer with no orders).
Customers with isActive = 0 and their corresponding order count
so briefly all I need is customer with isActive = 0 and to get an actual # for total_orders column.
In my attempt to better help you understand my requirement I created a SqlFiddle.
Please see my SqlFiddle
If your requirement is what you mentioned in your 3 points then you can use below queries-
If you want separate query then use below-
SELECT d.customer_id, d.fname, COUNT(o.order_id) AS Total_Orders
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE d.isActive > -1
GROUP BY d.customer_id;
SELECT DISTINCT d.customer_id, d.fname
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE o.customer_id IS NULL;
SELECT d.customer_id, d.fname, COUNT(o.order_id) AS Total_Orders
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE d.isActive = 0
GROUP BY d.customer_id;
If you want to merge them by union then use below-
SELECT 'isactive>-1' AS 'status', d.customer_id, d.fname, COUNT(o.order_id) AS Total_Orders
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE d.isActive > -1
GROUP BY d.customer_id
UNION ALL
SELECT DISTINCT 'Customer without Order' AS 'status', d.customer_id, d.fname, COUNT(o.order_id) AS Total_Orders
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE o.customer_id IS NULL
UNION ALL
SELECT 'isactive=0' AS 'status', d.customer_id, d.fname, COUNT(o.order_id) AS Total_Orders
FROM customer d
LEFT JOIN orders o ON d.customer_id=o.customer_id
WHERE d.isActive = 0
GROUP BY d.customer_id;
Note: Your main query is trying fetch latest order which can be for any other purpose, so if you provide your exact requirement if different from it then someone can help you.