Joining three tables mysql - mysql

I have three tables:
Houses Table
id title description
--------------------------
1 Big House Very big house
2 Small House Very small house
Rooms Table
id title room_status
--------------------------
1 Green room 24
2 Yellow room 25
3 Blue room 24
Houses_Rooms Table
id house_id room_id
-------------------------
1 1 1
2 2 3
3 1 2
I have room status 24, for example.
I need get all houses from Houses Table that contains rooms with room_status = 24. But, if house has rooms with different room_status we do not need to choose this house.
I build some query and i use WHERE house.id IN (SELECT ...). It works, but returns houses with same room_status, because IN.

You seem to want houses where all rooms have status 24.
WHERE house.id IN (SELECT ...) is a good idea. Now you need a subquery that only contains houses with only status-24 rooms.
select *
from houses
where id in
(
select hr.house_id
from houses_rooms hr
join rooms r on r.id = hr.room_id
group by hr.house_id
having min(r.room_status) = 24
and max(r.room_status) = 24
);
However, as it is unlikely that a house has no rooms at all, we can simply exclude houses with non-status-24 rooms instead:
select *
from houses
where id not in
(
select house_id
from houses_rooms
where room_id in (select id from rooms where status <> 24)
);

You should use an INNER JOIN instead of a sub-query for this kind of query.
SELECT
HR.ID
,H.Title
,H.Description
,R.Title
FROM #House_Room HR
INNER JOIN #Houses H on HR.HouseID = h.ID
INNER JOIN #Rooms R on HR.RoomID = r.ID
WHERE R.RoomStatus = 24
Returns:
1 | Big House | Very big house | Green room
2 | Small House | Very small house | Blue room

The below query selects all houses that have rooms with room_status = 24, but excludes houses that also have a room_status <> 24. It can probably be further optimized but my SQL is a little rusty:
select h.*
from houses h
join houses_rooms hr on h.id = hr.house_id
join rooms r on hr.room_id = r.id
where r.room_status = 24
and h.id not in (
select h.id
from houses h
join houses_rooms hr on h.id = hr.house_id
join rooms r on hr.room_id = r.id
where r.room_status <> 24
);
SQLFiddle Example

Try this:
SELECT A.*
FROM Houses A JOIN Houses_Rooms B
ON A.id=B.house_id JOIN (SELECT * FROM Rooms WHERE room_status=24) C
ON C.id=B.room_id;

This should work because it uses JOINS to get those that have a room with room_status = 24 and then uses NOT IN to rule out those that have other room statuses
SELECT * FROM Houses_Rooms hr
JOIN Houses h on hr.house_id = h.id
JOIN Rooms r on hr.room_id = r.id
WHERE r.room_status = 24
AND h.id NOT IN(
SELECT h.house_id FROM Houses_Rooms h
JOIN Rooms r on h.room_id = r.id
WHERE r.room_status <> 24
)

Select houses.title as 'house name'
From houses, rooms,houses_rooms
where houses.id=houses_rooms.house_id
and rooms.id=houses_rooms.room_id
and rooms.room_status=24
try this..am not 100% sure..i usually dont use joins.

Related

Trying to get a count in 3 tables on MySql

So I have 3 tables in a database
One is hotel which has hotel_id and status
one is partner which has partner_id and partner_name
and one is partner_hotel which has hotel_id and partner_id
What I am trying to get is the count for each partner that has a hotel with the status = 1
The closest I have gotten is
select p.partner_name,count(hotel_id)
from partner_hotel ph
join partner p on p.partner_id = ph.partner_id
group by ph.partner_id;
The problem is that does not limit to ones with a status of 1 and nothing I seem to be doing seems to work.
you should join hotel too if th hotel contain status and group by p.partner_id
select p.partner_name,count(*)
from partner_hotel ph
inner join partner p on p.partner_id = ph.partner_id
inner join hotel h on ph.hotel_id = h.hotel_id
where h.status = 1
group by p.partner_id;
or ig hotel use id
select p.partner_name,count(*)
from partner_hotel ph
inner join partner p on p.partner_id = ph.partner_id
inner join hotel h on ph.hotel_id = h.id
where h.status = 1
group by p.partner_id;
I guess you're trying to find the number of partners with one or more hotels having a status = 1. If my guess is correct, this gets you the one number.
SELECT COUNT(DISTINCT p.partner_id) partner_count
FROM partner p
JOIN partner_hotel ph ON p.partner_id = ph.partner_id
JOIN hotel h ON ph.hotel_id = h.hotel_id AND h.status = 1
If your requirement is to show one result row for each partner showing the number of hotels with status = 1, that will go like this.
SELECT COUNT(*) hotel_count,
p.partner_id
FROM partner p
JOIN partner_hotel ph ON p.partner_id = ph.partner_id
JOIN hotel h ON ph.hotel_id = h.hotel_id AND h.status = 1
GROUP BY p.partner_id
Pro tip: When preparing to write a query, it's helpful to imagine the result set you want. Ask yourself these questions:
How many rows must the result set have?
It should have one row per ____ (row per what? Hotel, Partner, Guest, Hotel with status = 1?).
Each row should show ____ values?
If you do this work of imagination, writing the query will be much easier.

