MySQL Select Count of Duplicate Value In Relation Table - mysql

I have 3 tables: foods, order_detail, and orders
Here are the records for table foods:
id | name | type
------------------------------
F01 | Omelette | Breakfast
F02 | Burger | Breakfast
F03 | Satay | Lunch
F04 | Fried Rice | Dinner
Here are the records for table order_detail:
food_id | order_id
-----------------------------
F01 | T01
F04 | T01
F02 | T02
F03 | T03
F03 | T04
And here are the records for orders table:
order_id | date | qty
---------------------------------
T01 | 2017-05-01 | 2
T02 | 2017-05-02 | 1
T03 | 2017-05-05 | 1
T04 | 2017-05-07 | 1
I want to show count order detail grouped by food type. I expected this result:
type | total_order
-------------------------
Breakfast | 2
Lunch | 2
Dinner | 1
Here is my approach, but it still doesn't show the expected result.
SELECT
f.type,
(SELECT COUNT(*) FROM order_detail od WHERE f.id = od.food_id) AS total_order
FROM foods f
LEFT JOIN order_detail od ON f.id = od.food_id
GROUP BY f.type
ORDER BY f.id
The result is:
type | total_order
-------------------------
Breakfast | 1
Lunch | 2
Dinner | 1
How can I get the result I want? Thanks in advance!

Aggregation can work here, but you need to join across all three tables:
SELECT f.type, COUNT(o.order_id) AS total_order
FROM foods f
LEFT JOIN order_detail od ON od.food_id = f.id
LEFT JOIN orders o ON o.order_id = od.order_id
GROUP BY f.type
ORDER BY f.id;
Note that we do a left join across all three tables in order to not drop any food type which might happen to have zero orders.

you can add the orderdtails to the subselect to get the correct number
SELECT
f.type,
(SELECT COUNT(*) FROM order_detail od2
WHERE f.id = od2.food_id AND od2.order_id = od.order_id
) AS total_order
FROM foods f
LEFT JOIN order_detail od ON f.id = od.food_id
GROUP BY f.type
ORDER BY f.id

WITH cte AS (
SELECT type
FROM foods
INNER JOIN order_detail ON (order_detail.food_id=foods.id)
INNER JOIN orders ON (order_detail.ord_id=orders.ord_id)
)
SELECT DISTINCT type, COUNT(type) OVER (PARTITION BY type) AS qty
FROM cte

Related

MySQL nearest date without duplicated data

So I need to display all my customers and with the associated booking number (null if there is no booking) without duplicated custome. If the customer has lot of bookings I need to display only the nearest booking date. I don't understand why my query doesn't work.
Here is what is did : http://sqlfiddle.com/#!9/df0455/19
SELECT c.name, x.number, x.start_date
FROM customer c
LEFT JOIN
(SELECT b.customer_id, b.number, b.start_date
FROM booking b
INNER JOIN (
SELECT customer_id, MIN(ABS(TIME_TO_SEC(TIMEDIFF(NOW(), start_date)))) as mindiff
FROM booking
GROUP BY customer_id
) nearest ON b.customer_id = nearest.customer_id AND ABS(TIME_TO_SEC(TIMEDIFF(NOW(), start_date))) = mindiff
) AS x ON c.id = x.customer_id
Actually Paul is displayed three times and what is need is to display Paul just once with the nearest booking number who is booking-1 2019-11-05 21:45:00
I hope you can help me
You can filter with a row-limiting correlated subquery:
select c.name, b.number, b.start_date
from customer c
inner join booking b on b.customer_id = c.id
where b.start_date = (
select b1.start_date
from booking b1
where b1.customer_id = b.customer_id
order by abs(timestampdiff(second, now(), b1.start_date))
limit 1
)
In your DB Fiddle, this produces:
name number start_date
Paul booking-1 2019-11-05T21:45:00Z
John booking-3 2019-09-27T21:45:00Z
Morgan booking-5 2019-09-27T21:45:00Z
If you wanted to also display customers without bookings, then you would left join and move the filtering to the on clause of the join:
select c.name, b.number, b.start_date
from customer c
left join booking b
on b.customer_id = c.id
and b.start_date = (
select b1.start_date
from booking b1
where b1.customer_id = b.customer_id
order by abs(timestampdiff(second, now(), b1.start_date))
limit 1
)
You can use NOT EXISTS to get the nearest booking and join to customer:
SELECT c.id, c.name, t.number, t.start_date
FROM customer c
LEFT JOIN (
SELECT b.* FROM booking b
WHERE NOT EXISTS (
SELECT 1 FROM booking
WHERE customer_id = b.customer_id
AND ABS(TIMESTAMPDIFF(SECOND, NOW(), start_date)) < ABS(TIMESTAMPDIFF(SECOND, NOW(), b.start_date))
)
) t ON t.customer_id = c.id
See the demo.
Results:
| id | name | number | start_date |
| --- | ------ | --------- | ------------------- |
| 1 | Paul | booking-1 | 2019-11-05 21:45:00 |
| 2 | John | booking-3 | 2019-09-27 21:45:00 |
| 3 | Morgan | booking-5 | 2019-09-27 21:45:00 |
| 4 | Jane | | |
| 5 | Mike | | |

