how to use full outer join by removing inner join - mysql

I am new to MySQL I have one query which works perfectly fine with inner join but with inner join some records got missing I want all the data from both the table but when i use full outer join or full join it gives error unknown column classroom.id in field list
here is the query
SELECT
classroom.id as id,
classroom.grade as grade,
classroom.status as status,
teacher.id as tid,
teacher.name as tname
FROM classroom
FULL JOIN teacher on classroom.teacherId = teacher.id
ORDER BY grade ASC
these are my two tables you can see in the picture enter image description here
and also I mention in column
classroom
id,grade,teacherid,status
teacher
id,email,password,name,status,role

MySQL does not support a FULL OUTER JOIN or FULL JOIN, you have to emulate it using UNION with LEFT JOIN and RIGHT JOIN.
Read more about it here: Why does MySQL report a syntax error on FULL OUTER JOIN?
So your syntax should look like this:
SELECT * FROM
(SELECT
a.id as id,
a.grade as grade,
a.status as status,
b.id as tid,
b.name as tname
FROM classroom a
LEFT JOIN teacher b ON a.teacherId = b.id
UNION
SELECT
a.id as id,
a.grade as grade,
a.status as status,
b.id as tid,
b.name as tname
FROM classroom a
RIGHT JOIN teacher b ON a.teacherId = b.id) c
WHERE c.grade != '' AND c.grade IS NOT NULL
ORDER BY c.grade ASC
UPDATE: Per your comments below, I've include a WHERE clause to remove NULL values AND empty '' values. You could also write a WHERE clause in each of the UNION queries above but I find it easier to put it in a subquery and write the WHERE clause once in the outer query. I've also added aliases a, b, c so its easier to read vs. using the table names.
Demo here.

Related

Multiple LEFT JOIN in SQL Running Slow. How do I optimize it?

I am combining three tables - persons, properties, totals - using LEFT JOIN. I find the following query to be really fast but it does not give me all rows from table-1 for which there is no corresponding data in table-2 or table-3. Basically, it gives me only rows where there is data in table-2 and table-3.
SELECT a.*, b.propery_address, c.person_asset_total
FROM persons AS a
LEFT JOIN properties AS b ON a.id = b.person_id
LEFT JOIN totals AS c ON a.id = c.person_id
WHERE a.city = 'New York' AND
c.description = 'Total Immovable'
Whereas the following query gives me the correct result by including all rows from table-1 irrespective of whether there is corresponding data or no data from table-2 and table-3. However, this query is taking a really long processing time.
FROM persons AS a
LEFT JOIN
properties AS b ON a.id = b.person_id
LEFT JOIN
(SELECT person_id, person_asset_total
FROM totals
WHERE description = 'Total Immovable'
) AS c ON a.id = c.person_id
WHERE a.city = 'New York'
Is there a better way to write a query that will give data equivalent to second query but with speed of execution equivalent to the first query?
Don't use a subquery:
SELECT p.*, pr.propery_address, t.person_asset_total
FROM persons p LEFT JOIN
properties pr
ON p.id = pr.person_id LEFT JOIN
totals t
ON a.id = c.person_id AND t.description = 'Total Immovable'
WHERE p.city = 'New York';
Your approach would be fine in almost any other database. However, MySQL materializes "derived tables", which makes them much harder to optimize. The above has the same effect.
You will also notice that I changed the table aliases to be abbreviations for the table names. This makes the query much easier to follow.

Get multiple value in single row using join

