Sum childrens in two tables of a table - mysql

I Have 3 tables:
a (id,date,ckey) b(id,a.ckey,hht,hha) c(id,a.ckey,date_ini,date_fin)
where B keeps all the activities to be done and their respective hours in 2 places (hht,hha), while c saves the activities carried out with its initial and final date (to determine the hours executed the dates are subtracted).
Now I need to know, for each record in A how many hours you have assigned (B) and how many hours you have completed (C)
actually i have this:
a:
+----------+----------+------------+
| id | date | ckey |
+----------+----------+------------+
| 1 |2018-01-20| 18 |
|----------|----------|------------|
b:
+----------+----------+--------+--------+
| id | a.ckey | hht | hht |
+----------+----------+--------+--------+
| 1 | 18 | 2 | 3 |
| 2 | 18 | 2 | 5 |
| 3 | 18 | 0 | 7 |
+----------+----------+--------+--------+
c:
+----------+----------+----------------------+----------------------+
| id | a.ckey | date_ini | date_fin |
+----------+----------+----------------------+----------------------+
| 1 | 18 | 2019-01-23 13:30:00 | 2019-01-23 14:00:00 |
| 1 | 18 | 2019-01-23 14:00:00 | 2019-01-23 14:30:00 |
+----------+----------+----------------------+----------------------+
I need this:
+----------+----------+----------------------+----------------------+
| id | a.ckey | hours | hours2 |
+----------+----------+----------------------+----------------------+
| 1 | 18 | 19 | 1 |
+----------+----------+----------------------+----------------------+
I get this:
+----------+----------+----------------------+----------------------+
| id | a.ckey | hours | hours2 |
+----------+----------+----------------------+----------------------+
| 1 | 18 | 38 | 37.5 |
+----------+----------+----------------------+----------------------+
This is my query:
SELECT
(b.hht+b.hha) AS hours,
(SUM(b.hht+b.hha) -
FORMAT(IFNULL((TIMESTAMPDIFF(MINUTE, c.date_ini, c.date_fin)/60),0),2)) AS hours2
FROM a
LEFT JOIN b ON a.key=b.akey
INNER JOIN c ON a.key=c.akey
GROUP a.ckey

Because you have multiple rows in tables b and c for each value of ckey you need to do the aggregation within a subquery, otherwise you get duplicated rows leading to incorrect sums.
SELECT a.id, a.key, b.hours, FORMAT(c.minutes/60, 2) AS hours2
FROM a
LEFT JOIN (SELECT akey, SUM(hht+hha) AS hours
FROM b
GROUP BY akey) b ON b.akey = a.key
LEFT JOIN (SELECT akey, SUM(TIMESTAMPDIFF(MINUTE, date_ini, date_fin)) AS minutes
FROM c
GROUP BY akey) c ON c.akey = a.key
ORDER BY a.id
Output:
id key hours hours2
1 18 19 1.00
Demo on SQLFiddle

You're doing an m-to-n-join, try UNION ALL instead:
select ckey, sum(hours) as hours, sum(hours) - sum(hours2) as hours2
from
(
SELECT ckey, (b.hht+b.hha) AS hours, NULL as hours2
FROM b
UNION ALL
SELECT ckey, NULL AS hours,
FORMAT(IFNULL((TIMESTAMPDIFF(MINUTE, c.date_ini, c.date_fin)/60),0),2)) as hours2
FROM c
) as dt
group by ckey
If you actually need columns from table a put this Select in a Derived Table and join to it.

please check this
SELECT
(SELECT SUM(hha + hht) from b where b.ckey = a.ckey) hours,
FORMAT((SELECT SUM(TIMESTAMPDIFF(MINUTE, c.date_ini, c.date_fin)/60) from c where c.ckey = a.ckey),2) as hours2
FROM A
Fiddle

Related

How to get hotel total room and booked room in single query

