MySQL use DISTINCT with GROUP BY on same column - mysql

I currently have a query similar to:
select customers.customerId, items.itemName, sum(orders.quantity) as boughtTotal
from customers join orders on customers.customerId = orders.customerId
join items on items.itemId = orders.itemId
group by customers.customerId, items.itemName
order by boughtTotal desc;
The purpose of the query is to relate customers to items they bought, which may span over many orders, and total the amount of each unique item bought. This being achieved with what I have. I want to take this a step further now and select the most popular item for each customer. Since this is ordered with most bought items at the top, I figured I'd just have to add distinct next to customers.customerId in the select statement to have duplicates removed. However, adding distinct seems to do nothing. I'd appreciate help in knowing why distinct is seemingly doing nothing here, but also how to achieve what I'm trying to do - remove duplicates besides a customer's most popular item.
Tables:
customers
customerId | name
1 | John
2 | Jane
orders
orderId | customerId | quantity | itemId
1 | 1 | 11 | 1
2 | 2 | 13 | 2
3 | 1 | 4 | 2
4 | 2 | 14 | 1
5 | 1 | 1 | 1
items
itemId | itemName
1 | dog
2 | cat
So from this data the current query will return the following:
customerId | itemName | boughtTotal
2 | dog | 14
2 | cat | 13
1 | dog | 12
1 | cat | 4
And what I'd like to have is the following:
customerId | itemName | boughtTotal
2 | dog | 14
1 | dog | 12

Try this;)
select t1.*
from (
select customers.customerId, items.itemName, sum(orders.quantity) as boughtTotal
from customers join orders on customers.customerId = orders.customerId
join items on items.itemId = orders.itemId
group by customers.customerId, items.itemName) t1
inner join (
select max(boughtTotal) as boughtTotal, customerId
from (
select customers.customerId, items.itemName, sum(orders.quantity) as boughtTotal
from customers join orders on customers.customerId = orders.customerId
join items on items.itemId = orders.itemId
group by customers.customerId, items.itemName)t
group by customerId) t2 on t1.customerId = t2.customerId and t1.boughtTotal = t2.boughtTotal
DEMO HERE

Related

Something like forEach in SQL?

So i need to make a query that displays how many times each item has been sold, i have two tables that look something like this
Products
+-----------+-------+-----------+
| productID | price | inventory |
+-----------+-------+-----------+
| 31f9d | 21 | 109 |
| M21xZ | 80 | 87 |
| 56JmZ | 35 | 48 |
+-----------+-------+-----------+
orderDetails
+---------+-------+----------+
| orderId | item | Quantity |
+---------+-------+----------+
| 1 | 31f9d | 2 |
| 2 | 31f9d | 5 |
| 2 | 56JmZ | 3 |
+---------+-------+----------+
Thanks in advance!
If you just want the number of sales per product, you can aggregate the details table:
select item, count(*) as cnt_sales, sum(quantity) as sum_quantity
from orderdetails
group by item
If you want the details of each products too, then you can join.
select p.*, d.cnt_sales, d.sum_quantity
from products p
left join (
select item, count(*) as cnt_sales, sum(quantity) as sum_quantity
from orderdetails
group by item
) d on d.item = p.productid
The left join allows products without any sale; it will bring null values in columns cnt_sales and sum_quantity in the resultset.
Join both tables together, then simply group by itemId:
select p.productID, sum(o.Quantity) from Products p
left join orderDetails o on p.productID = o.item
group by p.productID

Need MySQL query from three tables

