What Join should I use in SQL - sql

I have four tables in my Database:
Person that contains ID(PK) and Name.
Person_Skill that contains ID(PK), PID(FK), Skill(FK) and SkillLevel(FK).
Skill that contains ID(PK) and SkillLabel.
SkillLevel that contains ID(PK) and Name.
Every skill has level from 0 to 7
Now I want to display all the skill that the person has(Include the skilllevel = 0)
Select
[dbo].Person.Name as Name,
[Skill].SkillLabel as SkillName,
[Person_Skill].[SkillLevel] as SkillLevel
From
([dbo].Person inner join [dbo].[Person_Skill] ON [dbo].[Person_Skill].PID= Person.ID)
inner join [dbo].[Skill] ON [dbo].[Person_Skill].Skill=Skill.ID
The above code only display the skill that person has from level 1 to level 7.
I believe the reason I only get the skill from level 1 to 7 is because the person table only contains the skill from level 1 to 7, but I'm not sure about this. I got the database from other. If my assumption is correct, is there anyway to do this? To get all the skills in the skill table and display the skill level that the person has(Include skillllevel =0).
Sample Data:
Person
ID Name
----------
1 Michael
2 Alex
Person_Skill
ID PID SkillID Skill_Level
5 1 10 5
6 2 11 1
7 1 12 7
8 1 13 5
Skill
ID Name
10 java
11 C++
12 HTML
13 ASP
14 C
15 .NET
16 C#
17 Objective
The expect results are;
Name SkillName SkillLevel
----------------------------
Alex java 0
Alex C++ 1
Alex HTML 0
Alex ASP 0
Alex C 0
Alex .NET 0
Alex C# 0
Alex Objective C 0
Michael java 5
Michael C++ 0
Michael HTML 7
Michael ASP 0
Michael C 0
Michael .NET 5
Michael C# 0
Michael Objective C0
The current query only output
Alex C++ 1
Michael java 5
Michael HTML 7
Michael .NET 5

Edit, if you want to return all skill names for each person, then you will want to use:
select d.p_name,
d.s_name SkillName,
coalesce(ps.Skill_Level, 0) Skill_Level
from
(
select p.id p_id, p.name p_name, s.id s_id, s.name s_name
from person p
cross join skill s
) d
left join person_skill ps
on d.p_id = ps.pid
and d.s_id = ps.skillid
left join skill s
on ps.skillid = s.id
See SQL Fiddle with Demo
If you want to include all Skills 0-7, then you will want to use a LEFT JOIN. You query will be similar to the following:
select p.Name as Name,
s.SkillLabel as SkillName,
ps.[SkillLevel] as SkillLevel
from [dbo].[Skill] s
left join [dbo].[Person_Skill] ps
on ps.Skill=s.ID
left join [dbo].Person p
on ps.PID = p.ID
Edit, without seeing any data it is difficult to determine. But if you want to retrieve all SkillLevels, then you will need to include that table. You might need to use:
select
p.Name as Name,
s.SkillLabel as SkillName,
sl.[Name] as SkillLevel
from SkillLevel sl
left join [dbo].[Person_Skill] ps
on ps.SkillLevel=sl.ID
left join [dbo].[Skill] s
on ps.Skill = s.Id
left join [dbo].Person p
on ps.PID = p.ID

You would want to use a LEFT JOIN which when tableA is inner joined on tableb would return all records from tableA regardless of whether or not there was a match from tableB
Therefore, if there are no persons with a skill of 0, you will still get back all of the person records.

An INNER JOIN will only return rows where there is a match on both sides. So in your code if a person does not have a skill with level 0 it would not be returned.
You could do a LEFT or RIGHT join and these get all the rows from the table on either the left or the right side. I think you probably want to use a left join, but without knowing more about your schema it's hard to say for sure. See the answer given to the question Left join and Left outer join in SQL Server for more detail on the differences in different join types

Related

SQL Join to return data from The Same Column in the same table to two diffrent rows in result (Star Wars Example)