How to get hotel total room and booked room in single query.
I used 2 queries get result. I need this in single query.
The issue I am experiencing since all counts are in this table htl_room_information.id_hotel
Booked room:
SELECT x.hotel_name
, count(i.id_hotel) room
FROM htl_booking_detail d
JOIN htl_branch_info_lang x
ON x.id=d.id_hotel
JOIN htl_room_information i
ON d.id_room=i.id
group
by x.hotel_name;
+------------------------------------------+------+
| hotel_name | room |
+------------------------------------------+------+
| hotel | 3 |
| hotel1 | 1 |
| hotel2 | 4 |
| hotel3 | 13 |
| hotel4 | 9 |
| hotel5 | 3 |
| hotel6 | 3 |
| hotel7 | 2 |
+------------------------------------------+------+
Total Rooms
SELECT (htl_branch_info_lang.hotel_name) as hotel_name,count(htl_room_information.id_hotel) as total_room FROM htl_room_information ,htl_branch_info_lang where htl_room_information.id_hotel=htl_branch_info_lang.id group by htl_branch_info_lang.hotel_name;
+------------------------------------------+------------+
| hotel_name | total_room |
+------------------------------------------+------------+
| hotel | 219 |
| hotel2 | 25 |
| hotel3 | 16 |
| hotel4 | 5 |
| hotel5 | 55 |
| hotel6 | 27 |
| hotel7 | 56 |
| hotel8 | 52 |
+------------------------------------------+------------+
Use dependent subqueries.
SELECT x.hotel_name,
(
SELECT count(i.id_hotel)
FROM htl_booking_detail d
JOIN htl_room_information i
ON d.id_room=i.id
WHERE x.id=d.id_hotel
) as room,
(
SELECT count(i.id_hotel)
FROM htl_room_information i
WHERE i.id_hotel=x.id
) as total_room
FROM htl_branch_info_lang x
I assume that the hotel_name is unique in the htl_branch_info_lang table. If not, you have to put distinct behind the first SELECT.
Hoping, I understood your question correctly.
Please check below query.
select b.hotel_name hotel_name, a.room ,
b.total_room
from
(SELECT htl_branch_info_lang.hotel_name,
count(htl_room_information.id_hotel) as room FROM htl_booking_detail,
htl_branch_info_lang, htl_room_information WHERE
htl_branch_info_lang.id=htl_booking_detail.id_hotel and
htl_booking_detail.id_room=htl_room_information.id group by
htl_branch_info_lang.hotel_name ) a RIGHT join (SELECT (htl_branch_info_lang.hotel_name) as
hotel_name,count(htl_room_information.id_hotel) as total_room
FROM htl_room_information ,htl_branch_info_lang
where htl_room_information.id_hotel=htl_branch_info_lang.id
group by htl_branch_info_lang.hotel_name) b
on a.hotel_name = b.hotel_name
;
You can use subquery
try this:
SELECT (a.hotel_name) as
hotel_name,count(htl_room_information.id_hotel) as total_room, (SELECT
count(htl_room_information.id_hotel) as room FROM htl_booking_detail,
htl_branch_info_lang b, htl_room_information WHERE
b.id=htl_booking_detail.id_hotel and
htl_booking_detail.id_room=htl_room_information.id and
b.hotel_name = a.hotel_name), count(htl_room_information.id_hotel) - (SELECT
count(htl_room_information.id_hotel) as room FROM htl_booking_detail,
htl_branch_info_lang b, htl_room_information WHERE
b.id=htl_booking_detail.id_hotel and
htl_booking_detail.id_room=htl_room_information.id and
b.hotel_name = a.hotel_name) as available FROM htl_room_information
,htl_branch_info_lang
a where htl_room_information.id_hotel=a.id group by
a.hotel_name;

How to use Mysql SUM with JOIN

