need a help with joining multiple tables from SQL in PHP - php

Let's say I have 3 tables:
Table 1 called "states":
id | state
1 italy
2 netherlands
3 russia
Table 2 called "hotels":
id | hotel name | belongsToCountry
1 Green Hotel 2
2 Luxurious 2
3 Get Warm! 3
Table 3 called "free rooms":
id | roomID | belongsToHotel
1 815 2
2 912 2
3 145 1
4 512 1
5 1200 3
Now, what I need to echo is this:
Netherlands has 4 free rooms.
Russia has 1 free room.
In words:
I need to make a list of all states which have at least 1 free room and I need to return the exact value of how many free rooms there are.
If anyone can help me with this, I'd be so grateful!

Let's build the query step by step.
First, let's assemble the list of hotels and their free room count.
SELECT hotels.id, COUNT(*)
FROM hotels
INNER JOIN free_rooms ON(hotels.id = free_rooms.belongsToHotel)
GROUP BY hotels.id
INNER JOINs force rows from the table on the "left" side of the join (hotels) only to be included in the result set when there is a corresponding table on the "right" (free_rooms). I'm assuming here that there will only be a row in free_rooms when the room is free.
Having this, we can now join against the list-o-nations.
SELECT hotels.id, COUNT(*), states.state
FROM hotels
INNER JOIN free_rooms ON(hotels.id = free_rooms.belongsToHotel)
INNER JOIN states ON(hotels.belongsToCountry = states.id)
GROUP BY hotels.id
It should be noted, by the way, that you've made poor choices in naming these columns. states should be composed of id and state_name, hotels should be id, hotel_name, state_id, and free_rooms should be id, room_name and hotel_id. (I could also argue that states.id should be states.state_id, hotels.id should be hotels.hotel_id and free_rooms.id should be free_rooms.room_id because that makes the joins much easier...)
If you need to represent a "belongs to" relationship, you're actually looking for foreign key restraints. You should use those instead of special naming.
*ahem* Where was I? Oh yes. The second query will result in a result set with three columns - the hotel id, the number of rooms in it, and the country it's in. But, you just need the number of rooms per country, so let's do one last change.
SELECT COUNT(*), states.state
FROM hotels
INNER JOIN free_rooms ON(hotels.id = free_rooms.belongsToHotel)
INNER JOIN states ON(hotels.belongsToCountry = states.id)
GROUP BY states.state
Only two changes. First, we're now grouping together by state. Second, we're no longer including the hotel id in the result set. This should get you the data you need, again assuming that there will never be a row in free_rooms when the room is not free.

raw query - not tested:
SELECT state, COUNT( roomID ) AS rooms
FROM states
INNER JOIN hotels ON belongsToCountry = state.id
INNER JOIN free_rommms ON belongsToHotel = hotels.id
GROUP BY state

SELECT state, COUNT(*) AS nb
FROM states AS S, hotels AS H, rooms AS R
WHERE S.id = H.belongsToCountry
AND H.id = R.belongsToHotel
GROUP BY state
HAVING nb >= 1

Related

Group by with count not working Mysql Php

I have two tables "employer" and "user", and i want to get total user(according to country) Where "skills" and "entity" column
matches (like query) with both table
Table employer
id skills entity
1 Php,Design web
Table user
id skills entity country
33 programming,php web india
44 Graphic designing,php design canada
45 Php web india
46 Dot net test Sri Lanka
Now i want to get result like
id Count country
1 2 india
2 1 canada
i am trying with following query but not working for me, where i am wrong ?
SELECT employer_info.id as employer_id,users_info.country,COUNT(users_info.country) as total
FROM employer_info
JOIN users_info ON employer_info.skills=users_info.skills
GROUP BY users_info.country
If you want to count the number of users in a country by employer, you should count the number of user ids and not the country.
As for the GROUP BY, this should be done on the non-aggregated fields, which are the employer_info.id and users_info.country
SELECT employer_info.id as employer_id,users_info.country,
COUNT(users_info.id) as total
FROM employer_info
JOIN users_info
ON employer_info.skills=users_info.skills
GROUP BY employer_info.id, users_info.country
Try this one:
SELECT A.id as employer_id,B.country,COUNT(B.country) as total
FROM employer_info A
JOIN users_info B ON A.skills=B.skills AND A.entity=B.entity
GROUP BY A.id, B.country

GROUP_CONCAT with ordering and missing fields