Newbie question about joining tables. I want to retrieve a name from a column TWICE in a SQL statement, and I'm running in circles.
I Have two Tables - "Company" & "People"
Table -"People"
ID
Name
Phone
1
Luke
555-1212
2
Leia
555-1234
3
Han
999-8888
4
Anikin
888-9876
5
Obi-wan
555-1212
6
R2-D2
#% - **!?
Table - "Company"
ID
CompanyName
PrimaryContact
AltContact
1
Speeders R Us
5
1
2
Droid Repair World
6
4
3
Luke's Second Hand Store
1
4
4
Cloak World
4
5
5
Ye Old Blaster Shoppe
3
2
If I want to get a result that gives BOTH the Contact Names for a Company, How would I do it?
I can get the PrimaryContact to JOIN Properly using something like...
SELECT C.*, P.Name as 'Primary'
FROM `Company` C
Join People P on
C.PrimaryContact = P.ID
WHERE C.ID =3
which successfully returns
ID
CompanyName
PrimaryContact
AltContact
Primary
3
Luke's Second Hand Store
1
4
Luke
But for the life of me, I can't figure out how to modify this SQL to also return "Anikin" as the Alternate Contact. Is this an example of where a UNION statement would help?
You can join to the same table multiple times, just give a new alias every time.
Join People P on C.PrimaryContact = P.ID
Join People P1 on C.AltContact = P1.ID
Join People altcontact on C.AltContact = altcontact.ID
Join People P256 on C.yetanotheralternateContact = P256.ID
You need to to join for ecervy contact another Persons table
SELECT C.ID,C.CompanyName, P.Name as 'Primary' , P.Phone As 'primary_Phone', P2.Name on 'Alternative', P2.Phone as 'Alternatibe_Phone
FROM `Company` C
Join People P on
C.PrimaryContact = P.ID
Join People P2 on
C.AltContact = P2.ID
WHERE C.ID

SQL: Unable to find a join or union to produce the following table

A Pupil table with { ID, LastName}
a Subject Table with {ID, SubjectName}
and a Report Table with {ID, PupilID, SubjectID, Grade}
There is a one-to-many relationship between Pupil and Report Tables, and Subject and Report Tables.
I want to generate a table like this for say subjectID = 1
Pupil.ID Pupil.LastName SubjectID Grade
1 --------------Smith ---------- 1 ------------B
2 --------------Jones ---------- 1 ------------NULL
3 -------------Weston ----------1 ------------NULL
4 -------------Knightly ---------1 -----------A
The problem is that the Report table would contain just 2 entries for subject 1:
PupilID SubjectID Grade
----1------- 1 ----------- B
----4------- 1 ----------- A
Left joins don't seem to work since there are only 2 entries in the report table for subject 1
SAMPLE DATA
{Pupil Table}
ID LastName
1 ...Smith
2 ...Jones
3 ...Weston
4 ...Knightly
{Subject Table}
ID SubjectName
1 ....Maths
2 ....Physics
3 ....Chemistry
{Report Table}
ID PupilID SubjectID Grade
1 .......1 ..........1 ..........B
2 .......4 ..........1 ..........A
When I do a search on SubjectID = 1 I want the table:
Pupil.ID .......Pupil.LastName ........SubjectID ...........Grade
1 --------------Smith ---------- 1 ------------B
2 --------------Jones ---------- 1 ------------NULL
3 -------------Weston ----------1 ------------NULL
4 -------------Knightly ---------1 -----------A
Access doesn't do subqueries very easily, so everything gets crammed into the FROM clause with a series of wrapped parentheses. Based on your sample data and my fighting Access to stop being unnecessarily difficult, I came up with this:
SELECT ps.Pupil_ID, ps.LastName, ps.Subject_ID, r.Grade
FROM (SELECT * FROM (SELECT ID AS Pupil_ID, LastName FROM Pupil) p,
(SELECT DISTINCT ID AS Subject_ID FROM Subject)) ps
LEFT JOIN REPORT r ON r.PupilID = ps.Pupil_ID AND r.SubjectID = ps.Subject_ID
ORDER BY Pupil_ID, Subject_ID;
The subquery "ps" is a cartesian join of the Pupil and Subject table views that I specified. At this point, your query would look like this:
(LastName column not shown for clarity)
StudentID|SubjectID
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
Now, using that Cartesian join subquery (pupilstudent -> ps), I use a LEFT JOIN to assign the Report table to each unique student's ID and subject ID. Therefore, if a student did not take a particular class, there will be a NULL value in the final result.
I tested this in Access using your sample data and it works on my machine.
Also as a note, it is poor practice to have a field called just ID in each table (e.g. in the Pupil table, ID becomes PupilID). This makes it much easier to use, and it self documents.
Cross join pupil and subject tables and left join result to report table
What you need is a cross join:
SELECT Pupil.ID, Pupil.LastName, SubjectID, Grade FROM
Pupil, Subject LEFT JOIN Report ON Subject.ID=Report.SubjectID
WHERE Subject.ID=1
To combine every pupil with every (or with a particular) subject, use cross join; Then use left join to get the corresponding grades:
select *
from pupil p cross join (select * from subject where id = 1) s
left join report on subjectId = s.id and pupilId = p.id

Get data from relational tables