MySQL many-to-many junction table: Select all entries from A which contain no values in B not in list

I have a many-to-many relationship, joined through a junction table. My specific case is recipes and ingredients. I want to select all recipes which don't contain ingredients not in a given list. For example, if I input cheese, toast and crackers, I want the results to include cheese on toast, cheese with crackers, but not jam on toast.
So something like:
SELECT * FROM
recipe
JOIN recipe_ingredient on recipe.id = recipe_ingredient.recipe_id
JOIN ingredient on ingredient.id = recipe_ingredient.ingredient_id
WHERE ingredient.name
???
("cheese", "toast", "crackers")
Selecting recipes which do contain any or all of these ingredients is easy enough, but if it can be avoided I don't want to have to then subsequently filter out results which contain unlisted ingredients.
Edit:
Some example tables:
ingredient
-----------
id | name
1 | "cheese"
2 | "toast"
3 | "crackers"
4 | "jam"
recipe
-----------
id | name
1 | "cheese on toast"
2 | "cheese with crackers"
3 | "jam on toast"
recipe_ingredient
-------------------------
recipe_id | ingredient_id
1 | 1
1 | 2
2 | 1
2 | 3
3 | 2
3 | 4
One way to achieve this would be to select recipes that have any ingredient not listed in your criteria to match using ALL with a subquery:
SELECT r.id
FROM recipe r
JOIN recipe_ingredient ri on r.id = ri.recipe_id
JOIN ingredient i on i.id = ri.ingredient_id
WHERE i.name <> ALL ( SELECT 'cheese' UNION SELECT 'toast' UNION SELECT 'crackers' )
GROUP BY r.id
To retrieve only those recipees that match your conditions you could wrap the above statement using the very same <> ALL comparison.
SELECT *
FROM recipe
WHERE id <> ALL (
SELECT r.id
FROM recipe r
JOIN recipe_ingredient ri on r.id = ri.recipe_id
JOIN ingredient i on i.id = ri.ingredient_id
WHERE i.name <> ALL ( SELECT 'cheese' UNION SELECT 'toast' UNION SELECT 'crackers' )
GROUP BY r.id
);
Additional note: Actually NOT IN is an alias for <> ALL, so you could use them interchangeably.
Given your sample it would only return:
id | name
---|-------------------------
1 | cheese on toast
2 | cheese with crackers
See it working here: http://sqlfiddle.com/#!9/f20010/25
Pretty trick this one. But it can be done. The trick part here is to know how much ingredients each recipe has and then compare it with amount of ingredients with the given parameters.
select tb.name
from ( select r.id, r.name, count(*) qtd
from ingredient i
inner join recipe_ingredient ri
on i.id = ri.ingredient_id
inner join recipe r
on r.id = ri.recipe_id
where i.name in ('cheese', 'toast', 'crackers')
group by r.id, r.name
) tb
where exists ( select 1
from ingredient i
inner join recipe_ingredient ri
on i.id = ri.ingredient_id
inner join recipe rr
on rr.id = ri.recipe_id
where rr.id = tb.id
group by rr.id
having count(*) = tb.qtd)
First I selected all recipes that has those ingredients that you filter counting from it how much ingredients it has. That first query will give me:
"cheese on toast" 2
"cheese with crackers" 2
"jam on toast" 1
And on the EXISTS clause I made a subquery to count the total ingredients that all recipes have and joined with the upper subquery. So it will only give me the ones listed.
See it working here: http://sqlfiddle.com/#!9/f20010/22

