join between two tables with != condition - mysql

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

Related

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;

Joining to table with multiple FKs and Rows

I have the following query:
SELECT p.id,
p.firstname,
**p.address1id,
p.address2id,**
r.invoice_id,
i.authcode
FROM membershiprenewals r,
Profile p,
Invoice i
WHERE r.orgID = 1
and r.period_id = 3
and r.status = 0
and r.profile_id = p.id
and r.invoice_id = i.id;
This table selects a Users Profile and a few related details.
A Profiles Addresses are stored in another table (profileaddress). And a Profile can have 2 addresses. These addresses are referenced using p.address1 and p.address2.
I need to extend this query to join on the profileaddress table to get BOTH addresses and combined into the single record.
So the results I would need would be the following columns
p.id | p.firstname | .. etc .. | profileaddress1.address | profileaddress1.town | profileaddress2.address | profileaddress2.town | .. etc
I've been playing around with JOIN statements for hours, but just can't seem to crack it.
Any help hugely Appreciated !!
Jason
First, never use commas in the FROM clause. Always use proper, explicit JOIN syntax. So, your query should be:
SELECT p.id, p.firstname, **p.address1id, p.address2id,**
r.invoice_id, i.authcode
FROM membershiprenewals r JOIN
Profile p
ON r.profile_id = p.id JOIN
Invoice i
ON r.invoice_id = i.id
WHERE r.orgID = 1 AND r.period_id = 3 AND r.status = 0;
Then you want two joins to the address table:
SELECT p.id, p.firstname, p.address1id, p.address2id,
pa1.address, pa1.town,
pa2.address, pa2.town,
r.invoice_id, i.authcode
FROM membershiprenewals r JOIN
Profile p
ON r.profile_id = p.id JOIN
Invoice i
ON r.invoice_id = i.id LEFT JOIN
profileaddress pa1
ON p.address1id = pa1.id LEFT JOIN
profileaddress pa2
ON p.address2id = pa2.id
WHERE r.orgID = 1 AND r.period_id = 3 AND r.status = 0;
This uses LEFT JOIN in case one of the addresses is missing.

Select values from one table based on two other tables (relational)

I have 3 tables one with company details, one with officer details and one that connects those two Company_Officer by ID so I can tell which officer works for which company and he can also work for multiple companies and a company can have multiple workers.
I am trying to create a query that would give me ID of the company that Officer works for company_Id, officers name and his role. The company he works for must have company_index set to FTSE 100 his status officer_resigned must be set to 0 and also he must work for more than 1 company.
Something like:
Company_ID|Company_Name|Officer_Name|Officer_Role
--------------------------------------------------
1 | Apple PLC |Millis, John|Director
1 | Apple PLC |DLAMINI, Bob|Secretary
2 | Google PLC |Millis, Johm|Secretary
Company_Details:
Officer_Details:
Company_Officer:
I have started fiddling with sql but it does not make much sense to me when it comes to relational databases. I understand that I need to use join. Is it all possible to achieve with one query?
Another sql for extra constraint of "getting only those officers which work for more than 1 company".
SELECT cd.company_id,
cd.company_name,
od.officer_name,
co.officer_role
FROM COMPANY_DETAILS cd
inner join COMPANY_OFFICER co
ON cd.company_id = co.company_id
inner join OFFICER_DETAILS od
ON co.officer_id = od.officer_id
WHERE cd.company_index = 'FTSE 100' AND
od.officer_resigned = '0' AND
co.officer_id IN
( SELECT officer_id
FROM COMPANY_OFFICER
GROUP BY officer_id
HAVING Count( DISTINCT company_id ) > 1
);
SELECT
CD.company_id,
CD.company_name,
OD.officer_name,
CO.officer_role
FROM
company_details CD
INNER JOIN company_officer CO
ON CD.company_id = CO.company_id
INNER JOIN officer_details OD
ON CO.officer_id = OD.officer_id
WHERE CD.company_index='FTSE 100' AND
OD.officer_resigned='0';
Do you even need to join?
SELECT DISTINCT c.Company_ID, c.Company_Name, o.Officer_Name, o.Officer_Role
FROM Company_Details c, Officer_Details o, Company_Officer co
WHERE Company_Index = 'FTSE 100' AND Officer_Resigned = 0 AND co.Officer_ID = o.Officer_ID AND co.Company_ID = c.Company_ID
Simply use inner join between the 3 table
select
Company_Details.Company_ID
, Company_Details.Company_Name
, Officer_Details.Officer_Name
, Company_Officer.Officer_Role
from Company_Details
INNER JOIN Officer_Details on Officer_Details.Officer_ID = Company_Officer.Officer_ID
INNER JOIN Company_Officer on Company_Officer.Company_ID = Company_Details.Company_ID;

mysql: Group Concat with joined tables and values that aren't unique

