Display Null Elements - php

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;

Related

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 ?

MySQL join query for more than 1 id

So i encountered this problem when I was specifying a query for 2 connected tables with more than 1 id.
Suppose there are 2 tables:
Table Competition
p1_id(varchar)(FK)
p2_id(varchar)(FK)
p3_id(varchar)(FK)
table Competition refers to table Player by the player.id
Table Player
id (PK)
name
level
gender
The problem is I want to retrieve the p1 name, p2 name and p3 name from table Competition...
join the table player thrice to get it's equivalent values,
SELECT a.*,
b.name Player1_Name,
c.name Player2_Name,
d.name Player3_Name
FROM Competition a
INNER JOIN player b
a.p1_ID = b.ID
INNER JOIN player c
a.p2_ID = c.ID
INNER JOIN player d
a.p3_ID = d.ID
If one of the columns in table competition is nullable, better use LEFT JOIN than INNER JOIN.
To fully gain knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins

Wrapping my head around what I assume is a complicated MySQL query

I have a table called categories and a table called business_categories_coupling. In Categories, you have the usual id, name, parent. In the Coupling table, you have business_id and category_id. Each business can have multiple categories, so I store them in that table. It kinda looks like this:
business_id category_id
73 80
73 81
73 90
74 4
74 10
Right now, my query is just selecting all the categories, doing a foreach and doing a db query in each loop to find how many businesses are in that category. Obviously not the right way to go about it.
Is there a way to do a SQL query that basically selects all the categories, gets the number of times it comes up in the coupling table, and add a count to each category?
SELECT
C.*
FROM
CATEGORIES AS C
LEFT JOIN
BUSINESS_CATEGORIES_COUPLING AS B
ON
C.id = B.category_id;
Kinda like that, but with a count somewhere. I've tried various setups but nothing works like I want. Any suggestions?
EDIT 1
Solution as provided by #phani-rahul, but I added a WHERE clause:
SELECT cat.id AS id, cat.name AS name, cat.slug AS slug, COUNT(cat.id) AS business_count
FROM categories AS cat
LEFT JOIN business_categories_coupling AS coupling ON cat.id=coupling.category_id
WHERE coupling.category_id IS NOT NULL
GROUP BY cat.id
Yes, there is.
you can use Group by clause:
select a.id as category, count(a.id) as count_of_category
from categories a
left join business_categories_coupling b on a.id=b.category_id
group by a.id
your result would be something like:
category count_of_category
80 2
81 5
90 1
. .
. .
. .
You also need to GROUP by the fields of C table.
SELECT C.id, C.field1, C.field2, COUNT(*)
FROM CATEGORIES AS C
LEFT JOIN BUSINESS_CATEGORIES_COUPLING AS B
ON (C.id = B.category_id)
GROUP BY C.id, C.field1, ...
(In MySQL you can GROUP BY the single value C.id; in other SQL dialects you can express the concept of "grouping by rows of C table" by grouping by "C.*"; in some others you need to specify all non-aggregate columns of your query, in this case all columns you select from C, one by one).
What you're looking for is a GROUP BY clause.
SELECT
C.*, count(C.id)
FROM
CATEGORIES AS C
LEFT JOIN
BUSINESS_CATEGORIES_COUPLING AS B
ON
C.id = B.category_id
GROUP BY B.category_id;

need a help with joining multiple tables from SQL in 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

MySQL: 4 Table "has-many-through" Join?

Let's say I have the following 4 tables (for examples' sake): Owners, Trucks, Boxes, Apples.
An owner can have many trucks, a truck can have many boxes and a box can have many apples.
Owners have an id. Trucks have an id and owner_id. Boxes have an id and truck_id. Apples have an id and box_id.
Let's say I want to get all the apples "owned" by an owner with id = 34. So I want to get all the apples that are in boxes that are in trucks that owner 34 owns.
There is a "hierarchy" if you will of 4 tables that each only has reference to its direct "parent". How can I quickly filter boxes while satisfying conditions across the other 3 tables?
I hope that made sense somewhat.
Thanks.
select a.*
from Trucks t
inner join Boxes b on t.id = b.truck_id
inner join Apples a on b.id = a.box_id
where t.owner_id = 34
You just start at the "top" (owners) and keep joining until you get where you want:
SELECT a.*
FROM Owners o
INNER JOIN Trucks t ON t.owner_id = o.id
INNER JOIN Boxes b on b.truck_id = t.id
INNER JOIN Apples a on a.box_id = b.id
WHERE o.id = ?
If queries like that are needed often and you are working with very large data sets, sometimes it makes sense to denormalize the data a bit as well. For example by adding the owner_id to the apples table. It makes inserting/updating the data a bit more difficult, but can make queries easier.
SELECT a.*
FROM Apples a
INNER JOIN Boxes b ON b.id = a.box_id
INNER JOIN Trucks t ON t.id = b.truck_id
INNER JOIN Owners o ON o.id = t.owner_id
WHERE o.id = 34
You can simplify this somewhat by leaving out the join to owners and just selecting where t.owner_id = 34 if you don't need any information about the owner later.

Categories