I have following tables in my db.
Calls:
ID Name
1 Call1
2 Call2
3 Call3
Notes:
ID Name
1 Note 1
2 Note 2
Person:
ID Name
1 Andrew
PersonCalls:
ID CallID PersonID
1 1 1
2 2 1
PersonNotes:
ID NoteID PersonID
1 1 1
1 2 1
Now I need to get data from these tables according to person id. I am able to get data from 2 tables, for example Notes and PersonNotes, but I am not able to get combined data from these tables. This should be the result.
Result:
PersonID Note Call
1 Note1 Call1
1 Note2 Call2
Please suggest
Thanks
Use Join (ie: Inner Join):
select distinct p.ID as PersonID, n.Name as Note, c.Name as Call from Person p
inner join PersonCalls pc on p.ID = pc.PersonID
inner join PersonNotes pn on p.ID = pn.PersonID
inner join Calls c on pc.CallID = c.ID
inner join Notes n on pn.NoteID = n.ID
Without getting too detailed you can use a left join on dbo.calls.ID, dbo.notes.id, dbo.person.id, etc if you are using MS SQL server. Otherwise are you sure you can't just write a report?

Efficient way for finding attributes in a table which belong together, based on second table

I have two tables:
person
----------
id name
1 peter
2 paul
3 mary
4 george
5 andy
and
living
-------
id key
1 1
2 1
3 2
4 2
5 2
What would be the most efficient query for finding all ids in person which belong together, if I know the name. Let's say I want to find out: "Who is Mary living with"? In person, Mary's id is 3. I check living for the key, which belongs to Mary, which is 2. Now I can retrieve all ids in living which go with key 2, and I have the ids which live together with Mary.
This is what I got so far:
SELECT p2.name
FROM person p1
JOIN living l1
ON p1.id = l1.id
JOIN living l2
ON l1.key = l2.key
JOIN person p2
ON l2.id = p2.id
WHERE p1.name = 'mary'
Is there a better way? The result seems to be fine, but it doesn't feel right to perform three JOINS.
What you could do in you case is:
DEFINE name CHAR(15);
LET name = 'mary'
define keyValue char(10);
select l.key into keyValue from person p left outer join living l on p.id= l.id and p.name= name
select p.*, l.* from person p left outer join living l on p.id=l.id
where key= keyValue
I have never used Informix before, however the the best solution should be along those lines or at least close to them.
Thanks

SQL Query to display heirarchical data

I have two tables - 'Users' and 'Supervision'
For this example, my users table is very simple:-
Users
=====
ID (PK)
UserName
Some users manage other users, so I've built a second table 'Supervision' to manage this:-
Supervision
===========
UserID
SuperID - this is the ID of the staff member that the user supervises.
This table is used to join the Users table to itself to identify a particular users supervisor. It might be that a user has more than one supervisor, so this table works perfectly to this end.
Here's my sample data in 'Users':-
userID userName
1 Bob
2 Margaret
3 Amy
4 Emma
5 Carol
6 Albert
7 Robert
8 Richard
9 Harry
10 Arthur
And my data in 'Supervision':-
userID superID
1 2
1 3
2 4
2 5
3 4
3 5
6 1
6 7
7 8
7 9
9 10
If I want to see who directly reports to Bob, writing an SQL query is straightforward, and tells me that Margaret and Amy are his direct reports.
What I want to do however is to write a query that shows everybody who comes under Bob, so it would need to look at Bobs direct reports, and then their direct reports, and so on - it would give Margaret, Amy, Emma and Carol as the result in this case.
I'm assuming this requires some kind of recursion but I'm completely stuck..
You should use recursive CTE:
WITH RCTE AS
(
SELECT * FROM dbo.Supervision WHERE UserID = 1
UNION ALL
SELECT s.* FROM dbo.Supervision s
INNER JOIN RCTE r ON s.userID = r.superID
)
SELECT DISTINCT u.userID, u.userName
FROM RCTE r
LEFT JOIN dbo.Users u ON r.superID = u.userID
SQLFiddle DEMO
Sounds to me like you need a Recursive CTE. This article serves as a primer, and includes a fairly similar example to the one you have:
http://blog.sqlauthority.com/2012/04/24/sql-server-introduction-to-hierarchical-query-using-a-recursive-cte-a-primer/
Hope it helps.
WITH MyCTE
AS (
-- ID's and Names
SELECT SuperID, ID
FROM Users
join dbo.Supervision
on ID = dbo.Supervision.UserID
WHERE UserID = 1
UNION ALL
--Who Manages who...
SELECT s.SuperID, ID
FROM Supervision s
INNER JOIN MyCTE ON s.UserID = MyCTE.SuperID
WHERE s.UserID IS NOT NULL
)
SELECT distinct MyCTE.ID, NAMES.UserName, '<------Reports to ' as Hierarchy, res_name.UserName
FROM MyCTE
join dbo.Users NAMES on
MyCTE.ID = NAMES.ID
join dbo.Users res_name
on res_name.ID = MyCTE.SuperID
order by MyCTE.ID, NAMES.UserName, res_name.UserName