Order data by data from another table

I have two tables:
rooms (all the rooms)
id | title | ...
-----------------
1 |Room 1 |
2 |Room 2 |
3 |Room 3 |
user_rooms (in which room is every user, column user is user's id and it's primary column)
user | room | ...
------------------
20 | 3 |
14 | 1 |
35 | 3 |
So I want to select all the rooms from the 'rooms' table but to order them in that way to show the rooms with the most users in them and after that the rooms with less and less users. For example, I want to show room 3 first (because 2 users are in it), then room 1 (one user in it), and finally room 2 (since no users are in it). How to achieve that?
SELECT aa.id, aa.title
FROM rooms AS aa
LEFT JOIN (
SELECT room, COUNT(*) AS total_count
FROM user_rooms
GROUP BY room
) AS _aa
ON aa.id = _aa.room
ORDER BY _aa.total_count;
This would often be done without a subquery:
select r.id, r.title, count(ur.room) as numusers
from rooms r left join
user_rooms ur
on r.id = ur.room
group by r.id, r.title
order by numusers desc;
This would often be more efficient than a version using a subquery in the from clause because it can take advantage of an index on the join key.
Interestingly, the same index would be used for a correlated subquery in the select, which is an alternative approach:
select r.id, r.title,
(select count(*)
from user_rooms ur
where r.id = ur.room
) as numusers
from rooms r
order by numusers desc;
This might be the most efficient approach, because it removes the aggregation in the outer query.
select r.id, r.title, coalesce(t.cnt,0)
from rooms r left join
(select room, count(*) as cnt
from user_rooms
group by room) t on t.room = r.id
order by t.cnt desc
This will give you only rooms with users
SELECT title, count(user) As MostUsers
FROM Rooms R
INNER JOIN user_rooms U
ON R.?field? = U.?field?
GROUP BY title
ORDER BY MostUsers
You need to complete the query inserting the names of the fields that you can use to Join the tables
If you want all rooms you can use a Left Join:
SELECT title, count(user) As MostUsers
FROM Rooms R
LEFT JOIN user_rooms U
ON R.?field? = U.?field?
GROUP BY title
ORDER BY MostUsers
Please try the following query:
select * from rooms r
order by (select count(1)
from userroom ur
where ur.roomid = r.roomid ) desc

MySQL - joins with SUM() on mixed colmns

