SQL join tables, total spent by all customers - mysql

I'm a newbie at SQL and this is the question I've been struggling with:
I have these tables:
customers(custID, firstname, familyname)
items(itemID, unitcost)
lineitems(quantity, orderID, itemID)
orders(orderID, custID, date)
I need to find the names and total spend of all customers that made more than one order.
SELECT SUM(items.unitcost*lineitems.quantity) AS "total_spent"
FROM orders
INNER JOIN customers
ON orders.custID=customers.custID
GROUP BY firstname
HAVING COUNT(DISTINCT orderID)>1
LIMIT 0,30

I think you just need to continue your joins:
SELECT c.custId, c.firstname, SUM(i.unitcost*li.quantity) total_spent
FROM customers c
JOIN orders o ON c.custId = o.custId
JOIN lineitems li ON o.orderId = li.orderId
JOIN items i ON li.itemId = i.itemId
GROUP BY c.custId, c.firstname
HAVING COUNT(DISTINCT o.orderID)>1
LIMIT 0,30

Related

MySQL not returning values from NOT IN function

I currently am trying to write a query that shows customers with at least 5 orders and customer with no orders. Orders are tracked in their own table and in order to find customers with 0 orders we have to find the customers NOT IN orders. Below is my query I'm trying to use and it returns the same customer 5 times for zero orders.
with t1 as
(select o.customerNumber, c.customerName, count(o.orderNumber) as FiveOrders
from orders o join customers c on (o.customerNumber = c.customerNumber)
group by o.customerNumber having count(o.orderNumber) = 5),
t2 as
(select distinct o.customerNumber, c.customerName, count(o.orderNumber) as NoOrders
from orders o join customers c on (o.customerNumber = c.customerNumber)
group by c.customerNumber not in(select customerNumber from orders))
select distinct t1.customerNumber as FiveOrderNumber, t1.customerName as FiveOrderName,
t2.customerNumber as NoOrderNumber, t2.customerName as NoOrderName
from t1 join t2
order by NoOrderName;
Any and all help is appreciated thanks!
If the errors were only in the second table to, I think it is after using
having with condition NOT IN without any logical comparison, I think you can get wanted results easily like:
select distinct customerNumber, customerName, "0" as NoOrders
from customers
where customerNumber not in (Select customerNumber from orders)
If the group by is important, you can use it like in your code.
Zero or five could be counted together with LEFT JOIN
select c.customerNumber, max(c.customerName) customerName, count(o.orderNumber) as FiveOrdersOrZero
from customers c
left join orders o on o.customerNumber = c.customerNumber
group by c.customerNumber
having count(o.orderNumber) in ( 0, 5 )
order by FiveOrdersOrZero

MySQL Group Join Table

I have below three SQL statement and I want to select out like below, I tried but not success.
Need some
help.
Output:
member_id, balance, firstname, lastname, LastPurchase, LastOrde
SELECT c.member_id
, c.firstname
, c.lastname
, m.balance
FROM member m
, customer c
where m.member_id = c.member_id
order
by m.member_id
SELECT member_id, max(date) as LastPurchase
FROM purchase
GROUP
BY member_id
SELECT member_id, max(date) as LastOrder
FROM ordert
GROUP
BY member_id
You can join these statements -
SELECT c.member_id, c.firstname, c.lastname, m.balance, p.LastPurchase, o.LastOrder
FROM member m
join customer c on m.member_id = c.member_id
left join (SELECT member_id, max(date) as LastPurchase
FROM purchase
GROUP BY member_id) p on p.member_id = m.member_id
left join (SELECT member_id, max(date) as LastOrder
FROM ordert
GROUP BY member_id) o on o.member_id = m.member_id
order by m.member_id
You can join the aggregate queries. The JOIN ... USING syntax comes handy here, since all join column names are the same:
SELECT c.member_id, c.firstname, c.lastname, m.balance, p.last_purchase, o.last_purchase
FROM member m
INNER JOIN customer c USING(member_id)
INNER JOIN (
SELECT member_id, max(date) last_purchase FROM purchase GROUP BY member_id
) p USING(member_id)
INNER JOIN (
SELECT member_id, max(date) last_order FROM order GROUP BY member_id
) o USING(member_id)
ORDER BY c.member_id
Important: your original query uses implicit, old-shool joins (with a comma in the from clause) - this syntax fell out of favor more than 20 years ago and its use is discourage, since it is harder to write, read, and understand.
One of the many benefits of using explicit joins here is that you can easily change the INNER JOINs to LEFT JOINs if there is a possibility that a member has no purchase or no order at all.

MySQL: Ranking from multiple tables, sub queries?