I have three tables:
Items:
Id |Name | Price | Cat_Id
---- |-------|----------------
1 | Item1 | 50.00 | 1
2 | Item2 | 25.20 | 5
Category:
Id |Name |
---- |------|
1 | Cat1 |
2 | Cat2 |
Discount:
Id |Item_Id| Client_Id|Discount
---- |-------|----------------
1 | 1 | 1 | 10
2 | 1 | 2 | 15
3 | 2 | 2 | 6
I am trying to get all items with proper discount, which is different for every customer. In this example i have client with Client_Id 1, which have discount for Item1/10/ and he doesn`t have discount on Item2.
The result should look like this:
Id |Name | Price | Cat | Discount
---- |-------|----------------|----------
1 | Item1 | 50.00 | Cat1| 10
2 | Item2 | 25.20 | Cat5| 0
My question is what is the way to build the query. I join first two tables and need to filter the third, but should I use temp table or do query in query?
It's Simple SQL Query.
Select Id, Name, Price, Cat, Discount From Items
left join discount on Items.id=discount.Item_Id
left join category on Items.cat_id=id
Output Like:
Id |Name | Price | Cat | Discount
---- |-------|----------------|----------
1 | Item1 | 50.00 | Cat1| 10
2 | Item2 | 25.20 | Cat5| 0
Try this way:
select di.id,di.Client_Id,it.Name,it.Price,ca.Cat,ifnull(di.Discount,0)
from Discount di
right join Items it on di.Item_id=it.Id
left join Category ca on it.Cat_Id=ca.Id
order by di_Id,di.Client_id,It.Name
You get all rows from discount and then get all items and look for the category. If an item has no discount(null) you get a 0
To get the result you want,
following is the query...
select
i.id item_id,
d.id discount_id,
i.name,
i.price,
c.name cat_name,
d.discount
from items i
left join discount d on i.id = d.item_id
left join category c on i.cat_id = c.id
where d.client_id = 1 or d.client_id is null;
/*where condition added after update as OP required*/
UPDATE
As per OP's comment
select
i.id item_id,d.id discount_id,
i.name,i.price,c.name cat_name,d.discount
from discount d
left join items i on i.id = d.item_id
left join category c on i.cat_id = c.id
where d.client_id = 2;

SQL Select employees with at least 2 orders with at least one of which is in a certain district, without nested SQL blocks

I have two tables, 'Customers' and 'Employees' that are joined over the third table 'Orders'
| CustomerID | CDistrict | | EmployeeID | EName |
|------------|-----------| |------------|-------|
| 1 | A | | 1 | Alex |
| 2 | A | | 2 | Bob |
| 3 | B | | 3 | Edd |
| 4 | C |
| OrderID | CustomerID | EmployeeID |
|---------|------------|------------|
| 1 | 1 | 1 |
| 2 | 3 | 1 |
| 3 | 3 | 2 |
| 4 | 5 | 2 |
| 5 | 1 | 3 |
How do I select the names of all employees who have served at least 2 orders, at least of which was for a customer from the district 'A' without nested SQL Blocks? That is, result should be 'Alex'.
I managed to do this with a nested query, as follows:
SELECT EName FROM Database.Employees
WHERE
EmployeeID IN
(SELECT EmployeeID FROM Database.Orders
GROUP BY EmployeeID
HAVING COUNT(*) >= 2)
AND
EmployeeID IN
(SELECT EmployeeID FROM Database.Orders
WHERE
CustomerID IN
(SELECT CustomerID FROM Database.Customers
WHERE CDistrict = 'A'));
However, I need to do this without the nested queries.
My query to select all employees who have served at least 2 orders looks like:
SELECT EName FROM Database.Employees, Database.Orders
WHERE
Employees.EID = Orders.EID
GROUP BY EName HAVING COUNT(OrderID) >=2
ORDER BY EName;
My query to select all employees who have served a customer from the district 'A' looks like:
SELECT EName FROM Database.Employees, Database.Orders, Database.Customers
WHERE
Employees.EID = Orders.EID
AND
Orders.CID = Customers.CID
AND
CDistrict = 'A';
However, when I try to join these queries into a single query like
SELECT EName FROM Database.Employee, Database.Orders, Database.Customers
WHERE
Employees.EID = Orders.EID
AND
Orders.CID = Customers.CID
AND
CDistrict = 'A'
GROUP BY EName HAVING COUNT(OrderID) >=2;
I become 0 results. I figured, this query probably searches for all employees, who have served at least 2 orders in the district 'A', but this is not what I need - I need employees who have served at least two orders, at least one of which was in the district 'A'. What am I doing wrong? How do I translate my nested query into a regular query?
Use GROUP BY and HAVING:
SELECT o.EmployeeID
FROM Database.Orders o JOIN
Database.Customers c
ON o.CustomerId = c.CustomerId
GROUP BY o.EmployeeID
HAVING COUNT(*) >= 2 AND
SUM(c.CDistrict = 'A') > 0;
SELECT E.EName
FROM Employees E INNER JOIN Orders O
ON O.EmployeeID=E.EmployeeID
INNER JOIN Customers C
ON C.CustomerID=O.CustomerID
WHERE C.CDistrict='A'
GROUP BY E.EmployeeID
HAVING COUNT(O.CustomerID) >= 2

MySQL Query -> Finding a record with two conditions in child table

I have two simple databases:
Customers:
+----+--------------------------+
| id | customer |
+----+--------------------------+
| 1 | Dent, Arthur |
| 2 | Prefect, Ford |
| 3 | Beeblebrox, Zaphod |
+----+--------------------------+
Purchases:
+-------------+--------------------------+
| Customer ID | SKU | Date |
+-------------+--------------------------+
| 1 | PRODUCT1 | 2016-01-16 |
| 1 | PRODUCT3 | 2016-01-18 |
| 2 | PRODUCT2 | 2015-12-12 |
| 2 | PRODUCT1 | 2016-02-10 |
| 3 | PRODUCT3 | 2016-03-01 |
+-------------+------------+-------------+
What I'm trying to do is construct a query that returns any customers who have bought both PRODUCT1 and PRODUCT3 (so in the example above, that should just be Arthur Dent).
I can easily select for one product; e.g.
SELECT `Customer ID` FROM `Customers` a LEFT JOIN `Purchases` b
ON a.`Customer ID` = b.`Customer ID` WHERE `SKU`=`PRODUCT1'
... but I can't figure out how to only select records in "Customers" where there are corresponding records in "Purchases" that match two SKUs?
You can do it via couple of exists clauses, e.g.:
select c.id, c.customer
from customer
where exists (
select customer_id from purchases
where customer_id = c.id and sku = 'PRODUCT1'
)
and exists(
select customer_id from purchases
where customer_id = c.id and sku = 'PRODUCT3'
);
There are a couple different ways to do this. One is to use aggregation and one is to join to the purchases table multiple times:
select c.customer
from customer c
join purchases p1 on c.id = p1.customerid and p1.sku = 'PRODUCT1'
join purchases p2 on c.id = p2.customerid and p2.sku = 'PRODUCT3'
Depending on your data, you may need to use distinct with this to eliminate duplicates.
Here's an alternative with aggregation:
select c.customer
from customer c
where exists (
select 1
from purchases p
where sku in ('PRODUCT1','PRODUCT2')
and c.id = p.customerid
having count(distinct sku) = 2
)

