mysql problem when doing 2 counts in the same query - mysql

First of all, apologies if this seems like a very dumb question, I have just started working with mySQL.
I have 2 tables:
1- Customer_courier_chat
2- Orders
Basically I want to do a query to count the amount of messages from a customer and the amount of messages from a courier. When I do an individual query to look for it, it works well but when I do both in the same query it returns different values.
The following query provides good results:
SELECT o.*, count(j.from_id) AS messages_courier
FROM test.orders o
INNER JOIN test.customer_courier_chat_messages j ON j.from_id = j.courier_id and o.order_id = j.order_id
Group BY o.order_id;
Results of the previous query messgaes from courier
The following one also provides good results:
SELECT o.*, count(k.from_id) AS messages_customer
FROM test.orders o
INNER JOIN test.customer_courier_chat_messages k ON k.from_id = k.customer_id and o.order_id = k.order_id
Group BY o.order_id;
Results of the previous query messages from customer
The main problem comes when I try to do both in the same query as the output is not correct.
SELECT o.*, count(j.courier_id) messages_courier, count(k.from_id) AS messages_customer
FROM test.orders o
INNER JOIN test.customer_courier_chat_messages j ON j.from_id = j.courier_id and o.order_id = j.order_id
INNER JOIN test.customer_courier_chat_messages k ON k.from_id = k.courier_id and o.order_id = k.order_id
Group BY o.order_id;
Results of previous query not working
Based on the results of the 2 invidiual queries, the previous one should provide:
order_id |city_code |messages_courier |messages_customer|
59528555 | ES | 2 | 5 |
11223344 | FR | 3 | 4 |
But, it actually provides:
order_id |city_code |messages_courier |messages_customer|
59528555 | ES | 4 | 4 |
11223344 | FR | 9 | 9 |
Am I missing something?
Thank you in advanced.
Kindly regards,
J

You can do it with conditional aggregation:
SELECT o.order_id, o.city_code,
SUM(m.from_id = m.courier_id) AS messages_courier,
SUM(m.from_id = m.customer_id) AS messages_customer
FROM test.orders o INNER JOIN test.customer_courier_chat_messages m
ON o.order_id = m.order_id
GROUP BY o.order_id, o.city_code;

You are first joining the table then using the count. You have to first pick get the count then use the join -
SELECT o.*, j.cnt messages_courier, k.cnt AS messages_customer
FROM test.orders o
INNER JOIN (SELECT order_id, COUNT(*) CNT
FROM test.customer_courier_chat_messages
WHERE from_id = courier_id
GROUP BY order_id) j ON o.order_id = j.order_id
INNER JOIN (SELECT order_id, COUNT(*) CNT
FROM test.customer_courier_chat_messages
WHERE from_id = customer_id
GROUP BY order_id) k ON o.order_id = k.order_id;

Related

Getting associated data from max column with multiple tables in MySQL

I have two tables, Orders and OrderDetails. I would like to create an SQL query that retrieves the OrderID, Unit Cost and Process Date for orders with the latest processed T-Shirts for each user.
I have created a very simplified version of my tables in this SQL fiddle.
From my google searches, most of which ended up here on SO, I have managed to figure out SQL to get the associated unit cost for the latest processed order of T-shirts per order, but I'm not sure how to further condense it from here to be for each user and still maintain the correct unit cost and process dates. I know I can't use MAX and GROUP BY and I can't work out how to do a LEFT OUTER JOIN (like how I did the first part in the SQL fiddle) when the UserID and ProcessDate reside in different tables.
For the dataset in the SQL fiddle, the output I would be looking for would be
| UserID | OrderID | UnitCost | ProcessDate |
|-------- |--------- |---------- |------------- |
| 1 | 2 | 3 | 2018-03-01 |
| 2 | 3 | 1 | 2018-04-01 |
| 3 | 5 | 3 | 2018-06-01 |
Above query output is perfectly fine just the case is correlated subquery gets executed everytime for each row which is quite a overhead and in case of large data there will be performance issue.
I have changed the subquery into join which may be helpful.
SELECT O.UserID
, O.OrderID
, OD.UnitCost
, OD.ProcessDate
FROM Orders O
left
JOIN OrderDetails OD
ON O.OrderID = OD.OrderID
left
join
( SELECT o2.userid
, max(od2.processdate) newdate
FROM Orders o2
JOIN OrderDetails od2
ON o2.OrderId = od2.OrderId
WHERE od2.Product = 'T-Shirts'
group
by o2.userid
) temp
on O.userid = temp.userid
WHERE OD.Product = 'T-Shirts'
and OD.processdate = temp.newdate
ORDER
BY OD.OrderDetailID;
Hope it helps.
You can select the "latest" order for each user using a correlated subquery:
SELECT O.UserID, O.OrderID, OD.UnitCost, OD.ProcessDate
FROM OrderDetails OD LEFT JOIN
Orders O
ON O.OrderID = OD.OrderID
WHERE OD.Product = 'T-Shirts' AND
o.OrderID = (SELECT MAX(o2.OrderId)
FROM Orders o2
WHERE o2.UserID = o.UserID
)
ORDER BY OD.OrderDetailID;
This assumes that the latest order is the one with the largest OrderId.
EDIT:
Actually, the above happens to work on your data, because the largest order id for each user has t-shirts. However, you explicitly say that you want the latest order with t-shirts, so that requires a slight modification:
SELECT O.UserID, O.OrderID, OD.UnitCost, OD.ProcessDate
FROM Orders O LEFT JOIN
OrderDetails OD
ON O.OrderID = OD.OrderID
WHERE OD.Product = 'T-Shirts' AND
o.OrderID = (SELECT MAX(o2.OrderId)
FROM Orders o2 JOIN
OrderDetails od2
ON o2.OrderId = od2.OrderId
WHERE od2.Product = 'T-Shirts' AND
o2.UserID = o.UserID
)
ORDER BY OD.OrderDetailID;
Or, if you want to go by process date:
SELECT O.UserID, O.OrderID, OD.UnitCost, OD.ProcessDate
FROM Orders O LEFT JOIN
OrderDetails OD
ON O.OrderID = OD.OrderID
WHERE OD.Product = 'T-Shirts' AND
od.ProcessDate = (SELECT od2.ProcessDate
FROM Orders o2 JOIN
OrderDetails od2
ON o2.OrderId = od2.OrderId
WHERE od2.Product = 'T-Shirts' AND
o2.UserID = o.UserID
ORDER BY od2.ProcessDate DESC
LIMIT 1
)
ORDER BY OD.OrderDetailID;