I have the following tables:
purchase_tbl
id | productId | purchaseQuantity
---+-----------+-----------------
1 | 1 | 30
2 | 2 | 30
3 | 1 | 10
4 | 2 | 10
sale_tbl
id | productId | saleQuantity
---+-----------+-------------
1 | 1 | 10
2 | 2 | 10
3 | 1 | 10
4 | 2 | 10
5 | 1 | 10
6 | 2 | 10
I need to get the output as this one:
productId | totalPurchasedQuantity| totalSaleQuantity
----------+-----------------------+------------------
1 | 40 | 30
2 | 40 | 30
I'm using this query and how to get the desired result?
SELECT purchase_tbl.productId
, SUM(purchase_tbl.purchaseQuantity) AS totalPurchaseQuantity
, SUM(sale_tbl.saleQuantity) AS totalSaleQuantity
FROM purchase_tbl
JOIN sale_tbl
ON purchase_tbl.productId = sale_tbl.productId
GROUP BY purchase_tbl.productId
Current output
productId | totalPurchaseQuantity | totalSaleQuantity
----------+-----------------------+------------------
1 | 120 | 60
2 | 120 | 60
You better group then in separate query, as table have multiple records for each product, which getting cross product.
SELECT purchase.productId, totalPurchaseQuantity, totalSaleQuantity
FROM
(SELECT purchase_tbl.productId
, SUM(purchase_tbl.purchaseQuantity) AS totalPurchaseQuantity
FROM purchase_tbl
GROUP BY purchase_tbl.productId) purchase
INNER JOIN
(SELECT sale_tbl.productId
, SUM(sale_tbl.saleQuantity) AS totalSaleQuantity
FROM sale_tbl
GROUP BY sale_tbl.productId
) sale ON sale.productId= purchase.productId;
To obtain your expected result you have to do the aggregation on the individual table before joining them. Your query with be like:
SELECT A.productId, A.totalpurchaseQuantity, B.totalsaleQuantity
FROM
(SELECT productId, SUM(purchaseQuantity)
totalpurchaseQuantity FROM purchase_tbl
GROUP BY productId) A JOIN
(SELECT productId, SUM(saleQuantity)
totalsaleQuantity FROM sale_tbl
GROUP BY productId) B ON
A.productId=B.productId;

MySQl Query giving wrong result

Select * from YogaTimeTable;
Delete
from YogaTimeTable
Where RoomNum IN (select tt.RoomNum
from YogaRooms r,
YogaTypes t,
YogaTimeTable tt
where r.RoomNum = tt.roomNum
and ((r.RoomCapacity * t.ClassPrice) - (r.CostPerHour * tt.duration / 60)) < 200);
Select * from YogaTimeTable;
The goal is to delete any classes from the timetable that can make less than $200 profit. To calculate the profitability of each class, multiply the roomcapacity by the classprice and then subtract the cost of the room. To calculate the cost of the room multiply the costperhour by the duration divided by 60.
but it isn't giving the right result, can someone tell me where I made my mistake. Thanks. The tables are attached.
To me it looks like you have two problems.
A cross join between t and tt exists and should be resolved.
You're attempting to delete based on an incomplete or partial key of YogaTimeTable. The Unique Key of YogaTimeTable appears to be YogaID, StartTime,Day and RoomNum. I say this because the same yoga type could be in the same room at the same time on a different day, or in the same room on the same day at different start times. Thus I think the unique key for YogaTimeTable is a composite key of those 4 fields. So when deleting you need to use the complete key, not a partial key.
So this would result in.
.
DELETE FROM YogaTimeTable
WHERE exists
(SELECT 1
FROM YogaRooms r
INNER JOIN YogaTimeTable tt
on r.RoomNum = tt.roomNum
INNER JOIN YogaTypes t
on tt.YogaID = t.YogaID
WHERE YogaTimeTable.YogaID = TT.YogaID
and YogaTimeTable.RoomNum = TT.RoomNum
and YogaTimeTable.StartTime = TT.StartTime
and YogaTimeTable.Day = TT.Day
and ((r.RoomCapacity * t.ClassPrice) - (r.CostPerHour * tt.duration / 60)) < 200);
According to: I can use a correlated subquery to delete I just can't alias the table.... https://bugs.mysql.com/bug.php?id=2920
Profitability of all classes...
select ytt.YogaID,
ytt.Day,
ytt.StartTime,
ytt.RoomNum,
yt.ClassPrice,
ifnull(ytt.Duration,0) as Duration,
ifnull(yr.CostPerHour,0) as CostPerHour,
ifnull(yr.RoomCapacity,0) as RoomCapacity,
round( ifnull(yr.RoomCapacity,0)*yt.ClassPrice
- (ifnull(yr.CostPerHour,0)*ifnull(ytt.Duration,0)/60)
, 2) as Profitability
from YogaTypes yt
left join YogaTimeTable ytt on (ytt.YogaID=yt.YogaID)
left join YogaRooms yr on (yr.RoomNum=ytt.RoomNum);
+--------+-----------+-----------+---------+------------+----------+-------------+--------------+---------------+
| YogaID | Day | StartTime | RoomNum | ClassPrice | Duration | CostPerHour | RoomCapacity | Profitability |
+--------+-----------+-----------+---------+------------+----------+-------------+--------------+---------------+
| DRU | Wednesday | 10:30:00 | 1 | 18.50 | 60.00 | 100.00 | 20 | 270.00 |
| DRU | Tuesday | 17:00:00 | 2 | 18.50 | 90.00 | 50.00 | 10 | 110.00 |
| SUN | Monday | 07:30:00 | 3 | 18.00 | 60.00 | 150.00 | 25 | 300.00 |
| HAT | Tuesday | 07:30:00 | 4 | 20.00 | 90.00 | 70.00 | 15 | 195.00 |
| HAT | Monday | 18:30:00 | 4 | 20.00 | 60.00 | 70.00 | 15 | 230.00 |
| NULL | NULL | NULL | NULL | 17.00 | 0.00 | 0.00 | 0 | 0.00 |
+--------+-----------+-----------+---------+------------+----------+-------------+--------------+---------------+
6 rows in set (0.00 sec)
The classes with profitability less than desired...
select ytt.YogaID,
ytt.Day,
ytt.StartTime,
ytt.RoomNum
from YogaTypes yt
left join YogaTimeTable ytt on (ytt.YogaID=yt.YogaID)
left join YogaRooms yr on (yr.RoomNum=ytt.RoomNum)
where ifnull(yr.RoomCapacity,0)*yt.ClassPrice
- (ifnull(yr.CostPerHour,0)*ifnull(ytt.Duration,0)/60) < 200;
+--------+---------+-----------+---------+
| YogaID | Day | StartTime | RoomNum |
+--------+---------+-----------+---------+
| DRU | Tuesday | 17:00:00 | 2 |
| HAT | Tuesday | 07:30:00 | 4 |
| NULL | NULL | NULL | NULL |
+--------+---------+-----------+---------+
3 rows in set (0.00 sec)
Now to delete the undesirable sessions...
delete tt.*
from YogaTimeTable tt,
(select ytt.YogaID,
ytt.Day,
ytt.StartTime,
ytt.RoomNum
from YogaTypes yt
left join YogaTimeTable ytt on (ytt.YogaID=yt.YogaID)
left join YogaRooms yr on (yr.RoomNum=ytt.RoomNum)
where ifnull(yr.RoomCapacity,0)*yt.ClassPrice
- (ifnull(yr.CostPerHour,0)*ifnull(ytt.Duration,0)/60) < 200
) as unprof
where tt.YogaID=unprof.YogaID
and tt.RoomNum=unprof.RoomNum
and tt.Day=unprof.Day
and tt.StartTime=unprof.StartTime;
Query OK, 2 rows affected (0.00 sec)