Add column from another table, but not affect count()

I have already a query with multiple JOINs, simple list of reservations
SELECT reservation.reservation_id, customer.customer_id, customer.name, count(ordered_services.reservation_id) AS num_of_ordered_services
FROM reservations
JOIN customers ON reservations.customer_id = customer.customer_id
LEFT JOIN ordered_services ON reservations.reservation_id = ordered_services.reservation_id
GROUP BY reservation.reservation_id, customer.customer_id, customer.name
ORDER BY reservation.reservation_id
which outputs something like
reservation_id | customer_id | name | num_of_ordered_services
1 | 1909091202 | John | 2
2 | 2512541508 | Jane | 3
I would like to add another column with information about payment, but simple JOIN, LEFT JOIN interferes with existing count() column. Like
SELECT reservation.reservation_id, count(payments.reservation_id) AS num_of_payments
FROM reservations
LEFT JOIN payments ON reservations.reservation_id = payments.reservation_id
GROUP BY reservation.reservation_id
ORDER BY reservation.reservation_id
reservation_id | num_of_payments
1 | 0
2 | 2
but in both a single result. How to achieve this?
PS: num_of_payments is not necessary, I only need to know if the payment for certain reservation exists or not (1, 0).
Thank you
tbl structure, nothing special:
reservations
reservation_id | customer_id | added
1 | 1909091202 | 2011-11-04 02:37:28
2 | 2512541508 | 2011-11-04 14:27:01
customers
customer_id | name | personal information columns ...
1909091202 | John | | |
2512541508 | Jane | | |
... | ... | | |
payments
payment_id | reservation_id | customer_id | total | added
1 | 2 | 1909091202 | 199 | 2011-11-04 02:37:28
2 | 2 | 2512541508 | 50 | 2011-11-04 14:27:01
You could use a subselect for the additional field.
SELECT reservation.reservation_id, customer.customer_id, customer.name,
count(ordered_services.reservation_id) AS num_of_ordered_services,
(SELECT count(*) FROM payments WHERE reservation.reservation_id=payments.reservation_id) AS num_of_payments
FROM reservations
JOIN customers ON reservations.customer_id = customer.customer_id
LEFT JOIN ordered_services ON reservations.reservation_id = ordered_services.reservation_id
GROUP BY reservation.reservation_id, customer.customer_id, customer.name
ORDER BY reservation.reservation_id
Something like the following should work:
select
reservation.reservation_id,
(case when exists (select * from payments p1 where p1.reservation_id = reservation.reservation_id) then 1 else 0 end) as one_or_many_payments_made
from reservation
GROUP BY reservation.reservation_id
ORDER BY reservation.reservation_id
But without your data, there is some guesswork here.