MySQL left join COUNT() and SUM() with CASE from same table - mysql

I have the following tables in a succession of 1-to-many relationships:
company_company, company_portfolio, building_site and statistics_meter. The area of difficulty I am having is the final table, statistics_meter.
For the benefit of this exercise, it's structure is as follows:
Records are related within the same table, with some being parent meters, and some being child meters. Where a record is a child, it will have parent_meter_id set, and building_id, which crucially, is how the table is LEFT JOIN'ed set to NULL.
id | parent_meter_id | site_ref | building_id
1 | NULL | some building | 45
2 | NULL | some other building | 45
3 | 1 | and another | NULL
4 | 1 | one another one | NULL
5 | 2 | final one | NULL
I have two requirements:
1 - count the number of parent meters where the building_id is set (which I am doing successfully)
2 - count the number of meters where the parent_meter_id matches the meter_id of those counted in (1)
Thus I would expect a result whereby (1) = 2 and (2) = 3.
Here is the SQL I've got so far...I tried fiddling around with a SUM case when but I think it's totally wrong. Is this even possible within one query?
Thanks for the help.
SELECT
building_site.id as site_id,
building_site.site_ref as building_name,
COUNT(statistics_meter.id) AS meter_count,
SUM(CASE WHEN statistics_meter.parent_meter_id = [???] THEN 1 ELSE 0 END) AS check_meter_count
FROM company_company
LEFT JOIN company_portfolio ON company_portfolio.company_id=company_company.id
LEFT JOIN building_site ON building_site.portfolio_id=company_portfolio.id
LEFT JOIN statistics_meter ON statistics_meter.building_id=building_site.id
WHERE company_company.id=41
GROUP BY building_site.id

Well if I understand you, you'll need to use a subquery to get the parent meters with a building id, and then join that to your main table.
SQL Fiddle
select
sm.id,
sm.parent_meter_id,
sm2.id as ID2,
sm.site_ref,
sm.building_id
from
statistics_meter sm
inner join (
select
id,
parent_meter_id
from
statistics_meter
where
building_id is not null) sm2
on sm.parent_meter_id = sm2.id

Not sure if this is the most efficient way to do it, but in the end I performed a left join and subquery as below and performed two counts, one COUNT() for total number to answer my requirement (2) and a COUNT(distinct) to answer my requirement (1)
SELECT
count(distinct statistics_meter.id) as meter_count,
count(statistics_meter.id) as check_meter_count
FROM company_company
LEFT JOIN company_portfolio ON company_portfolio.company_id=company_company.id
LEFT JOIN building_site ON building_site.portfolio_id=company_portfolio.id
LEFT JOIN statistics_meter ON statistics_meter.building_id=building_site.id
LEFT JOIN (select * from statistics_meter where parent_meter_id is not NULL) sm2 on sm2.parent_meter_id = statistics_meter.id

Related

MySQL left join with where condition

I have two tables like this
rooms
id | number
1 | 111
2 | 112
occupied_rooms
id | check_in | check_out | room_id
1 | 2017-10-01 | 2017-10-04 | 1
I want to get all the unoccupied rooms according to date check_in and check_out for this I tried
select r.id
, r.number
from rooms r
left join occupied_rooms o
on r.id = o.room_id
where (o.check_in not between "2017-10-05" and "2017-10-08" )
or (o.check_in >= "2017-10-05" and o.check_out <= "2017-10-08"))
but this query giving me result like this. which is incorrect.
id | number
1 | 111
What is wrong with this query?
Thank you for your any help and suggestions
Just join the two tables on the condition that the id matches and the range of intended stay overlaps with the range in the occupied_rooms table.
SELECT r.*
FROM rooms r
LEFT JOIN occupied_rooms t
ON r.id = t.id AND
('2017-10-02' <= t.check_out AND '2017-10-03' >= t.check_in)
WHERE
t.id IS NULL; -- NULL value indicates the room did not overlap
-- with any existing reservations
You can also check out this great reference question on how to deal with overlapping ranges in SQL queries. It makes the problem you are facing much simpler.
Demo
Your data in table occupied_rooms meets the first condition in "where";
check_in date(2017-10-01) is not between "2017-10-05" and "2017-10-08" and your where is or.
Thus, the result is included this data.
Can you tell us what output you expect?