I have a series of tables that I want to get rows returned from in the following format:
Student ID | Last Name | First Name | Quiz Scores
-------------------------------------------------
xxxxxxx | Snow | Jon | 0,0,0,0,0,0,0,0
There's 3 relevant tables (changing any existing DB structure is not an option):
person - table of all people in the organization
enrollment - table of student and faculty enrollment data
tilt.quiz - table of quiz scores, with each row storing an individual score
The tricky part of this is the Quiz Scores. A row for the quiz score only exists if the student has taken a the quiz. Each quiz row has a module, 1 - 8. So possible quiz data for a student could be (each of these being a separate row):
person_id | module | score
---------------------------
223355 | 1 | 100
223355 | 2 | 95
223355 | 4 | 80
223355 | 7 | 100
I need the quiz scores returned in proper order with 8 comma separated values, regardless if any or all of the quizzes are missing.
I currently have the following query:
SELECT
person.id,
first_name,
last_name,
GROUP_CONCAT(tilt.quiz.score) AS scores
FROM person
LEFT JOIN enrollment ON person.id = enrollment.person_id
LEFT JOIN tilt.quiz ON person.id = tilt.quiz.person_id
WHERE
enrollment.course_id = '$num' AND enrollment_status_id = 1
GROUP BY person.id
ORDER BY last_name
The problems with this are:
It does not order the quizzes by module
If any of the quizzes are missing it simply returns fewer values
So I need the GROUP_CONCAT scores to at least include commas for missing quiz values, and have them ordered correctly.
The one solution I considered was creating a temporary table of the quiz scores, but I'm not sure this is the most efficient method or exactly how to go about it.
EDIT: Another solution would be to execute a query to check for the existence of each quiz individually but this seems clunky (a total of 9 queries instead of 1); I was hoping there was a more elegant way.
How would this be accomplished?
There are some assumptions here about your data structure, but this should be pretty close to what you're after. Take a look at the documentation for GROUP_CONCAT and COALESCE.
SELECT `person`.`id`, `person`.`first_name`, `person`.`last_name`,
GROUP_CONCAT(
COALESCE(`tilt`.`quiz`.`score`, 'N/A')
ORDER BY `tilt`.`quiz`.`module_id`
) AS `scores`
FROM `person`
CROSS JOIN `modules`
LEFT JOIN `enrollment` USING (`person_id`)
LEFT JOIN `tilt`.`quiz` USING (`person_id`, `module_id`)
WHERE (`enrollment`.`course_id` = '$num')
AND (`enrollment`.`enrollment_status_id` = 1)
GROUP BY `person`.`id`
ORDER BY `person`.`last_name`
First thing to do is use the IFNULL() function on the score
Then, use ORDER BY inside the GROUP_CONCAT
Here is my proposed query
SELECT
person.id,
first_name,
last_name,
GROUP_CONCAT(IFNULL(tilt.quiz.score,0) ORDER BY tilt.quiz.module) AS scores
FROM person
LEFT JOIN enrollment ON person.id = enrollment.person_id
LEFT JOIN tilt.quiz ON person.id = tilt.quiz.person_id
WHERE
enrollment.course_id = '$num' AND enrollment_status_id = 1
GROUP BY person.id
ORDER BY last_name

MySQL - structuring query to discard common results

That title is really not useful, but its a complex question (in my head, maybe) ... anywho...
Say I have a MySQL table of Countries (A-Z all countries in the world) with id & name
Then I have a table where I am tracking which countries a user has been to: Like so:
Country Table
id name
1 india
2 luxembourg
3 usa
Visited Table
id user_id country_id
1 1 1
2 1 3
Now here's what I want to do, when I present the form to add to the list of visited countries I want country.id 1 & 3 to be excluded from the query result.
I know I can filter this using PHP ... which is something I have done in the past ... but surely there must be a way to structure a query in such a way that 1 & 3 are excluded from the returned results, like:
SELECT *
FROM `countries`
WHERE `id`!= "SELECT `country_id`
FROM `visited`
WHERE `user_id`='1'"
I suspect it has something to do with JOIN statements but I can't quite figure it out.
Bonus gratitude if someone can point me in the right direction with Laravel.
Thanks you all :)
Is this what you want?
select c.*
from countries c left join
visited v
on c.id = v.country_id and v.user_id = 1
where v.country_id is null;
You can also express this as a not in or not exists, but the left join method typically has pretty good performance.
The left outer join keeps all records in the first table regardless of whether or not the on clause evaluates to true. If there are no matches in the second table, then the columns are populated with NULL values. The where clause simply chooses these records -- the ones that do not match.
Here is another way of expressing this that you might find easier to follow:
select c.*
from countries c
where not exists (select 1 from visited where c.id = v.country_id and v.user_id = 1)
You can use your query like this.
SELECT *
FROM `countries` c LEFT JOIN `visited` v on c.id = v.country_id
WHERE v.`country_id` is null
AND v.`user_id` = 1
This is a operation of a LEFT JOIN. What is means is that I'm selecting all registries from the table countries that may or may not is on the table visited based on the ID of the country.
So it will bring you this group
from country from visited
1 1
2 no registry
3 3
So on the where condition (v.country_id is null) I'm saying: I only want the ones that on this left join operation is only on the country table but it is not on visited table so it brings me the id 2. Plus the condition that says that those registries on visited must be from the user_id=1
SELECT * FROM COUNTRIES LEFT JOIN
VISITED ON
countries.id = visited.country_id and visited.country_id NOT IN ( SELECT country_id FROM visited )
if i understand right maybe you need something like this ?