This is a MySQL question. I have three tables with the following columns:
transactions (table): transact_id, customer_id, transact_amt, product_id,
products (table): product_id, product_cost, product_name, product_category
customers (table): customer_id, joined_at, last_login_at, state, name, email
I'd like a query that finds out the most popular item in every state and the state. One of the tricky parts is that some product_name have multiple product_id. Therefore I though joining the three tables that generate an output with two columns: state and product_name. Until here that worked fine doing this:
SELECT p.product_name, c.state
FROM products p
INNER JOIN transactions t
ON p.product_id = t.product_id
INNER JOIN customers c
ON c.customer_id = t.customer_id
This selects all the products, and the states from where the customer is. The problem is that I can't find the way to rank the mos popular product per state. I tried different group by, order by and using subqueries without success. I suspect I need to do subqueries, but I can't find the way to resolve it. The expected outcome should look like this:
most_popular_product | state
Bamboo | WA
Walnut | MO
Any help will be greatly appreciated.
Thank you!
You need a subquery that gets the count of transactions for each product in each state.
SELECT p.product_name, c.state, COUNT(*) AS count
FROM products p
INNER JOIN transactions t
ON p.product_id = t.product_id
INNER JOIN customers c
ON c.customer_id = t.customer_id
GROUP BY p.product_name, c.state
Then write another query that has this as a subquery, and gets the highest count for each state.
SELECT state, MAX(count) AS maxcount
FROM (
SELECT p.product_name, c.state, COUNT(*) AS count
FROM products p
INNER JOIN transactions t
ON p.product_id = t.product_id
INNER JOIN customers c
ON c.customer_id = t.customer_id
GROUP BY p.product_name, c.state
) AS t
GROUP BY state
Finally, join them together:
SELECT t1.product_name AS most_popular_product, t1.state
FROM (
SELECT p.product_name, c.state, COUNT(*) AS count
FROM products p
INNER JOIN transactions t
ON p.product_id = t.product_id
INNER JOIN customers c
ON c.customer_id = t.customer_id
GROUP BY p.product_name, c.state
) AS t1
JOIN (
SELECT state, MAX(count) AS maxcount
FROM (
SELECT p.product_name, c.state, COUNT(*) AS count
FROM products p
INNER JOIN transactions t
ON p.product_id = t.product_id
INNER JOIN customers c
ON c.customer_id = t.customer_id
GROUP BY p.product_name, c.state
) AS t
GROUP BY state
) AS t2 ON t1.state = t2.state AND t1.count = t2.maxcount
This is basically the same pattern as SQL select only rows with max value on a column, just using the first grouped query as the table you're trying to group.

inner join three tables results in multiplied values

I'm trying to (let's say) gather a report on customers.
In that report I want to include sum of orders and ticket number for each client.
Tables:
Customer(id, name)
Order(id, customer_id, amount)
support_ticket(id, customer_id)
query:
select
c.id as 'Customer',
count(distinct t.id) as "Ticket count",
count(distinct o.id) as "Order count",
sum(o.amount) as 'Order Amount'
from customer as c
inner join `order` as o on c.id = o.customer_id
inner join support_ticket as t on c.id = t.customer_id
group by c.id
Since I join with customer.id on the two tables, I get all the rows "duplicated", since I get all possible combinations, so if the client as multiple tickets, the sum(o.amount) will we multiplied because of "duplicated rows"
sqlFiddle (mysql): http://sqlfiddle.com/#!9/ba39ba/13
sqlFiddle (pg): http://sqlfiddle.com/#!17/bc32e/7
It seems like a simple case but I've been looking at it too much I think, I can't find the proper way to do that report.
What am I doing wrong?
your best bet is to re-write the Aggregation off the Order table as as Derived Table;
EG
select
c.id as 'Customer',
count(distinct t.id) as "Ticket count",
o.amount as 'Order Amount' ,
o.[Order count]
from customer as c
inner join
(SELECT
o.customer_id,
sum(amount) as amount ,
count(distinct o.id) as "Order count"
from [order]
group by o.customer_id)
as o on c.id = o.customer_id
inner join support_ticket as t on c.id = t.customer_id
group by
c.id ,
o.amount ,
o.[Order count]
Note that the Derived Table Columns then are added to the group by clause at the bottom.
Cheers!
Just calculate order values in a sub-query and join it.
SELECT
c.id as 'Customer'
,count(DISTINCT st.id) as 'Ticket Count'
,o.`Order Count`
,o.amount as `Order Amount`
FROM customer c
INNER JOIN support_ticket st
on c.id = st.customer_id
INNER JOIN (
SELECT
customer_id
,SUM(amount) as 'amount'
,count(distinct id) as 'Order Count'
FROM `order`
group by customer_id
) o
on c.id = o.customer_id
GROUP BY c.id;
select c.id as 'Customer'
,t2.count_ticket as "Ticket count"
,t1.count_order as "Order count"
,t1.amount as 'Order Amount'
from customer as c
inner join (select customer_id
,count(id) as count_order
,sum(amount) as amount
from Order group by customer_id) t1
on c.id = t1.customer_id
inner join (select customer_id
,count(id) as count_ticket
from support_ticket group by customer_id) t2
on c.id = t2.customer_id
In cases like yours, when I think the solution of my problem should be fairly simple but I cant wrap my head around it, I tend to use a WITH clause.
Not because its better, but because it helps me to understand my code better by splitting up complexity. First I create a relatively simple temp. Solving the first part of my problem.
WITH temp AS (
SELECT
c.id AS "customer",
COUNT(DISTINCT o.id) AS "order_count",
SUM(o.amount) AS "order_amount"
FROM customer AS c
INNER JOIN "order" AS o on c.id = o.customer_id
GROUP BY c.id
)
Then I simply select the first half of my solution from temp, adding this way all intermediate results, and solve the second part of my initial sql.
SELECT
temp.customer,
COUNT(DISTINCT t.id) as "ticket_count",
temp.order_count,
temp.order_amount
FROM temp
INNER JOIN support_ticket as t on temp.customer = t.customer_id
GROUP BY temp.customer, temp.order_count, temp.order_amount
The principle is the same like in all previous answers, but SELECTS are separated and I can check them fast, and continue on if I'm happy with parts of the solution.

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;