MySQL query - select from one, count from another table

I have database with two tables - 'Warehouses' and 'Boxes'.
Each box has field with warehouse code, each Warehouse - 'capacity' field.
The purpose is to find only Warehouses that are "overfilled" (capacity of warehouse is less then number of all boxes with this warehouse code).
So, I count all boxes and join warehouse capacity by this query:
SELECT Warehouses.Code, Warehouses.Capacity, COUNT(Boxes.Code)
FROM `Warehouses` RIGHT JOIN
`Boxes`
on Warehouses.Code = Boxes.Warehouse
GROUP BY Boxes.Warehouse
Result:
------------------------------
Code | Capacity | COUNT
------------------------------
1 | 3 | 4
------------------------------
2 | 4 | 2
------------------------------
3 | 7 | 2
------------------------------
4 | 2 | 1
------------------------------
That returns me warehouse's capacity and counts boxes in it, but I don't know how and where to compare these numbers.
You do this in a HAVING clause:
SELECT w.Code, w.Capacity, COUNT(b.Code)
FROM `Warehouses` w LEFT JOIN
`Boxes` b
on w.Code = b.Warehouse
GROUP BY w.Code, w.Capacity
HAVING w.Capacity < COUNT(b.Code);
Notes:
LEFT JOIN is generally much easier to understand than RIGHT JOIN ("Keep all rows in the first table" versus "keep all rows in the last table, which I haven't read yet"). However, this query probably only needs an INNER JOIN.
Presumably, Warehouses should be the first table, because your question is about this entity.
The HAVING clause does the comparison after the aggregation.

Sql query to get data diffrence of total in 2 tables

I have two tables:
booking - records the order detail
id | booking_amount
-------------------
1 | 150
2 | 500
3 | 400
payment - records the payment for order
id | booking_id | amount
------------------------
1 | 1 | 100
2 | 1 | 50
2 | 2 | 100
I want to find all bookings where the payments are not complete. With the above data, we expect the answer to be 2,3, because the sum of payments for booking_id=1 matches the corresponding booking_amount in the booking_table.
To answer your question, you have 2 things you need to think about :
you want the total amount in your table payment by every booking row
you want to join your booking_amount table with payment.
Part 1 is quite simple:
SELECT sum(amount) as TotalP, booking_id FROM payment GROUP BY booking_id
Just a basic query with a simple aggregate function...
For part 2, we want to join booking_amount and payment; the basic JOIN would be:
SELECT * FROM booking b
LEFT JOIN payment p ON b.id = p.booking_id
We do a LEFT JOIN because we may have some booking who are not in the payment table. For those bookings, you will get NULL value. We will use a COALESCE to replace the NULL values by 0.
The final query is this:
SELECT b.id, COALESCE(TotalP, 0), b.booking_amount
FROM
booking b
LEFT JOIN
(SELECT sum(amount) as TotalP, booking_id FROM payment GROUP BY booking_id) as T
ON b.id = T.booking_id
WHERE COALESCE(TotalP, 0) < b.booking_amount
You need to use a outer join to combine your two tables and look for your conditions. Also, you will need to use SUM(..) function to get the sum of the amount for each id in the payment table.
Please try this:
select b.id from booking b
left outer join -- cant be inner join because we lose id:3 in that case.
(
select booking_id, SUM(amount) as Total
from payment group by booking_id
) p on b.id = p.booking_id
where b.booking_amount > Coalesce(Total,0) --Coalesce is required for such values coming NULL, like id:3, they will be assigned as 0.

Join tables keeping empty results