Select max value from joined table

i need help with a mysql query. My tables:
objects
+---------+--------+
| id | name |
+---------+--------+
| 1 | house 1|
| 2 | house 2|
| 3 | house 3|
+---------+--------+
objects_expire
+----------+-----------+
| object_id| expire |
+----------+-----------+
| 1 | 2014-09-11|
| 1 | 2015-09-11|
| 2 | 2014-09-11|
| 2 | 2015-09-11|
| 2 | 2016-09-11|
| 3 | 2013-09-11|
| 3 | 2014-09-11|
| 3 | 2015-09-15|
+----------+-----------+
Now i need objects where max 'expire' is bigger then 2015-09-04 and smaller then 2015-09-18 (+/- 7 days)
Like this result:
+----------+-----------+-----------+
| object_id| expire | name |
+----------+-----------+-----------+
| 1 | 2015-09-11| house 1 |
| 3 | 2015-09-15| house 3 |
+----------+-----------+-----------+
This is what i have now:
SELECT o.id, MAX(uio.expire) AS object_expires
FROM objects AS o
LEFT JOIN objects_expire AS oe ON oe.object_id = o.id
WHERE expire < '2015-09-18'
AND expires > '2015-09-04'
GROUP BY o.id
But thats not correct.
Thanks for any help!!!
One usual approach is to do the grouping first and then join back, also if you do not want to hardcode the dates you can always use date_sub and date_add function to get -/+ 7 days from the current date.
select
o.id,
e.mexpire as expire,
o.name
from objects o
join(
select object_id,max(expire) as mexpire
from objects_expire
group by object_id
having mexpire > date_sub(curdate(),interval 7 day) and mexpire < date_add(curdate(),interval 7 day)
)e
on o.id = e.object_id
You need to group, and to use HAVING as a filter for the grouped column
select object_id, max(expire) as expire, name
from objects_expire
left join objects on objects_expire.object_id=objects.id
group by object_id, name
having max(expire) < '2015-09-17'
and max(expire) > '2015-09-03'