Multiple joins in mysql tables with union

In mysql, I am having an issue trying to get the right data. I think I have to use union to get all the results from both tables, but not sure how to do it.
Description of tables:
Order holds order numbers
Employee holds the employee
Zone holds the zones names
Actual time has the zone id, the order id and the amount of hours it should take to deliver
Deliver details contains the employee id, the zone the employee delivered and the amount of hours it took to deliver
Order
| id | number |
|----|--------|
| 1 | 0001 |
employees
| id | name |
|----|------|
| 1 | Jon |
zones
| id | name |
|----|-------|
| 1 | ZoneA |
| 2 | ZoneB |
actual_times
| id | zone_id | eta_hours | order_id |
|----|---------|-----------|----------|
| 1 | 1 | 5 | 1 |
| 2 | 2 | 4 | 1 |
deliver_details
| id | order_id | employee_id | zone_id | hours |
|----|----------|-------------|---------|-------|
| 1 | 1 | 1 | 1 | 3 |
| 2 | 1 | 1 | 1 | 1 |
What I am hoping to get is the zone name, the amount of hours it takes to deliver and the sum of hours the employee took deliver. If the employee did not deliver to that zone then show 0
Expected output
| zone_name | hours | eta_hours | employee_name |
|-----------|-------|-----------|---------------|
| ZoneA | 4 | 5 | Jon |
| ZoneB | 0 | 4 | Jon |
I tried making a union all on the actual time but I am not getting it right.
This is something I tried (note that this was just to get the right zones with deliver times and actual times).
SELECT deliver_details.zone_id, actual_times.zone_id, zones.zone_name FROM actual_times
RIGHT JOIN deliver_details ON actual_times.order_id = deliver_details.order_id
INNER JOIN zones ON zones.id = deliver_details.zone_id
WHERE deliver_details.order_id = 1
GROUP BY deliver_details.zone_id
UNION ALL
SELECT deliver_details.zone_id, actual_times.zone_id, zones.zone_name FROM actual_times
LEFT JOIN deliver_details ON actual_times.order_id = deliver_details.order_id
INNER JOIN zones ON zones.id = actual_times.zone_id
WHERE actual_times.order_id = 1
group by actual_times.zone_id
I am pretty much trying to get all of this in one query. Is there a way to do this?
Please note that this is a simplification to a more complex problem I am having. If you need more explanation or something does not make sense, please let me know.
No need to use UNION.
Start from table employees, then CROSS JOIN with zones and actual_times to get a simple cartesian products. Then search the deliver_details for deliveries performed by each employee on each zone ; use a LEFT JOIN for that. If an epmplyee did not deliver on a given zone, use COALESCE to return 0 instead of NULL.
Query :
select
z.name,
coalesce(sum(dd.hours), 0),
at.eta_hours,
e.name
from
employees e
cross join zones z
inner join actual_times at on at.zone_id = z.id
left join deliver_details dd on dd.employee_id = e.id and dd.id = at.zone_id
group by
z.name, at.eta_hours, e.name
I came up with a simillar solution to GMB, but using UNION to get rows with 0 hours...:
SELECT z.name, sum(dd.hours), at.eta_hours, e.name
FROM zones z JOIN deliver_details dd ON z.id = dd.zone_id
JOIN actual_times at ON z.id = at.zone_id
JOIN employees e ON dd.employee_id = e.id
GROUP BY z.name, at.eta_hours, e.name
UNION
SELECT z.name, 0, at.eta_hours, e.name
FROM zones z JOIN actual_times at ON z.id = at.zone_id,
employees e
WHERE e.id NOT IN (SELECT employee_id FROM deliver_details WHERE zone_id = z.id)
If you have only one employee for each zone the following query should work for you:
SELECT Z.name AS zone_name
,DT.hours_total
,ACT.eta_hours_total
,E.name AS employee_name
FROM zones Z
INNER JOIN (SELECT zone_id
, SUM(eta_hours) eta_hours_total
FROM actual_times
GROUP BY zone_id) ACT ON Z.zone_id = ACT.zone_id
INNER JOIN (SELECT zone_id
, employee_id
, SUM(hours) hours_total
FROM deliver_details
GROUP BY zone_id, employee_id) DT ON Z.zone_id = DT.zone_id
INNER JOIN employees E ON DT.employee_id = E.employee_id

Mysql select query for 3 tables

