I have three tables tblCourse , tblDegree , tblStudent. I have created a course and degree relation table as like tblCourseDegreeRelation. This relational table uses foreign keys of course and degree table as like:
The tblCourseDegreeRelation table is like:
The tblCourse table is like:
The tblDegree table is like:
The tblStudent (In this table the degree id is foreign key d_id) table is like:
I need to get all records including the tblStudent all record using this query:
SELECT * from tbldegree d
INNER JOIN tblcoursedegreerelation cdr ON d.d_id = cdr.d_id
INNER JOIN tblcourse c ON cdr.c_id = c.c_id
INNER JOIN tblstudent s ON d.d_id = s.d_id
ORDER BY cdr.cdr_id DESC
But this only returns the one student's record while I've two students in the database see below:
How I can get all students records from the joins query?
In your case you have all inner joins, so it will return results where both/all tables satisfies their criteria (on clause).
Viewing your data your student 1 => Ali has a relation with degree 1 =>BS Information Technology.
Further degree 1 has courses (1 => Programming, 2 => English, 5=> Mathematics , 6 => Electronics)
So for student 1 your inner join clause works because it has data in all joined tables.
Now if we look for your student 3 => Bilal who has a relation with degree 3 => BS Mathematics, But this degree has no assigned courses that is why your student Bilal isn't returned
To get all students no matter their related degree has courses you can turn your inner joins into left join not for all tables but for tblcoursedegreerelation and tblcourse
SELECT *
FROM tblstudent s
INNER JOIN tbldegree d ON d.d_id = s.d_id
LEFT JOIN tblcoursedegreerelation cdr ON d.d_id = cdr.d_id
LEFT JOIN tblcourse c ON cdr.c_id = c.c_id
ORDER BY cdr.cdr_id DESC
Demo
In the result set you can see following columns as null due to no association with courses
cdr_id, c_id, d_id, c_id, c_name, c_credit
Just do a Right join on tblstudent:
SELECT * from tbldegree d
INNER JOIN tblcoursedegreerelation cdr ON d.d_id = cdr.d_id
INNER JOIN tblcourse c ON cdr.c_id = c.c_id
RIGHT JOIN tblstudent s ON d.d_id = s.d_id
ORDER BY cdr.cdr_id DESC
EDIT
This way is better:
SELECT c.d_id,c.d_name,c.d_fee,cdr.cdr_id,cdr_c_id,deg.c_name,deg.d_credit,a.s_id,a.s_name
FROM tblstudent a
left join tblDegree c ON a.d_id = c.d_id
left join tblcoursedegreerelation cdr ON cdr.d_id=c.d_id
left join tblcourse deg on deg.c_id=cdr.c_id
ORDER BY cdr.cdr_id DESC
Related
I'm trying to do a 3 table join and it's giving me duplicate records so I'm unsure what I'm doing incorrectly.
SELECT e.*, cs.*, c.* FROM employee e
LEFT JOIN coffee_shop cs ON e.shop_id = cs.shop_id
LEFT JOIN coffee c ON cs.shop_id = c.shop_id
I want the coffee_shop table to join on the employee table's shop_id and the coffee table to join on coffee_shop's shop_id to perform a 3 table join. However it's giving me duplicate rows (9 rows) when all the tables only have 3 rows each.
How do I perform this join without duplicates?
Edit:
If I do only the join on the first two tables(employee and coffee_shop) it is as expected
I want to perform one more join from coffee onto coffee_shop. Which should also return 3 rows
Here is the result I want:
This will give you 3 rows for your use-case. For each shop_id in the coffee table, this will return the row with the first coffee_id.
Depending on which coffee you want to return, you can adjust the logic for the row_rank field.
SELECT e.*, cs.*, c.*
FROM employee e
LEFT JOIN coffee_shop cs ON e.shop_id = cs.shop_id
LEFT JOIN (
SELECT *,
RANK() OVER(PARTITION BY shop_id ORDER BY coffee_id) as row_rank
FROM coffee
) c ON cs.shop_id = c.shop_id and c.row_rank = 1
Try to use DISTINCT in your query like :
SELECT DISTINCT e.*, cs.*, c.* FROM employee e
LEFT JOIN coffee_shop cs ON e.shop_id = cs.shop_id
LEFT JOIN coffee c ON cs.shop_id = c.shop_id
if your main tables is employee,
SELECT e.*, cs.*, c.* FROM employee e
INNER JOIN coffee_shop cs ON e.shop_id = cs.shop_id
LEFT JOIN coffee c ON cs.shop_id = c.shop_id
I have a bills table with column customer_type and customer_id fields.
This customer_type tells if the customer is in the customers table or in the users table or in the suppliers table.
I need to create a query with left join according to customer_type.
select c.* from bills b
left join ***b.customer_type*** c on c.id = b.customer_id
You could join all three with necessary condition:
select c.*, u.*, s.* from bills b
left join customers c on c.id = b.customer_id and b.customer_type = 'customers'
left join users u on u.id = b.customer_id and b.customer_type = 'users'
left join suppliers s on s.id = b.customer_id and b.customer_type = 'suppliers'
Then you can take the data that is relevant from the result.
However if there are similar columns in these 3 tables you might want to restructure the database to only store one type of information in one place.
I have a database with the tables:
Student(SID,Name,Surname,Age)
Registration(StudentID,CourseID)
Course(CID,Name,Cost)
I would like to extract only the name of the courses with students younger than 20. Will the query below do just that?
SELECT C.NAME
FROM Course C
INNER JOIN Registration
INNER JOIN Student S
WHERE CID = CourseID
AND SID = StudentID
AND Age < 20
GROUP BY C.NAME
I would also like to extract the number of students in each course having students younger than 20. Is it correct to do it as below?
SELECT count(S.NAME)
,C.NAME
FROM Student S
INNER JOIN Course C
INNER JOIN Registration
WHERE Age < 20
AND CID = CourseID
AND SID = StudentID
GROUP BY C.NAME
You are missing the ON part for the join otherwise it would just be a CROSS JOIN.
Your first query should look like this if you want just a distinct list of student names:
SELECT DISTINCT C.NAME
FROM Course C
INNER JOIN Registration R ON C.CID = R.CourseID
INNER JOIN Student S ON R.StudentID = S.SID
WHERE Age < 20
Your second query shouldn't really have the C.Name in the select if you want to get just a count unless you want a count of how many students have that name.
SELECT count(*)
FROM Student S
INNER JOIN Registration R ON s.SID = R.StudentID
INNER JOIN Course C ON c.CID = R.CourseID
WHERE Age < 20
GROUP BY C.NAME
First join these tables, then group by Course's PK(CID), Add the HAVING condition to filter the course which has students younger than 20.
Then use Course table to join the result to get the course name and count of students in the course.
SELECT
T1.Name,
T2.StudentCount
FROM
Course T1
INNER JOIN (
SELECT
c.CID,
COUNT(s.SID) AS StudentCount
FROM
Course c
LEFT JOIN Registration r ON c.CID = r.CourseID
LEFT JOIN Student s ON s.SID = r.StudentID
GROUP BY c.CID
HAVING COUNT(IF(s.Age < 20, 1, NULL)) > 0
) T2 ON T1.CID = T2.CID
More correctly, you should move the conditions of the join, to the join statements themselves by including them in the on clause instead of the where. While the results may not change in this instance, if you were to start including outer joins you would encounter difficulties.
SELECT count(S.NAME)
,C.NAME
FROM Student S
INNER JOIN Registration R
ON s.SID = R.StudentID
INNER JOIN Course C
ON c.CID = R.CourseID
WHERE Age < 20
GROUP BY C.NAME
There's a fiddle here showing it in action: http://sqlfiddle.com/#!9/c3b8f/1
Your first query will also produce the results you want, but again, you should move the join predicates to the join itself. Also, you don't need to perform the grouping just to get distinct values, mysql has an expression for that called distinct. So rewritten, the first query would look like:
SELECT DISTINCT C.NAME
FROM Student S
INNER JOIN Registration R
ON s.SID = R.StudentID
INNER JOIN Course C
ON c.CID = R.CourseID
WHERE Age < 20.
Again, the results are the same as what you have already but it is easier to 'read' and will put you in good stead when you move on to other queries. As it stands you have mixed implicit and explicit join syntax.
This fiddle demonstrates both queries: http://sqlfiddle.com/#!9/c3b8f/4
edit
I may have misinterpreted your original question - if you want the total number of students enrolled in a course with at least one student under 19, you can use a query like this:
select name, count(*)
from course c
inner join registration r
on c.cid = r.courseid
where exists (
select 1
from course cc
inner join registration r
on cc.cid = r.courseid
inner join student s
on s.sid = r.studentid
where cc.cid = c.cid
group by cc.cid
having min(s.age) < 20
)
group by name;
Again with the updated fiddle here: http://sqlfiddle.com/#!9/c3b8f/17
I am struggling to get this query to work . I have three tables and I want to do a query to get the red area.
Each circle is a table with different structure. I have managed a lot of combinations of inner joins but i specially cant get all the red areas.
A Payments : idPayment , idInvoice , Amount , date.
B Invoice : idInvoice , amount date.
C PromissoryNotes: IdNote , idInvoice, amount, date.
so far ...
SELECT B.idInvoice,A.idPayment,C.idNote FROM (Invoice b INNER JOIN payments a ON a.idInvoice=b.idInvoice) LEFT OUTER JOIN PromissoryNotes c ON c.idInvoice=b.idInvoice ORDER BY idInvoice.
DOESNT QUITE WORK
Any suggestions?
You were pretty close -- another OUTER JOIN and some WHERE criteria will do the trick:
SELECT B.idInvoice, A.idPayment, C.idNote
FROM Invoice b
LEFT JOIN payments a ON a.idInvoice=b.idInvoice
LEFT JOIN PromissoryNotes c ON c.idInvoice=b.idInvoice
WHERE a.idInvoice IS NOT NULL
OR c.idInvoice IS NOT NULL
ORDER BY B.idInvoice
What this basically says is give me all results from table B, where there's a match in table a or table c.
Condensed SQL Fiddle Demo
You could do this two ways:
1) Create a set A that is the inner join of B and A, create a set C that is the inner join of B and C, then union A and C.
2) Create a sub query that inner joins A and B, then full outer join to a sub query that inner joins C and B.
Example of 1)
SELECT b.idInvoice FROM Invoice B
JOIN Payments A on A.IdInvoice = B.IdInvoice
UNION
SELECT b.idInvoice FROM Invoice B
JOIN PromissoryNotes C on c.idInvoice = B.id Invoice
Example of 2)
SELECT idInvoice FROM
(
SELECT b.idInvoice FROM Invoice B
JOIN Payments A on A.IdInvoice = B.IdInvoice
) B FULL OUTER JOIN
(
SELECT b.idInvoice FROM Invoice B
JOIN Payments A on A.IdInvoice = B.IdInvoice
) C on b.idInvoice = C.idInvoice
Try
SELECT B.idInvoice, A.idPayment, C.idNote FROM Invoice B INNER JOIN payments A ON A.idInVoice = B.idInvoice INNER JOIN PromissoryNotes C ON C.idInvoice = B.idInvoice ORDER BY idInvoice
INNER JOIN means you get the intersection of both tables. So this is what you want.
Does this do the trick?
SELECT
ZZ.idInvoice,
ZZ.idPayment,
YY.idInvoice,
YY.idNote
FROM
(SELECT idInvoice, idPayment
FROM Invoice b
INNER JOIN payments a ON a.idInvoice=b.idInvoice) AS ZZ
FULL OUTER JOIN
(SELECT idInvoice, idNote
FROM PromissoryNotes c
INNER JOIN payments a ON a.idInvoice=c.idInvoice) AS YY ON ZZ.idInvoice = YY.idInvoice
SELECT p.idInvoice, p.idPayment, idNote
FROM Payments p JOIN Invoice i ON p.adInvoice-i.adInvoice RIGHT OUTER JOIN PromissoryNotes
UNION
SELECT i.idInvoice, idPayment, idNote
FROM Invoice i JOIN PromissoryNotes pn ON i.idInvoice=pn.idInvoice RIGHT OUTER JOIN Payments
You need to include the outer joins because the resulting tables that are to be unioned must have the same schema. I believe these are the desired fields from the query.
I have a main table:
- cam (id, name, type, desc, tenant_id)
with information normalized into 3 different tables:
- cs with columns (cam_id, st_id)
- ot with columns (cam_id, loc_id, tar_id, tenant_id)
- st with columns (id, code, name)
please note:
- cs.st_id is a foreign key to st.id
- ot.loc_id is a foreign key to st.id,
- ot.tar_id is a foreign key to st.id
My intention is to get the
- st.code value for all ot.loc_id and cs.st_id
(I am not interested in the id's but the their codes which is stored in table st)
This SQL:
- select
cam.id, cam.name, camp.type, cam.desc, st.code as cs.code
from
cam
left join cs on cam.id = cs.cam_id
left join ot on cam.id = ot.cam_id
left join st on cam.tenant_id = st.tenant_id;
works in that the last join condition to st table makes the st.codes available.
But what do I have to do to get the ot.loc_id codes?? I can't have multiple from clauses right? or multiple tables in from clause ... right?
Or is there no way out but to make separate SQL statements (which may not be performant i.e making an additional call)?
Thanks!
Take-Away: The join condition does not need to include the table in the from clause! Please see answer below.
No you can't have multiple from clauses
Yes you can have multiple tables in from clause
For example you could do :
select a.id, a.name, a.type, a.desc, b.code as cs.code, b.code
from cam a, st b, cs c, ot d
where a.id = c.cam_id
and a.id = d.cam_id
and st.id = d.loc_id
and b.tenant_id = a.tenant_id
Or your could simply refer to field in joined tables or views in the select statement.
When you do a join, you "join" the table to your select statement and can access them.
Example with join :
select cam.id, cam.name, camp.type, cam.desc, st.code as cs.code, st.code
from cam
left join cs on cam.id = cs.cam_id
left join ot on cam.id = ot.cam_id
left join st on cam.tenant_id = ot.tenant_id
left join st st2 on st.id = ot.loc_id;
Is your intention to join to the st table twice?
select cam.id, cam.name, camp.type, cam.desc, st.code as cs_code , st.code as loc_code
from cam left join
cs
on cam.id = cs.cam_id left join
ot
on cam.id = ot.cam_id left join
st
on cam.tenant_id = st.id left join
st stloc
on ot.loc_id = stloc.id;
According to the columns in your tables, st doesn't have a tenant_id so I changed that to just id.