I want to get multiple values in a single row which matches with primary table. Below are the example tables:
members:
- id
- name
- status
address:
- id
- ref_id(member id)
- address1
- state
contacts:
- id
- ref_id(member id)
- phone
- email
mem_cc
- id
- ref_id(member id)
- category_id
- coverage_id
I'm using below query to create view to get all the records in single view so I can query that view to display a list page:
SELECT a.id, a.name, a.status, b.address1, b.state, c.phone, d.category_id, d.coverage_id
FROM members a LEFT JOIN address b
ON a.id = b.ref_id
LEFT JOIN contacts c
ON a.id = c.ref_id
LEFT JOIN mem_cc d
ON a.id = d.ref_id
Now case like Member A is subscribed with 3 coverages or 3 categories then it'll show me Member A's record three times, I want to get Member A record in table single time with covering all categories and coverages in that single row. Question is how to do that?
I believe you need function "group_concat" when selecting the category:
select a.id,a.name,a.status,b.address1,b.state,c.phone,
group_concat(d.category_id, d.coverage_id)
from members a left join address b on a.id = b.ref_id
left join contacts c on a.id = c.ref_id and left join mem_cc d on a.id = d.ref_id
group by a.id
As DMorillo already said you will have to use grouping. In this way you will get one record for the user and in the different columns you can then group the results as necessary.
If you were thinking of extra columns popping up based on your joins then I don't think this is possible. See if the query below works for your case.
SELECT a.id,
a.name,
a.status,
-- This group_concat will produce something like "5th street - Alabama"
-- separated with newlines
-- Check for NULL values since you are using left joins
GROUP_CONCAT(IFNULL(CONCAT(b.address1, ' - ', b.state), ''))
DELIMITER '\n') AS address,
-- Same goes for phone numbers. Default delimiter is comma.
GROUP_CONCAT(IFNULL(c.phone, '') DELIMITER ','),
-- Now you can group your categories.
GROUP_CONCAT(IFNULL(CONCAT(d.category_id,' ', JOINEDCATEGORYNAME), '') AS category,
GROUP_CONCAT(IFNULL(CONCAT(d.coverage_id,' ', JOINEDCOVERAGENAME), '') AS coverage
FROM members a
LEFT JOIN address b ON a.id = b.ref_id
LEFT JOIN contacts c ON a.id = c.ref_id
LEFT JOIN mem_cc d ON a.id = d.ref_id
-- Here probably your inner joins to categories table and coverage table
GROUP BY a.id

My-Sql JOIN two tables error

I tried to combine two tables' data.
I got an error like this. can you see why?
Every derived table must have its own alias
SELECT a.title, number
FROM store a
JOIN
( SELECT count(b.code) as number
FROM redeem_codes b
WHERE product = a.title
AND available = "Available")
It's a little hard tell without knowing more about your table structures. I'll give a try anyway:
SELECT a.title, count(b.code) AS number FROM store a
LEFT JOIN redeem_codes b ON b.product = a.title
WHERE b.available = "Available"
GROUP BY a.title;
you need to have ALIAS on your subquery.
SELECT a.title, number 
FROM store a  
JOIN (subquery) b -- b is the `ALIAS`
-- and this query will not give you the result you want
but here's a more efficient query without using subquery,
SELECT a.title, count(b.code) number
FROM store a
INNER JOIN redeem_codes b -- or use LEFT JOIN to show 0
-- for those who have no product
ON b.product = a.title
WHERE b.available = 'Available'
GROUP BY a.title

Problem using MySQL Join

i have a MySQL SELECT query which fetches data from 6 tables using Mysql JOIN. here is the MySQL query i am using.
SELECT
u.id,u.password,
u.registerDate,
u.lastVisitDate,
u.lastVisitIp,
u.activationString,
u.active,
u.block,
u.gender,
u.contact_id,
c.name,
c.email,
c.pPhone,
c.sPhone,
c.area_id,
a.name as areaName,
a.city_id,
ct.name as cityName,
ct.state_id,
s.name as stateName,
s.country_id,
cn.name as countryName
FROM users u
LEFT JOIN contacts c ON (u.contact_id = c.id)
LEFT JOIN areas a ON (c.area_id = a.id)
LEFT JOIN cities ct ON (a.city_id = ct.id)
LEFT JOIN states s ON (ct.state_id = s.id)
LEFT JOIN countries cn ON (s.country_id = c.id)
although query works perfectly fine it sometimes returns duplicate results if it finds any duplicate values when using LEFT JOIN. for example in contacts table there exist two rows with area id '2' which results in returning another duplicated row. how do i make a query to select only the required result without any duplicate row. is there any different type of MySQL Join i should be using?
thank you
UPDATE :
here is the contacts table, the column area_id may have several duplicate values.
ANSWER :
there was an error in my condition in last LEFT JOIN where i have used (s.country_id = c.id) instead it should be (s.country_id = cn.id) after splitting the query and testing individually i got to track the error. thank you for your response. it works perfectly fine now.
Duplicating the rows like you mentioned seems to indicate a data problem.
If users is your most granular table this shouldn't happen.
I'd guess, then, that it's possible for a single user to have multiple entries in contacts
You could use DISTINCT as mentioned by #dxprog but I think that GROUP BY is more appropriate here. GROUP BY whichever datapoint could potentially be duplicated....
After all, if a user has corresponding contact records, which one are you intending to JOIN to?
You must specify this if you want to remove "duplicates" because, as far as the RDBMS is concerned, the two rows matching
LEFT JOIN contacts c ON (u.contact_id = c.id)
Are, in fact, distinct already
I think a DISTINCT may be what you're looking for:
SELECT DISTINCT
u.id,u.password,
u.registerDate,
u.lastVisitDate,
u.lastVisitIp,
u.activationString,
u.active,
u.block,
u.gender,
u.contact_id,
c.name,
c.email,
c.pPhone,
c.sPhone,
c.area_id,
a.name as areaName,
a.city_id,
ct.name as cityName,
ct.state_id,
s.name as stateName,
s.country_id,
cn.name as countryName
FROM users u
LEFT JOIN contacts c ON (u.contact_id = c.id)
LEFT JOIN areas a ON (c.area_id = a.id)
LEFT JOIN cities ct ON (a.city_id = ct.id)
LEFT JOIN states s ON (ct.state_id = s.id)
LEFT JOIN countries cn ON (s.country_id = c.id)
This should only return rows where the user ID is distinct, though you may not get all the joined data you'd hoped for.

MySQL query with LEFT OUTER JOIN and WHERE

I have three tables: stories, story_types, and comments
The following query retrieves all of the records in the stories table, gets their story_types, and the number of comments associated with each story:
SELECT s.id AS id,
s.story_date AS datetime,
s.story_content AS content,
t.story_type_label AS type_label,
t.story_type_slug AS type_slug,
COUNT(c.id) AS comment_count
FROM stories AS s
LEFT OUTER JOIN story_types AS t ON s.story_type_id = t.id
LEFT OUTER JOIN comments AS c ON s.id = c.story_id
GROUP BY s.id;
Now what I want to do is only retrieve a record from stories WHERE s.id = 1 (that's the primary key). I have tried the following, but it still returns all of the records:
SELECT s.id AS id,
s.story_date AS datetime,
s.story_content AS content,
t.story_type_label AS type_label,
t.story_type_slug AS type_slug,
COUNT(c.id) AS comment_count
FROM stories AS s
LEFT OUTER JOIN story_types AS t ON s.story_type_id = t.id
AND s.id = 1
LEFT OUTER JOIN comments AS c ON s.id = c.story_id
GROUP BY s.id;
I have also tried a WHERE clause at the end, which throws an error.
Can someone point out the correct syntax for a condition like this in this situation?
I'm using MySQL 5.1.47. Thanks.
I'm guessing you put the WHERE after the GROUP BY, which is illegal. See this reference on the SELECT syntax in MySQL.
Try this:
SELECT
s.id AS id,
s.story_date AS datetime,
s.story_content AS content,
t.story_type_label AS type_label,
t.story_type_slug AS type_slug,
COUNT(c.id) AS comment_count
FROM
stories AS s
LEFT JOIN story_types AS t ON s.story_type_id = t.id
LEFT JOIN comments AS c ON s.id = c.story_id
WHERE
s.id = 1
GROUP BY
s.id;
editor's note: I reformatted the code to highlight the query structure
Following up this comment on the accepted answer:
It is not intuitive to me that this WHERE would go in the second JOIN
This is just to outline how proper code formatting enhances understanding. Here is how I usually format SQL:
SELECT
s.id AS id,
s.story_date AS datetime,
s.story_content AS content,
t.story_type_label AS type_label,
t.story_type_slug AS type_slug,
COUNT(c.id) AS comment_count
FROM
stories AS s
LEFT JOIN story_types AS t ON t.id = s.story_type_id
LEFT OUTER JOIN comments AS c ON s.id = c.story_id
WHERE
s.id = 1
GROUP BY
s.id;
The WHERE is not on the second join. There is only one WHERE clause allowed in a SELECT statement, and it always is top level.
PS: Also note that in many database engines (apart from MySQL) it is illegal to use a GROUP BY clause and then selecting columns without aggregating them via functions like MIN(), MAX(), or COUNT(). IMHO this is bad style and a bad habit to get into.