I have a 3 tables order, order_option, product_option
order
order_id | cus_name | cus_phone
-------------------------------
1 | Test-1 | 9876543211
2 | Test-2 | 9876543212
3 | Test-3 | 9876543213
4 | Test-4 | 9876543214
order_option
product_option_id | order_id
-------------------------------
11 | 1
12 | 1
13 | 2
14 | 4
15 | 3
product_option
product_id | product_option_id | sku | qty
------------------------------------------
1 | 11 | TS01 | 3
2 | 12 | TS02 | 2
3 | 13 | TS033 | 3
4 | 14 | TS023 | 3
Here I want to select order table and product_option table values with a where condition on the sku field.
i tried to join the query like below:
SELECT o.order_id, o.cus_name, o.cus_phone,po.sku,po.qty FROM order o
LEFT JOIN order_option op
ON (o.order_id = op.order_id)
LEFT JOIN product_option po
ON (op.product_option_id = po.product_option_id)
WHERE po.sku = "TS023"
But it's not showing the correct answer. I don't know what I have missed.
order is a reserved word, use backticks ``.
SELECT o.order_id, o.cus_name, o.cus_phone, po.sku, po.qty
FROM `order` o
LEFT JOIN order_option op ON o.order_id = op.order_id
LEFT JOIN product_option po ON op.product_option_id = po.product_option_id
WHERE po.sku = "TS023"
Output:
order_id cus_name cus_phone sku qty
4 Test-4 9876543214 TS023 3
SQL Fiddle: http://sqlfiddle.com/#!9/9b76b/2/0
Move the po condition from WHERE to ON to get true LEFT JOIN result:
SELECT o.order_id, o.cus_name, o.cus_phone,po.sku,po.qty FROM order o
LEFT JOIN order_option op
ON (o.order_id = op.order_id)
LEFT JOIN product_option po
ON (op.product_option_id = po.product_option_id)
AND po.sku = "TS023"
(When in WHERE, you'll get regular INNER JOIN result.)
#Matt is correct, here's another way of doing this.
SELECT o.order_id, o.cus_name, o.cus_phone, po.sku, po.qty
FROM `order` o, order_option op, product_option po
WHERE o.order_id = op.order_id
AND op.product_option_id = po.product_option_id
AND po.sku = "TSO23"

mysql sum column with same id

I have orders table, order_details table
I join orders table with order_details table to make a temporary table that look like this
order_id | customer_id | Quantity |
2 | 2 | 10 |
2 | 2 | 25 |
2 | 2 | 5 |
2 | 2 | 15 |
3 | 2 | 25 |
What I am trying to achieve is sum Quantity column where order_id is same.
so the end result look like this
order_id | customer_id | Quantity |
2 | 2 | 55 |
3 | 2 | 25 |
my sql statement to get to here look like this
SELECT
orders.orders_id,
orders.customer_id_fk,
sum(order_detail.quantity)
FROM orders
LEFT JOIN order_detail on orders.orders_id = order_detail.orders_id_fk
WHERE customer_id_fk IN(2) <-- this is needed because I only want to see customer 2.
how do I put a condition on the sum ?
Looks like you are very close!
You should be able to group by order_idto get a list of unique order_ids with the Quantity sum for each order_id
SELECT
orders.orders_id,
orders.customer_id_fk,
sum(order_detail.quantity)
FROM orders
LEFT JOIN order_detail on orders.orders_id = order_detail.orders_id_fk
WHERE customer_id_fk IN(2)
GROUP BY orders.orders_id,orders.customer_id_fk
use Group by
SELECT
orders.orders_id,
orders.customer_id_fk,
sum(order_detail.quantity)
FROM orders
LEFT JOIN order_detail on orders.orders_id = order_detail.orders_id_fk
WHERE customer_id_fk IN(2) GROUP BY orders.orders_id,orders.customer_id_fk
Here is OP. Sorry I derped. I put GROUP BY after Order by and it didnt work. the below sovled it
SELECT
orders.orders_id,
orders.customer_id_fk,
sum(order_detail.quantity)
FROM orders
LEFT JOIN order_detail on orders.orders_id = order_detail.orders_id_fk
WHERE customer_id_fk IN(2)
GROUP BY orders_id
ORDER BY customer_id

MySQL pivot table - NOT IN

In a MySQL database I have the following tables:
customers
CUSTOMER_ID | NAME
---------------------------
1 | John Doe
2 | Peter Jones
3 | David Smith
products
PRODUCT_ID | DESCRIPTION
---------------------------
1 | Toothbrush
2 | Shaving cream
3 | Deodorant
customer_product
CUSTOMER_ID | PRODUCT_ID
---------------------------
1 | 2
1 | 3
2 | 1
3 | 1
3 | 2
The table customer_product is a pivot table. When a customer orders a product, it will be logged there.
My question is: how can I select all customers that didn't ordered a certain product?
So for example, I want to retrieve all customers that never ordered a Toothbrush.
You can use NOT EXISTS:
SELECT CUSTOMER_ID,NAME
FROM customers AS c
WHERE NOT EXISTS (
SELECT 1
FROM customer_product AS cp
INNER JOIN products AS p
ON cp.PRODUCT_ID= p.PRODUCT_ID
WHERE p.DESCRIPTION = 'Toothbrush' AND cp.CUSTOMER_ID = c.CUSTOMER_ID)
Here is another way that will work for you
select c.customer_id,c.Name from customers c
where c.customer_id not in
(select c.customer_id from customers c
left join customer_product cp on c.customer_id = cp.customer_id
inner join products p on cp.product_id = p.product_id
where p.description ='toothbrush'
) ;