distinct records sum when joining different tables in mysql

Table : O
ID Date
1 2016-01-10
2 2016-01-10
3 2016-01-11
Table : OD
ODI ID Quantity
1 1 1
2 1 2
3 2 1
4 3 1
Table: OH
OHI ID
1 1
2 2
3 1
4 3
I have three table O,OD,OH. I need to join these three table and get the sum of quantity For each day.
Tried
SELECT O.date,SUM(od.Quantity),group_concat(OHI) FROM O
INNER JOIN OD ON OD.ID = O.ID
INNER JOIN OH ON OH.ID = O.ID
GROUP BY O.date;
But the resulting quantity sum was different due to joining the OH Table.Now How do i get the proper Sum.
Expected Result :
Date SUM(od.Quantity)
2016-01-10 4
2016-01-10 1
Sorry For Change In The Question.
You don't need to join OH table, this can be done with below query:
SELECT O.ID,SUM(od.Quantity) FROM O
INNER JOIN OD ON OD.ID = O.ID
GROUP BY O.ID;
You don't need to join with the OH table.
select O.ID, SUM(OD.Quantity) total_qty
from O inner join OD
on O.ID = OD.ID
group by O.ID;
If you need join with OH, then do it after aggregation.
select *
from OH inner join (
select O.ID, SUM(OD.Quantity) total_qty
from O inner join OD
on O.ID = OD.ID
group by O.ID
) t on t.ID = OH.ID;
Aggregate over the OD table first in a subquery, then join this to the other two tables:
SELECT t1.Date,
t2.Quantity,
t3.OHI -- and possibly other columns from OH
FROM O t1
INNER JOIN
(
SELECT ID, SUM(Quantity) AS Quantity
FROM OD
GROUP BY ID
) t2
ON t1.ID = t2.ID
INNER JOIN OH t3
ON t1.ID = t3.ID

Mysql query with count(distinct) show nothing if group by has no result

I have a query on 3 tables, settlement, order and passenger.
A settlement has orders, and orders has passengers.
TABLE: Settlemnt
id
TABLE: Order
id settlement_id
TABLE: passenger
id orders_id
If settlement has no orders (and then no passengers), it returns nothing.
What I want, is to return the empty settlement even if it has no orders.
It is the count(passengers) that ruins it for me and returns no settlement if empty.
This is the simplified query:
select s.id, o.id, count(distinct p.id)
from settlement s
left join orders o ON o.settlement_id = s.id
left join passenger p on p.orders_id = o.id
where s.date = '2016-02-02';
group by o.id
How can I make it return settlements with no orders?
Works for me...
SELECT s.id s_id
, o.id o_id
, p.id p_id
FROM settlement s
LEFT
JOIN orders o
ON o.sid = s.id
LEFT
JOIN passengers p
ON p.oid = o.id;
+------+------+------+
| s_id | o_id | p_id |
+------+------+------+
| 1 | NULL | NULL |
| 2 | 1 | 1 |
+------+------+------+
http://sqlfiddle.com/#!9/9a01b/4
The major problem with your query appears to be that you GROUP BY the o.id. Any settlement that has no order will have an o.id of NULL, hence all settlements with no orders will be lumped together into one row. Which s.id that is returned for this o.id is not defined (and effectively random).
Change it to GROUP BY both s.id and o.id
SELECT s.id,
o.id,
COUNT(DISTINCT p.id)
FROM settlement s
LEFT JOIN orders o ON o.settlement_id = s.id
LEFT JOIN passenger p ON p.orders_id = o.id
WHERE s.date = '2016-02-02';
GROUP BY s.id,
o.id

