2 Nested queries from the same table? - mysql

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;

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

COnditional JOIN query in MySQL

I have two mysql tables
user:
|--------------------------------|
| id | name | type | ruser_type |
|--------------------------------|
| 1 | Admin | a | |
| 2 | | r | c |
|--------------------------------|
customer
|-------------------------|
| id | name | user_id |
|-------------------------|
| 1 | Sam | 2 |
|-------------------------|
If user.type is 'a' or 's', then its admin user whose name is in user table.
If user.type is 'r' and ruser_type is 'c', then its regular user which has a relation in customer table where customer.user_id = user.id
I want a query which would run a conditional join.
If user.type is 'a' or 's', then name would be fetched from user table.
If user.type is 'r' and and ruser_type is 'c', then name would be fetched from customer table with the JOIN condition customer.user_id = user.id.
For this, I have written a query like this:-
SELECT users.fname as adminFname, customers.fname as customerFname, users.type FROM users
LEFT JOIN customers ON (customers.user_id = users.id AND
(
(users.type = 'r' AND users.ruser_type = 'c')
OR users.type = 'a'
OR users.type = 's'
)
)
WHERE users.id = 1
Is there any possibility to optimize the query more?
Also, how can I write this query using Laravel eloquent?
FWIW, I find this marginally easier to read...
SELECT u.fname adminFname
, c.fname customerFname
, u.type
FROM users u
LEFT
JOIN customers c
ON c.user_id = u.id
WHERE u.id = 1
AND (
(u.type = 'r' AND u.ruser_type = 'c')
OR (u.type IN('a','s'))
)
I have written two sql query hope this will help you
SELECT CASE CU.type WHEN 'a' OR 's' THEN CU.name END AS name,
CASE WHEN CU.type = 'r' AND CU.ruser_type = 'c' THEN CR.name END AS cust_name, CU.type
FROM
user AS CU
LEFT JOIN customer AS CR ON CR.user_id = CU.id
In this you'll get result like this,
name cust_name type
Sam r
Admin a
and i have wrote another query like this,
SELECT CASE WHEN CU.type = 'a' OR 's' THEN CU.name ELSE CR.name END AS name, CU.type
FROM
user AS CU
LEFT JOIN customer AS CR ON CR.user_id = CU.id
In this you'll get result like this
name type
Sam r
Admin a
DB File Link
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=b02d931b68a4f70d8b4a84144c60a572
This will give you required result for all users:
SELECT u.fname adminFname
, c.fname customerFname
, u.type
FROM users u
LEFT JOIN customers c
ON (u.type = 'r' AND u.ruser_type = 'c' AND c.user_id = u.id)
Add where condition as required.
You can even simplify it further to get common firstName column in output:
SELECT COALESCE(u.fname, c.fname) firstName, u.type
FROM users u
LEFT JOIN customers c
ON (u.type = 'r' AND u.ruser_type = 'c' AND c.user_id = u.id)

join between two tables with != condition

I have two tables
courses_available:
Course | language
-----------------
A | English
A | Spanish
courses_enrolled
Name |course | language
--------------------------
PersonA | A | English
PersonB | A | Spanish
PerconC | A | French
I want to write a query to gives me records where a person is taking a course in an language that it is not offered in. In the above example, PersonC is taking course A in French but its only offered in English & Spanish.
select p.name, p.course, p.language from courses_available c, courses_enrolled p where p.course=c.course and p.language != c.language.
The above query is giving me course A as well. What kind of join will give me only C?
i hope it helps. Just use "<>" instead of !=
Example:
SELECT * FROM courses_available
LEFT JOIN courses_enrolled ON courses_available.course = courses_enrolled.course
WHERE courses_available.language <> courses_enrolled.language
No need to use '!=' you can add one more on condition
select ce.* from course c inner join courses_enrolled ce
on
c.Course = ce.Course
and
c.Language = ce.Language
you can see in sqlFiddle
use left join with condition b.language is null
select p.name, p.course, p.language
from courses_enrolled a left join courses_available b
on a.course=b.course and a.language = b.language
where b.language is null
Use not exists
Select enrolled.*
From enrolled
Where.not exists (
Select 1
From courses
Where courses.id = enrolled.id
And courses.language = enrolled.language
)
You can't do this by joining where language doesn't match, you want to join where it does match but only select rows where, with a left join, no courses_available is found:
select p.name, p.course, p.language
from courses_enrolled p
left join courses_available c on p.course=c.course and p.language = c.language
where c.course is null

Populate/Join multiple tables of many to many relationship table in 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;

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