Populate/Join multiple tables of many to many relationship table in mysql - mysql

This question should have been asked before, but I am finding it hard to find.
I have table users,allergies,dietaries. users can have multiple allergies,and multiple dietaries. So for that I have user_allergies, user_dietaries table which store both tables foreign key.
Now I want to show list of users with their corresponding allergies, dietaries name. I tried with left join like this -->
SELECT
users.user_id as 'id',
IFNULL(CONCAT(users.first_name,' ',users.last_name),'') as 'name',
IFNULL(GROUP_CONCAT(allergies.title),'') as 'allergies'
FROM (users
LEFT JOIN user_allergies
ON users.user_id = user_allergies.user_id
LEFT JOIN allergies
ON user_allergies.allergies_id = allergies.allergies_id
)
GROUP BY users.user_id;
Which respond exactly like I want-->
7 | Khabib Nurmagamedov | Milk,Corn
8 | Conor Mcgregor | Milk,Corn
Now I wish to get the dietaries same way. So I added Left Join for dietaries similarly -->
SELECT
users.user_id as 'id',
IFNULL(CONCAT(users.first_name,' ',users.last_name),'') as 'name',
IFNULL(GROUP_CONCAT(allergies.title),'') as 'allergies',
IFNULL(GROUP_CONCAT(dietaries.title),'') as 'dietaries'
FROM (users
LEFT JOIN user_allergies
ON users.user_id = user_allergies.user_id
LEFT JOIN allergies
ON user_allergies.allergies_id = allergies.allergies_id
LEFT JOIN user_dietaries
ON users.user_id = user_dietaries.user_id
LEFT JOIN dietaries
ON user_allergies.allergies_id = dietaries.dietaries_id
)
GROUP BY users.user_id;
But now the output I get -->
7 | Khabib Nurmagamedov | Milk,Corn,Eggs,Meat | Milk,Corn,Eggs,Meat
8 | Conor Mcgregor | Milk,Corn,Eggs,Meat | Milk,Corn,Eggs,Meat
Which should be -->
7 | Khabib Nurmagamedov | Milk,Corn | Eggs,Meat
8 | Conor Mcgregor | Milk,Corn | Eggs,Meat
I am struggling here. What should I do?
Thanks In Advance.
Edit:
Te response is -->
7 | Khabib Nurmagamedov | Milk,Corn,Milk,Corn | Milk,Milk,Corn,Corn
8 | Conor Mcgregor | Milk,Eggs,Milk,Eggs | Milk,Milk,Corn,Corn
I thought both table data getting concatenated but apparently result is appearing twice.
Result needs to be-->
7 | Khabib Nurmagamedov | Milk,Corn | Milk,Corn
8 | Conor Mcgregor | Milk,Eggs | Milk,Corn

Your issue is that you want your GROUP_CONCAT in 2 separate columns, and it can't work like that.
You have to join your group_concat results 2 times, from the user table:
SELECT u.user_id as 'id',
IFNULL(CONCAT(u.first_name,' ',u.last_name),'') as 'name',
ua.allergies_c as 'allergies',
ud.dietaries_c as 'dietaries'
FROM users u
LEFT JOIN
(
SELECT
users.user_id ,
IFNULL(GROUP_CONCAT(allergies.title),'') as allergies_c
FROM (users
LEFT JOIN user_allergies
ON users.user_id = user_allergies.user_id
LEFT JOIN allergies
ON user_allergies.allergies_id = allergies.allergies_id
)
GROUP BY users.user_id
) ua ON u.user_id=ua.user_id
LEFT JOIN
(
SELECT
users.user_id,
IFNULL(GROUP_CONCAT(dietaries.title),'') as dietaries_c
FROM (users
LEFT JOIN user_dietaries
ON users.user_id = user_dietaries.user_id
LEFT JOIN dietaries
ON user_dietaries.dietaries_id = dietaries.dietaries_id
)
GROUP BY users.user_id
) ud ON u.user_id=ud.user_id
I made some mass-replace to go faster, you might have a few things to fix, but the idea is there.