MySQL join table return only one match

I'm struggling the the JOIN in a MySQL query. Somehow I can't find out why my result is not what I want.
I have two tables, a table orders and a table products. The table product holds the order.id of the order. So a order can have more than one products, so for example the table products holds two records for a order.
The result I need is all orders where a product holds a VAT of 21.
Table example.
orders
id | customer
---------------
1 | John Doe
2 | Hello World
order_products
id | order_id | product | vat
1 | 1 | Porsche 911 GT4 | 21
2 | 1 | Audi R8 LMS | 21
3 | 1 | Ferrari Enzo | 19
3 | 2 | Bugatti Veyron | 19
No I want all orders where the products have a VAT of 21. So I will do a LEFT JOIN on the table order_products:
SELECT orders.id, orders.customer, order_products.product FROM orders LEFT JOIN order_products ON orders.id = order_products.order_id WHERE order_products.vat = '21'
This returns the following:
1 John Doe Porsche
1 John Doe Audi R8 LMS
But I only need one result because the orders.id is important for me, not all products in the order. I only join on the order_products to get the orders with only VAT 21. At the moment I ran out of options on how to fix this. Even after reading several topics about joins on this site and other sites.
First, you aren't going to return any orders that don't have products, so there is no need for a left join...an inner join is fine.
If the orders_product is not important to you, you can use a subquery and not select any columns from the orders_product. With your current query, you're selecting a column though.
Something like...
SELECT id, customer
FROM orders
WHERE order_id IN (SELECT order_id FROM order_products
WHERE order_products.vat = '21');
If you prefer not to use a correlated subquery, you can use a group by or distinct
SELECT orders.id, orders.customer
FROM orders
INNER JOIN order_products ON orders.id = order_products.order_id
WHERE order_products.vat = '21'
GROUP BY orders.id, orders.customer;
or...
SELECT DISTINCT orders.id, orders.customer
FROM orders
INNER JOIN order_products ON orders.id = order_products.order_id
WHERE order_products.vat = '21';
If you only care about the orders then you can group by the order id:
SELECT orders.id, orders.customer, order_products.product
FROM orders
LEFT JOIN order_products ON orders.id = order_products.order_id
WHERE order_products.vat = '21'
GROUP BY orders.id;
However note that order_products.product will only be one of the products. If you wish to display all of the products but in the one column you can user GROUP_CONCAT:
SELECT orders.id, orders.customer, GROUP_CONCAT(order_products.product)
FROM orders
LEFT JOIN order_products ON orders.id = order_products.order_id
WHERE order_products.vat = '21'
GROUP BY orders.id;
This will return:
1 John Doe Porsche, Audi R8 LMS
SELECT DISTINCT o.id
, o.customer
FROM orders o
JOIN order_products op
ON op.order_id = o.id
WHERE op.vat = 21;

Union Two SQL Queries

I have a query like this
SELECT o.product_id,
p.name,
count(o.product_id) AS total_products_sold,
(SELECT count(o.id)
FROM ebay.`order` o) AS total_orders
FROM ebay.`order` o
INNER JOIN product p ON o.product_id = p.id
GROUP BY o.product_id
The total_orders is rerun when executed for each which not i want. I
Question:
I want the total_orders combines with every result set from the outer query.
I tried this but it only return 1 row
SELECT tps.product_id,
tps.name,
tps.total_products_sold,
count(oo.id) AS total_orders
FROM ebay.`order` oo
INNER JOIN
( SELECT o.id,
o.product_id,
p.name,
count(o.product_id) AS total_products_sold
FROM ebay.`order` o
INNER JOIN product p ON o.product_id = p.id
GROUP BY o.product_id ) AS tps ON oo.product_id = tps.product_id
Any better solution ?
Thanks.
You can use with rollup which will give you the total without changing the actual query
It wont give you the result in column of every row but you will get the result of total orders in the last row.
SELECT
o.product_id,
p.name,
count(distinct o.id) AS totalorder
FROM
ebay.`order` o
INNER JOIN
product p
ON
o.product_id = p.id
GROUP BY
o.product_id
WITH ROLLUP
For example
+-----------+------+------------+
| product_id| name | totalorder |
+-----------+------+------------+
| 2000 | A | 10 |
| 2001 | B | 20 |
| NULL | NULL | 30 | <--- Last row is having the Total order
+-----------+------+------------+
WITH ROLLUP
SELECT tps.product_id,
tps.name,
tps.total_products_sold,
s.total_orders
FROM ebay.`order` oo
INNER JOIN
(
SELECT o.id,
o.product_id,
p.name,
count(o.product_id) AS total_products_sold
FROM ebay.`order` o
INNER JOIN product p
ON o.product_id = p.id
GROUP BY o.product_id
) AS tps ON oo.product_id = tps.product_id
CROSS JOIN
(
SELECT count(id) total_orders
FROM ebay.`order`
) s