I have two tables that I need to cross and return as many results as the ids of one of them.
The first is a table of roles/tasks:
id | rolename
---+---------
1 | check_in
2 | cleaning
3 | taxi
4 | guide
5 | car_rental
6 | meals
7 | house_owner
20 | custom
and another table that has the columns:
id | client_booking_id | staff_role_id | confirmed | staff_cost
I need a query that always gives me as many results as nr of columns in the first table. Because for each unique client_booking_id there will be only one (if any) of those tasks/roles.
So if I do:
SELECT sr.role_name, sr.id, ss.staff_cost, ss.confirmed
FROM staff_role AS sr
LEFT JOIN staff_schedule AS ss ON sr.id=ss.staff_role_id
I get a result with the nr of rows I want. Now I need to match it with a specific client_booking_id so I did
SELECT sr.role_name, sr.id, ss.staff_cost, ss.confirmed
FROM staff_role AS sr
LEFT JOIN staff_schedule AS ss ON sr.id=ss.staff_role_id
WHERE ss.client_booking_id=1551 // <-- this is the new line
And this gives me only 2 results because in the second table I have only booked 2 tasks to a id.
But I need a result with all tasks even those that do not match, with NULL values. How can I do this?
With your query (without where clause) you get rows with null and non-null values for client_booking_id. You want to match specific client_booking_id and at the same time leave all records with null values, so you add additional condition with specific client_booking_id to left join.
Moving condition to left join:
select sr.role_name
, sr.id
, ss.staff_cost
, ss.confirmed
from staff_role sr
left join staff_schedule ss on sr.id = ss.staff_role_id
and ss.client_booking_id = 1551

Combine multiple SQL select statements into columns

I'm having a hard time wrapping my mind around this, any assistance is most appreciated.
I have two select statements with joins to 1 or more tables.
SELECT repinfo.repName, SUM(callstatssummary.CallsIn)
FROM repinfo
LEFT JOIN callstatssummary
ON repinfo.isaacID = callstatssummary.IsaacID AND callstatssummary.ShiftDate >= '2013-02-10' AND callstatssummary.ShiftDate <= '2013-02-16'
GROUP BY repinfo.repName;
The output of the first statement is a list of everyone in the repinfo table, with the sum of the total calls they took during the week. I used a left join to include people who didn't take calls in the result.
SELECT repinfo.repName, SUM(`1036`.afterRgu) - SUM(`1036`.priorRgu)
FROM repinfo
JOIN reporders
ON repinfo.repID = reporders.oRep
JOIN `1036`
ON reporders.workOrder = `1036`.workOrder AND `1036`.entryDate >= '2013-02-10' AND `1036`.entryDate <= '2013-02-16' AND `1036`.afterRgu >= `1036`.priorRgu
GROUP BY repinfo.repName;
The second statement outputs the number of products that each person sold during the week. The repinfo table has the information about the representative, which joins with the reporders table to match the work order. The 1036 table has detailed information about the orders.
I am looking to output something like this - essentially combine the output of the two select statements:
| repName | SUM(callstatssummary.CallsIn) | SUM(`1036`.afterRgu) - SUM(`1036`.priorRgu) |
______________________________________________________________________________________________
| Bruce W | 41 | 13 |
| Cathy M | 84 | 17 |
| Jonah S | NULL | 29 |
Any suggestions?
One way to combine those statements is to make each of them a derived-table / inline-view and join on repName.
Please note: Obviously you would want to join on a rep ID number (or whatever you call the primary key of the repinfo table) if two reps can have the same name.
select
r.repName, c.sumCallsIn, o.sumProdSold
from
repinfo r
left join (
SELECT repinfo.repName,
SUM(callstatssummary.CallsIn) sumCallsIn
FROM repinfo
LEFT JOIN callstatssummary
ON repinfo.isaacID = callstatssummary.IsaacID
AND callstatssummary.ShiftDate >= '2013-02-10'
AND callstatssummary.ShiftDate <= '2013-02-16'
GROUP BY repinfo.repName
) c
on c.repName = r.repName
left join (
SELECT repinfo.repName,
SUM(`1036`.afterRgu) - SUM(`1036`.priorRgu) sumProdSold
FROM repinfo
JOIN reporders
ON repinfo.repID = reporders.oRep
JOIN `1036`
ON reporders.workOrder = `1036`.workOrder
AND `1036`.entryDate >= '2013-02-10'
AND `1036`.entryDate <= '2013-02-16'
AND `1036`.afterRgu >= `1036`.priorRgu
GROUP BY repinfo.repName
) o
on r.repName = o.repName
order by r.repName;