My tables structure is as follows:
units {id, owner_id}
contracts {id, rent, unit_id}
rents {paid, contract_id}
What i need, is the sum of two things: all rents.paid and the sum of contracts.rent.
My query is:
SELECT
Sum(Rent.paid) AS Rent__summed_rents
FROM
rents AS Rent
LEFT JOIN contracts
ON contracts.id = Rent.id
LEFT JOIN units
ON contracts.unit_id = units.id AND units.owner_id = 29
I tried to add contracts.rent * Rent.paid AS Rent__summed_expected_rents, but it didn't work.
edit
Each contract has a column rent, which contains the payment expected for each month.
Each rent record has a column paid which contains the actual payment made.
The query should get all the paid records and sum them (summed_rents), it also should tell me what was the expected income (which is contracts.rent * rents.paid)
* edit #2 *
rents data:
id | paid | contract_id
1 | 200 | 6
1 | 300 | 6
1 | 500 | 4
1 | 200 | 4
contracts data:
id | rent | unit_id
6 | 500 | 22
4 | 600 | 22
units data:
id | owner_id
22 | 29
What i expect is:
summed rents = 1,200 (sum of all paid)
expected rents = 2,200 = (500 * 2) + (600 *2) (sum of contract.rent, one for each rents record)
Here's how I'd build the query:
start with units for an owner. simple enough:
SELECT u.*
FROM units u
WHERE u.owner_id = 29
next step, get the contracts on those units:
SELECT c.*
, u.*
FROM units u
JOIN contracts c
ON c.unit_id = u.id
WHERE u.owner_id = 29
next step, get the rents paid on those contracts
SELECT r.*
, c.*
, u.*
FROM units u
JOIN contracts c
ON c.unit_id = u.id
JOIN rents r
ON r.contract_id = c.id
WHERE u.owner_id = 29
-- aggregate the rows from that result by contracts id (per the specification, we need the count from rents by contract number, to multiply by the contracts rent amount)
SELECT SUM(r.paid) AS total_paid
, SUM(c.rent) AS total_rent
FROM units u
JOIN contracts c
ON c.unit_id = u.id
JOIN rents r
ON r.contract_id = c.id
WHERE u.owner_id = 29
GROUP BY c.id
-- get grand totals by wrapping that result as an inline view, and aggregating the total_paid and total rent
SELECT SUM(t.total_paid) AS total_paid
, SUM(t.total_rent) AS total_rent
FROM (
SELECT SUM(r.paid) AS total_paid
, SUM(c.rent) AS total_rent
FROM units u
JOIN contracts c
ON c.unit_id = u.id
JOIN rents r
ON r.contract_id = c.id
WHERE u.owner_id = 29
GROUP BY c.id
) t
NOTE: counting the number of rows in rents for a given contract seems an odd way to get the multiplier for rent. But if that's the specification, we make sure our code does that.
We'd only need outer joins to generate "zero" paid and "zero" rent. If we introduce a LEFT JOIN to the rents table, then we'd need to adjust the expression to get total_rent. We'd want to use something like:
c.rent*COUNT(r.contract_id) AS total_rent
Something like this:
SELECT IFNULL(SUM(t.total_paid),0) AS total_paid
, IFNULL(SUM(t.total_rent),0) AS total_rent
FROM (
SELECT SUM(r.paid) AS total_paid
, c.rent*COUNT(r.contract_id) AS total_rent
FROM units u
JOIN contracts c
ON c.unit_id = u.id
LEFT
JOIN rents r
ON r.contract_id = c.id
WHERE u.owner_id = 29
GROUP BY c.id
) t
It's as easy as:
select sum(r.paid) as rent_paid,
sum(c.rent) as expected_rent
from rents r
join contracts c on (r.contract_id = c.id)
join units u on (c.unit_id = u.id)
where u.owner_id = 29;
As seen on sqlfiddle

Select only the latest version of the data with SQL join

I have two tables one containg offer information and the other containg products
something like this:
OFFER PRODUCTS
ID Number Version poID offer_id Product how_many
========================== ========================================
1 123 1 1 1 Apple 1
2 123 2 2 1 Banana 2
3 124 1 3 1 Orange 1
4 2 Apple 1
5 2 Banana 2
6 2 Orange 2
7 2 Kiwi 1
8 3 Apple 2
9 3 Banana 3
I would like a list of how many products that are currently offered.
Since OFFER(id = 2) is an update of (id = 1) only (id = 2) should be counted.
How should I best query this?
First you need to get all the latests offers:
select o.id
from offer o
where version = (select max(version)
from offer o2
where o2.number = o.number);
Based on the above you can then get all the products:
select p.*
from products p
where offer_id in (select o.id
from offer o
where version = (select max(version)
from offer o2
where o2.number = o.number));
If id and version correlate:
select sum(how_many) from products p
join offer on p.offer_id=offer.id
join (
select number, max(version) version from offer group by number
) x
on offer.id=x.id and offer.version = x.version
SELECT *
FROM products
WHERE offer_id = (SELECT MAX(id) FROM offer)
or, if you prefer the join syntax
SELECT p.*
FROM products p
INNER JOIN (SELECT MAX(id) id FROM offer) o ON p.offer_id = o.id
Edit (still not completely sure this is what you want without seeing your desired results)
SELECT p.*
FROM products p
INNER JOIN offer o on p.offer_id = o.id
INNER JOIN
(SELECT number, max(version)
FROM offer
GROUP BY number
) oMax ON o.number = oMax.number AND o.version = oMax.version
Try this:
select [list columns here]
from products p
join (select offernumber, max(id) as ID from offer group by offernumber) a
on a.id = p.offer_id
If you need addtional columns from offer other than the offernumber and the id:
select [list columns here]
from products p
join (select offernumber, max(id) as ID from offer group by offernumber) a
on a.id = p.offer_id
join offer o on o.id = a.id