Using left join with min

I am trying to connect two tables with left join and a date.
My SQL Query
SELECT
ord.`ordernumber` bestellnummer,
his.`change_date` zahldatum
FROM
`s_order` ord
LEFT JOIN
`s_order_history` his ON ((ord.`id`=his.`orderID`) AND (ord.`cleared`=his.`payment_status_id`)) #AND MIN(his.`change_date`)
WHERE
ord.`ordertime` >= \''.$dateSTART.'\' AND ord.`ordertime` <= \''.$dateSTOP.'\'' ;
s_order
+----+---------------------+---------+-------------+
| id | ordertime | cleared | ordernumber |
+----+---------------------+---------+-------------+
| 1 | 2014-08-11 19:53:43 | 2 | 123 |
| 2 | 2014-08-15 18:33:34 | 2 | 125 |
+----+---------------------+---------+-------------+
s_order_history
+----+-------------------+-----------------+---------+---------------------+
| id | payment_status_id | order_status_id | orderID | orderID change_date |
+----+-------------------+-----------------+---------+---------------------+
| 1 | 1 | 5 | 1 | 2014-08-11 20:53:43 |
| 2 | 2 | 5 | 1 | 2014-08-11 22:53:43 |
| 3 | 2 | 7 | 1 | 2014-08-12 19:53:43 |
| 4 | 1 | 5 | 2 | 2014-08-15 18:33:34 |
| 5 | 1 | 6 | 2 | 2014-08-16 18:33:34 |
| 6 | 2 | 6 | 2 | 2014-08-17 18:33:34 |
+----+-------------------+-----------------+---------+---------------------+
Wanted result:
+-------------+---------------------+
| ordernumber | change_date |
+-------------+---------------------+
| 123 | 2014-08-11 22:53:43 |
| 125 | 2014-08-17 18:33:34 |
+-------------+---------------------+
The problem I have is getting only the date, where the cleared/payment_status_id value has been changed in s_order. I currently get all dates where the payment_status_id matches the current cleared value, but I only need the one, where it happend first.
This is only an excerpt of the actually query, since the original is a lot longer (mostly more left joins and a lot more tables).
You can group data by ordernumber
SELECT
ord.`ordernumber` bestellnummer,
MIN(his.`min_change_date`) as zahldatum
FROM
`s_order` ord
LEFT JOIN
`s_order_history` his ON ((ord.`id`=his.`orderID`) AND (ord.`cleared`=his.`payment_status_id`)) #AND MIN(his.`change_date`)
WHERE
ord.`ordertime` >= \''.$dateSTART.'\' AND ord.`ordertime` <= \''.$dateSTOP.'\''
GROUP BY
ord.`ordernumber`;
or you can group data in a subquery:
SELECT
ord.`ordernumber` bestellnummer,
his.`min_change_date` zahldatum
FROM
`s_order` ord
LEFT JOIN (
SELECT
orderID, payment_status_id, MIN(change_date) as min_change_date
FROM
s_order_history
GROUP BY
orderID, payment_status_id
) his ON (ord.`id` = his.`orderID` AND ord.`cleared` = his.`payment_status_id`)
WHERE
ord.`ordertime` >= \''.$dateSTART.'\' AND ord.`ordertime` <= \''.$dateSTOP.'\'';
Try this:
select s_order.ordernumber, min(s_order_history.change_date)
from s_order left join s_order_history
on s_order.id = s_order_history.orderID
and s_order.cleared = s_order_history.payment_status_id
group by s_order.order_id
SELECT ord.`ordernumber` bestellnummer,
MIN( his.`change_date` ) zahldatum
...
GROUP BY ord.`ordernumber`
MIN is an aggregate function so you can't use it in a JOIN straight up like you've tried above. You also are not comparing it to a value in your JOIN.
You'll want to do something like:
his.`change_date` = (SELECT MIN(his.`change_date`) FROM s_order_history where ord.`id` = his.`orderID`)
in your JOIN.