Display Null Elements

I have a table for students (containing a foreign key referencing school) and a table for schools.
I would like display list the Schools in London and total students in each school.
SQL Code is:
SELECT
sc.id_school,
sc.name,
count(*) as count
FROM
students as st
INNER JOIN
schools as sc
ON
sc.id_school=st.id_school
WHERE
sc.city='London'
GROUP BY
sc.name
The result is:
id_school name count
2 Gateway 4
3 St Peters 3
The result however does not display Schools in London that do not have listed students.
I would want the result to show:
id_school name count
2 Gateway 4
7 Manchels 0
1 Rowers 0
3 St Peters 3
4 St Johns 0
Please assist.
You need to use RIGHT join for schools table, so even if there are no students in london schools they will be returned
SELECT
sc.id_school,
sc.name,
count(*) as count
FROM
students as st
RIGHT JOIN
schools as sc
ON
sc.id_school=st.id_school
WHERE
sc.city='London'
GROUP BY
sc.name
You can use LEFT JOIN to display all the rows 'on the left' even if they have no entry in the column 'count'. Same goes for RIGHT JOIN. OUTER JOIN displays all combinations.
To display '0' instead of NULL:
IFNULL(count, 0)
While #MKhalidJunaid provided the correct answer (I already upvoted), I thought I'd offer another answer that more accurately reflects the data but also should be more performant.
SELECT sc.id_school AS School_ID
,sc.name AS School_Name
,COUNT(st.name) AS Student_Count
FROM schools AS sc
LEFT OUTER JOIN students AS st ON sc.id_school = st.id_school
WHERE sc.city = 'London'
GROUP BY sc.id_school,sc.name
By making everything ANSI-compliant (LEFT OUTER JOIN rather than just LEFT JOIN, complete GROUP BY statement), as well as making schools the driver of the query rather than students, you should get the same result set but with some performance gains.
Try to Group by the schoolID
SELECT
sc.id_school,
sc.name,
count(*) as count
FROM
students as st
INNER JOIN
schools as sc
ON
sc.id_school=st.id_school
WHERE
sc.city='London'
GROUP BY
sc.name, sc.id_school;

Using left join to get total rows in other table

Hi I all I have two tables, yalladb_hotel and yalladb_room_types and their structure are
yalladb_hotel
-----------------------------------------
| id | name | address | fax | telephone |
---------------------------------------------
And yalladb_room_types
-----------------------------
|id | hotel_id | roomtype_name | rate |
Now I want to get all information from hotel table and want to get total number of room types related to hotel table. I used left join as it is not necessary that all hotels have room types. So I used following query
SELECT
h.*,
count(rm.*) as total_room_types
FROM
yalladb_hotel h
LEFT JOIN yalladb_room_types rm
ON h.id=rm.hotel_id
LIMIT
0,5
But it is producing following error and I am totally unable to understand the error is....
#1064 - You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version
for the right syntax to use near '*) as total_room_types FROM
yalladb_hotel h LEFT JOIN yalladb_room_types rm ON h' at line 1
Can any one tell what is there?
Regards
If you are using agregate function you should put every columns(except agregate column) to GROUP BY section
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
Explanation:
Select (these columns should be in group by section), count(agr_col)
from table
group by (here should be those columns also)
Just do
COUNT(rm.id) as Total_Room_Types
Unless you have a specific classification to differentiate between, Double, Queen, King size bed rooms.
If your Hotel room "Name" is the classification of room type as described above, you should pre-query the room types first and join to that.
SELECT
h.*,
COALESCE( PreQuery.Name, " " ) as RoomType,
COALESCE( PreQuery.RoomTypeCount, 0 ) as RoomTypeCount
FROM
yalladb_hotel h
LEFT JOIN ( select rm.hotel_id,
rm.name,
count(*) as RoomTypeCount
from
yalladb_room_types rm
group by
rm.hotel_id,
rm.name ) PreQuery
ON h.id=PreQuery.hotel_id
LIMIT
0,5
EDIT CLARIFICATIONS...
To clarify my answer. Instead of just a count of how many rooms, you wanted them per room type. Per your original listed structure, you had "Name" as a column which is now listed as roomType_Name per your edits. I suspected this column to describe the type of room. So my inner query (as opposed to an inner join) tells the query to pre-aggregate this stuff first, grouping by the criteria and let its results be known as an alias of "PreQuery" for the join condition. THEN, back to the main hotel table LEFT joined to "PreQuery" on the hotel ID.
Since a left join will otherwise result in NULL values if no such matches are found in whatever the "OTHER" table is, COALESCE() says... Get the value from parameter 1. If that is null, get the second value... and put that into a final query column called ... RoomType or RoomTypeCount as in this example. So your final query will not have any "NULL" as part of the result, but at least of proper data type expected (char and numeric respectively).

Categories