I have an issue where I need to aggregate and concatenate multiple row data into single row output. I understand the tables are a problem in that their is no unique index, but I need to do this at the query level instead of the scripting level and I can't touch the database structure.
Here we go:
table: characteristics
id code_a code_b
-------------------------------
2201 CHAU AIRS
2201 CHAU PELC
2201 PROX AUTO
2201 PROX HOP`
table: characteristics_types
code description
-------------------------------
CHAU Heating System
PROX Nearby
table: characteristics_sub_types
code_a code description
-------------------------------
CHAU AIRS Forced Air
CHAU PELC Baseboard
PROX AUTO Highway
PROX HOP Hospital
Result required:
id Heating System Nearby
--------------------------------------------------------
2201 Forced Air, Baseboard Highway, Hospital
Not working:
SELECT id,
(case when C.code_a='CHAU' THEN GROUP_CONCAT(STC.description) ELSE NULL END) AS Heating System,
(case when C.code_a='PROX' THEN GROUP_CONCAT(STC.description) ELSE NULL END) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join characteristics_sub_types STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id,C.code_a
I am getting the following results:
id Heating System Nearby
--------------------------------------------------------
2201 Forced Air, Baseboard NULL
2201 NULL Highway, Hospital
Any direction would be greatly appreciated!
GROUP_CONCAT accepts a DISTINCT keyword which is useful in fan-out-queries. You can also use ORDER BY within a GROUP_CONCAT if you need your results to be ordred. See Documentation. Using this, we can write your intended query like below:
SELECT
c.id,
GROUP_CONCAT(DISTINCT ct.description) as 'Heating System',
GROUP_CONCAT(DISTINCT cst.description) as 'Nearby'
FROM characteristics c
LEFT JOIN characteristics_types ct ON ct.id = c.id
LEFT JOIN characteristics_sub_types cst
ON cst ON cst.code_a = c.code_a AND cst.code = c.code_b
GROUP BY 1
I would write separate subqueries, each one preforming the group_concat that you need, and join them together. Individually, I wrote them like this:
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Heating System'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'CHAU'
GROUP BY c.id;
And the join like this:
SELECT c.id, t1.`Heating System`, t2.`Nearby`
FROM(
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Heating System'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'CHAU'
GROUP BY c.id) t1
JOIN(
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Nearby'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'PROX'
GROUP BY c.id) t2 ON t1.id = t2.id;
Here is a working SQL Fiddle.
You can try this way:
SELECT c.id,
GROUP_CONCAT(STC.description_heating) AS `Heating System`,
GROUP_CONCAT(STC.description_nearby) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join (
SELECT code_a, code,
CASE WHEN code_a = 'CHAU' THEN description ELSE null END as descriptio_heating,
CASE WHEN code_a = 'PROX' THEN description ELSE null END as descriptio_nearby,
FROM characteristics_sub_types
) as STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id
SELECT c.id,
GROUP_CONCAT(STC.description_heating) AS `Heating System`,
GROUP_CONCAT(STC.description_nearby) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join (
SELECT code_a, code,
CASE WHEN code_a = 'CHAU' THEN description ELSE null END as description_heating,
CASE WHEN code_a = 'PROX' THEN description ELSE null END as description_nearby
FROM characteristics_sub_types
) as STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id

Selecting from different tables based on condition

I Have a table comments ->
id | comment | type | userid |
1 Hello human 9
2 Hi robot 4
3 Gnaw! animal 1
4 Boo ghost 2
Also i have four more tables human,robot,ghost and animal
These tables contains some basic details about themselves...
Now I have a know value of comment say : $id = 3
if i do
$data = mysqli_query($con,"SELECT type FROM comments WHERE id = $id");
while($row = mysqli_fetch_assoc($data)){
$table = $row['type'];
$table_data = mysqli_fetch_assoc(mysqli_query($con,"SELECT * FROM $table"));
}
this will fetch me all the data about the one who commented but this will prove to be too slow....is there any way i can combine this in one single query ?
One way to do this is with left joins.
SELECT c.type, COALESCE(h.detail,r.detail,a.detail,g.detail)
FROM comments
LEFT JOIN human h ON c.type = 'human' AND c.id = h.id
LEFT JOIN robot r ON c.type = 'robot' AND c.id = r.id
LEFT JOIN animal a ON c.type = 'animal' AND c.id = a.id
LEFT JOIN ghost g ON c.type = 'ghost' AND c.id = g.id
Another way would be to do a UNION on the four tables and then join those:
SELECT c.type, q1.detail
FROM comments c
LEFT JOIN
(
SELECT 'human' AS type, detail FROM human
UNION
SELECT 'robot', detail FROM robot
(etc.)
) q1 ON c.type = q1.type AND q1.id = c.id
I would prefer the second option, because this one makes it easier to join lots of detail-columns. I don't think there's much of a difference perfomance-wise.