By adding Distinct before allergies.title and dietaries.title, I can get my desired result.
But not sure why title fields were appearing twice.
SELECT
users.user_id as 'id',
IFNULL(CONCAT(users.first_name,' ',users.last_name),'') as 'name',
IFNULL(GROUP_CONCAT(Distinct allergies.title),'') as 'allergies',
IFNULL(GROUP_CONCAT(Distinct dietaries.title),'') as 'dietaries'
FROM (users
LEFT JOIN user_allergies
ON users.user_id = user_allergies.user_id
LEFT JOIN allergies
ON user_allergies.allergies_id = allergies.allergies_id
LEFT JOIN user_dietaries
ON users.user_id = user_dietaries.user_id
LEFT JOIN dietaries
ON user_allergies.allergies_id = dietaries.dietaries_id
)
GROUP BY users.user_id;

Related

Sql: To fetch all record along with matching record

Fiddle i created: http://sqlfiddle.com/#!9/e22eb6/2
Data for each tables
I tried below query but its only showing result which particular user has authority/permission assigned on him, i want all records(permission avaialble) in authority_master table along with matched one.
SELECT U.first_name,
UR.authority_id as AUTHORITY_REL_AUTH_ID,
AM.authority
FROM userdetails U
INNER JOIN users_authority_relation UR
ON U.user_id=UR.user_id
LEFT JOIN authority_master AM
ON AM.authority_id=UR.authority_id
WHERE U.user_id=1;
first_name AUTHORITY_REL_AUTH_ID authority
admin 1 ADMIN_USER
admin 2 STANDARD_USER
admin 4 HR_PERMISSION`
Expected output(order does not matter), how to get this output?
first_name AUTHORITY_REL_AUTH_ID authority
admin 1 ADMIN_USER
admin 2 STANDARD_USER
admin null NEW_CANDIDATE
admin 4 HR_PERMISSION
It isn't pretty, but you need the hole thing
Query 1:
SELECT COALESCE(U.first_name,U2.first_name)
,UR.authority_id as AUTHORITY_REL_AUTH_ID,
AM.authority
FROM ( userdetails U
INNER JOIN users_authority_relation UR
ON U.user_id=UR.user_id ANd u.user_id = 1)
RIGHT JOIN (userdetails U2 CROSS JOIN authority_master AM )
ON AM.authority_id=UR.authority_id
WHERE U2.user_id = 1
Results:
| COALESCE(U.first_name,U2.first_name) | AUTHORITY_REL_AUTH_ID | authority |
|--------------------------------------|-----------------------|---------------|
| admin | 1 | ADMIN_USER |
| admin | 2 | STANDARD_USER |
| admin | (null) | NEW_CANDIDATE |
| admin | 4 | HR_PERMISSION |
You could try this query
SELECT U.first_name,
UR.authority_id as AUTHORITY_REL_AUTH_ID,
AM.authority
FROM
authority_master AM
LEFT JOIN userdetails U ON U.user_id = 1
LEFT JOIN users_authority_relation UR
ON AM.authority_id = UR.authority_id AND UR.user_id = u.user_id
WHERE U.user_id = 1;
Can you try this :
SELECT U.first_name, UR.authority_id as AUTHORITY_REL_AUTH_ID, AM.authority
FROM authority_master AM
LEFT JOIN users_authority_relation UR ON AM.authority_id=UR.authority_id AND UR.user_id = 1
LEFT JOIN userdetails U ON U.user_id = 1
Demo here

2 Nested queries from the same table?

So I have 3 tables
User Table
UserId User
Language Table
LanguageId Language Fluency
User Language Table
UserLanguageId UserId LanguageId
Basically what I need is a query where PriLang would be where Fluency='Primary' and SecLang is Fluency='Secondary' and looks somethin like this
+------------+-----------------+-----------+
| User | PriLang | SecLang |
+------------+-----------------+-----------+
| Jimbo | English | Spanish |
+------------+-----------------+-----------+
| Norm | French | Spanish |
+------------+-----------------+-----------+
| Kathy | Japanese | Italian |
+------------+-----------------+-----------+
In my view, the most straightforward approach involves two joins to the user_languages table, as well as a join to the langauges table for each of them.
select usr.User, lg1.Language PriLang, lg2.Language SecLang
from users usr
left join user_languages ul1
on ul1.userId = usr.userId
and ul1.Fluency='Primary'
left join user_languages ul2
on ul2.userId = usr.userId
and ul2.Fluency='Secondary'
left join languages lg1
on lg1.languageId = ul1.languageId
left join languages lg2
on lg2.languageId = ul2.languageId
One option uses conditional aggregation:
select
u.user,
max(case when l.fluency = 'Primary' then l.language end) as pri_lang,
max(case when l.fluency = 'Secondary' then l.language end) as sec_lang
from users u
left join user_languages ul on ul.user_id = u.user_id
left join languages l on l.language_id = ul.language_id
group by u.user_id, u.user
Maybe something like this depending on your setup; this should get you close using a subquery:
SELECT u.User,
(SELECT lt.Language
FROM Language_table lt
JOIN UserLanguage_table ult ON lt.LanguageId = ult.LanguageId
JOIN User_table u ON ult.UserId = u.UserId
WHERE lt.Language_Fluency = 'primary'
AND ult.UserId = u.UserId
AND lt.LanguageId = ult.LanguageId) AS primary_lang,
(SELECT lt.Language
FROM Language_table lt
JOIN UserLanguage_table ult ON lt.LanguageId = ult.LanguageId
JOIN User_table u ON ult.UserId = u.UserId
WHERE lt.Language_Fluency = 'secondary'
AND ult.UserId = u.UserId
AND lt.LanguageId = ult.LanguageId) AS secondary_lang
FROM User_table u;

MySQL multiple inner join between 2 tables on different columns for one of the tables

Table transport
Id | FirstLevSubcat | SecondLevSubcat | ThirdLevSubcat
--------------------------------------------------------
8 | 4 | 27 | 1418
Table categories
Id | CategoriesUrl
--------------------
4 | cars
27 | audi
1418 | audi-100
Query if not to use categories table (without inner join) would be like
SELECT count(*) FROM transport
WHERE FirstLevSubcat = 4 AND SecondLevSubcat = 27 AND ThirdLevSubcat = 1418
Trying to get the same result using INNER JOIN
SELECT count(*) FROM transport main_table
INNER JOIN categories cat_table_first ON cat_table_first.IdRows = main_table.FirstLevSubcat
INNER JOIN categories cat_table_second ON cat_table_second.IdRows = main_table.SecondLevSubcat
INNER JOIN categories cat_table_third ON cat_table_third.IdRows = main_table.ThirdLevSubcat
WHERE
cat_table_first.CategoriesUrl = 'cars'
AND cat_table_second.CategoriesUrl = 'audi'
AND cat_table_third.CategoriesUrl = 'audi-100'
At first sight all works
But is such query ok? May be can improve something?
Your query is correct. You can also do it in following way:
SELECT count(*) FROM transport main_table
INNER JOIN categories cat_table_first ON cat_table_first.IdRows = main_table.FirstLevSubcat and cat_table_first.CategoriesUrl = 'cars'
INNER JOIN categories cat_table_second ON cat_table_second.IdRows = main_table.SecondLevSubcat and cat_table_second.CategoriesUrl = 'audi'
INNER JOIN categories cat_table_third ON cat_table_third.IdRows = main_table.ThirdLevSubcat and cat_table_third.CategoriesUrl = 'audi-100'
You can also do it using 3 EXISTS block.
SELECT count(*) FROM transport main_table
WHERE
EXISTS (SELECT NULL FROM categories WHERE main_table.FirstLevSubcat=categories.IdRows AND categories.CategoriesUrl ='cars')
AND
EXISTS (SELECT NULL FROM categories WHERE main_table.SecondLevSubcat=categories.IdRows AND categories.CategoriesUrl ='audi')
AND
EXISTS (SELECT NULL FROM categories WHERE main_table.ThirdLevSubcat=categories.IdRows AND categories.CategoriesUrl ='audi-100')

Mysql nested select with multiple joins with condition on join table

I've got a SELECT with multiple JOINS for a paginated Tableview. In general this is working for unfiltered results.
The query looks like this:
SELECT seltable.*,
tbl2.name AS tbl2name,
tbl3.name AS tbl3name,
tbl4.name AS tbl4name
FROM
( SELECT * FROM selecttable
WHERE value = 99
ORDER BY datetime DESC
LIMIT 50 OFFSET 0 )
AS seltable
LEFT JOIN table1 AS tbl1 ON seltable.tbl1_uid = tbl1.uid
LEFT JOIN table2 AS tbl2 ON tbl1.tbl2_uid = tbl2.uid
LEFT JOIN table3 AS tbl3 ON tbl2.tbl3_uid = tbl3.uid
LEFT JOIN table4 AS tbl4 ON tbl3.tbl4_uid = tbl4.uid;
Now I've got no clue how to accomplish filtering the results with a condition related to one of the join tables.
When I just set a:
LEFT JOIN tablex AS table ON foreign_table.tblx_uid = table.uid AND {condition}
this condition regards only to the 50 results of the nested SELECT.
Is there any way to achieve using WHERE clauses on the JOIN tables in this scenario?
For sample data see http://sqlfiddle.com/#!2/fad4d/2
Expected results:
to get x team records limited to 5 team uids, where Tournament2 is one of the related tournaments for the team.
Best regards
w1ll1
Try not controlling the pagination in that subquery, instead just use a more conventional query with a composite where clause. HOWEVER, because you are using left joins take care adding filters through the where clause that would override the outer join to produce the effect of an inner join.
SELECT seltable.*,
tbl2.name AS tbl2name,
tbl3.name AS tbl3name,
tbl4.name AS tbl4name
FROM selecttable AS seltable
LEFT JOIN table1 AS tbl1 ON seltable.tbl1_uid = tbl1.uid
LEFT JOIN table2 AS tbl2 ON tbl1.tbl2_uid = tbl2.uid
LEFT JOIN table3 AS tbl3 ON tbl2.tbl3_uid = tbl3.uid
LEFT JOIN table4 AS tbl4 ON tbl3.tbl4_uid = tbl4.uid
WHERE seltable.value = 99
...
ORDER BY seltable.datetime DESC
LIMIT 50 OFFSET 0
Alternatively use more subqueries, like this:
SELECT seltable.*,
tbl2.name AS tbl2name,
tbl3.name AS tbl3name,
tbl4.name AS tbl4name
FROM
( SELECT * FROM selecttable
WHERE value = 99
ORDER BY datetime DESC
LIMIT 50 OFFSET 0 )
AS seltable
LEFT JOIN ( SELECT uid, name
FROM table1
WHERE 1=1 -- amend to suit
) AS tbl1 ON seltable.tbl1_uid = tbl1.uid
LEFT JOIN ( SELECT uid, name
FROM table2
WHERE 1=1 -- amend to suit
) AS tbl2 ON tbl1.tbl2_uid = tbl2.uid
LEFT JOIN ( SELECT uid, name
FROM table3
WHERE 1=1 -- amend to suit
) AS tbl3 ON tbl2.tbl3_uid = tbl3.uid
LEFT JOIN ( SELECT uid, name
FROM table4
WHERE 1=1 -- amend to suit
) AS tbl4 ON tbl3.tbl4_uid = tbl4.uid;
Here is another attempt, based on your sqlfiddle it appears that INNER JOINS may be used:
SELECT theteam.*,
trnmnt.name AS tournamentname,
cat.name AS categoryname,
sport.name AS sportname
FROM (
SELECT * FROM team
ORDER BY team.name ASC )
AS theteam
INNER JOIN tournament_team AS tntm ON tntm.team_uid = theteam.uid
INNER JOIN tournament AS trnmnt ON tntm.tournament_uid = trnmnt.uid AND trnmnt.name = 'Tournament2'
INNER JOIN category AS cat ON trnmnt.category_uid = cat.uid
INNER JOIN sport ON cat.sport_uid = sport.uid
LIMIT 5 OFFSET 0
;
The result of that query is:
| UID | NAME | TOURNAMENTNAME | CATEGORYNAME | SPORTNAME |
|-----|--------|----------------|--------------|-----------|
| 2 | Team02 | Tournament2 | Germany | Soccer |
| 3 | Team03 | Tournament2 | Germany | Soccer |
| 4 | Team04 | Tournament2 | Germany | Soccer |
| 5 | Team05 | Tournament2 | Germany | Soccer |
| 6 | Team06 | Tournament2 | Germany | Soccer |

SQL Query that counts grouped by results not working as intended

My goal is to get a table that counts the correct answers from a game.
For example I want this
| G.Name | E.Action
| game 1 | correctAnswer
| game 1 | correctAnswer
| game 2 | correctAnswer
| game 3 | correctAnswer
| game 3 | correctAnswer
to become this
| G.Name | Count(*)
| game 1 | 2
| game 2 | 1
| game 3 | 2
the problem is that im getting this instead:
| G.Name | Count(*)
| game 1 | 5
| game 2 | 5
| game 3 | 5
where 5 is the sum of 2+1+2
This is my query
SELECT G.Name, Count(*)
FROM enduser EU
INNER JOIN prescription P
ON EU.UserRefID = P.EndUserRefID
INNER JOIN prescriptiongame PG
ON PG.PrescriptionRefID = P.PrescriptionID
INNER JOIN games G
ON G.GameID = PG.GameRefID
INNER JOIN session S
ON S.PrescriptionRefID = P.PrescriptionID
INNER JOIN entries E
ON E.SessionGameRefID = S.Session
WHERE P.EndUserRefID = 889
AND E.Action = 'correctAnswer'
GROUP BY G.Name
ORDER BY G.Name
How can I solve this issue, when I googled people are using the same method but they get good results
Thanks in advance
I'd bet money that one of your joins is inflating the result. Strip your query down to just the critical tables: prescription, session, and entries. Do you still get excessive results? If not, try adding joins back in, one at a time, until you get over-counting.
Your joins are somehow multiplying the number of rows. This happens when you join tables along different dimensions that are not related to each other. This is a common problem, but I don't understand your data structure so the following is a best-guess.
Your example can be fixed by using count(distinct) on something. So try this:
SELECT G.Name, Count(distinct SessionGameRefID)
Should there be a relationship between session and game?
I wonder if this simplification of your query would fix the problem (it removes the first two tables):
SELECT G.Name, Count(*)
FROM prescriptiongame PG
INNER JOIN games G
ON G.GameID = PG.GameRefID
INNER JOIN session S
ON S.PrescriptionRefID = PG.PrescriptionRefID
INNER JOIN entries E
ON E.SessionGameRefID = S.Session
WHERE PG.EndUserRefID = 889
AND E.Action = 'correctAnswer'
GROUP BY G.Name
ORDER BY G.Name;
supposing all the joins are needed try this query:
SELECT G.Name, Count(G.Name)
FROM enduser EU
INNER JOIN prescription P
ON EU.UserRefID = P.EndUserRefID
INNER JOIN prescriptiongame PG
ON PG.PrescriptionRefID = P.PrescriptionID
INNER JOIN games G
ON G.GameID = PG.GameRefID
INNER JOIN session S
ON S.PrescriptionRefID = P.PrescriptionID
INNER JOIN entries E
ON E.SessionGameRefID = S.Session
WHERE P.EndUserRefID = 889
AND E.Action = 'correctAnswer'
GROUP BY G.Name
ORDER BY G.Name
select G.Name , count(*) from enduser EU, prescription P,prescriptiongame PG ,
games G ,session S ,entries E where P.EndUserRefID = 889 and E.action='correctAnswer' and (EU.UserRefID = P.EndUserRefID) and (PG.PrescriptionRefID = P.PrescriptionID) and (G.GameID = PG.GameRefID) and (S.PrescriptionRefID = P.PrescriptionID) and (E.SessionGameRefID = S.Session)
GROUP BY G.Name
ORDER BY G.Name