Oracle SQL joins with a many to one relationship - sql

Thanks in advance for any assistance you might be able to give. I think I may be asking a trivial question but I cannot determine how I would do this nor what exactly to search for. So my apologies if your time is wasted.
So I have two tables TableA and TableB and I need to join the two sets of data in a specific manner that I will explain below with reference to some example tables.
TableA:
Name | Contact_1 | Contact_2 | Contact_3
----------------------------------------
Joe | Anne | Sue | Phil
Dan | Tom | |
May | Jeff | Pete | Tim
etc.
TableB:
Name | Contact_No
-----------------
Anne | 123456789
Sue | 234567891
Phil | 345678912
Tom | 456789123
Jeff | 567891234
Pete | 678912345
Tim | 789123456
Resulting Table when Joining between TableA and TableB on:
TableA.Contact_1 = TableB.Name
AND
TableA.Contact_2 = TableB.Name
AND
TableA.Contact_3 = TableB.Name
gives:
Name | Contact_1 | ContactNo_1 | Contact_2 | ContactNo_2 | Contact_3 | ContactNo_3
----------------------------------------
Joe | Anne | 123456789 | Sue | 234567891 | Phil | 345678912
Dan | Tom | 456789123 | | | |
May | Jeff | 567891234 | Pete | 678912345 | Tim | 789123456
This is just a trivial example to explain my problem. Sorry for such a lengthy explanation but I would rather have a detailed example to limit confusion.
Thanks!

You have to join TableA with TableB three times:
select ta.name,
ta.contact_1, tb1.contact_no,
ta.contact_2, tb2.contact_no,
ta.contact_3, tb3.contact_no
from TableA ta
left join TableB tb1 on ta.contact_1 = tb1.name
left join TableB tb2 on ta.contact_2 = tb2.name
left join TableB tb3 on ta.contact_3 = tb3.name;
If there are null values allowed in TableA.contact_1 (_2, _3), then you need left joins.
EDIT
Notice, however, that it is usually better to have a separate table that keeps the fact that a pair (name, name) is connected, e.g.,
create table TableA(a_name varchar primary key);
create table TableB(b_name varchar primary key,
b_contact_no varchar);
create table Contacts(a_name varchar,
b_name varchar,
primary key(a_name,b_name),
foreign key(a_name) references TableA(a_name),
foreign key(b_name) references TableB(B_name));
That way, you can model an arbitray number of contacts without having to change your database schema.

Related

SQL Self Join not "joining" in PopSQL

I'm currently learning SQL and came across an obstacle.
I have been looking around the web and found no case where the table is being "replicated" as is my case.
The case is based on a classical example where we want to have a result where we get the employee name and the next column with the supervisor name.
Desired result:
|Employee Name | Supervisor Name|
---------------------------------
|Dave | NULL |
|Rick | Dave |
|Andy | Rick |
The current table goes like this (employee):
|emp_id | first_name | last_name| super_id |
--------------------------------------------
|100 | Dave | Star | NULL |
|101 | Rick | Port | 100 |
|101 | Andy | Huds | 101 |
I'm trying to do this with a SELF JOIN in POPSQL (MySQL based) but first I will show how the employee table was created.
CREATE TABLE employee (
emp_id INT PRIMARY KEY,
first_name VARCHAR(40),
super_id INT,
);
ALTER TABLE employee
ADD FOREIGN KEY (super_id)
REFERENCES employee(emp_id)
ON DELETE SET NULL;
This is the code I'm using to make the query:
SELECT m.first_name, s.first_name
FROM employee AS m
INNER JOIN employee AS s
ON m.super_id = s.emp_id;
And below the result I'm getting.
Notice how the table has two problems.
First, each row seems to be repeated (David appears in two rows).
Second, the emp_id is not matched to sup_id (as per the ON m.super_id = s.emp_id).
|emp_id | first_name |
----------------------
|David | David |
|David | David |
|Rick | Rick |
|Rick | Rick |
Could somebody help me?
Thanks
if you need to get all records (including Dave), you need to use OUTER JOIN
SELECT m.first_name AS EmployeeName,
s.first_name AS SupervisorName
FROM employee AS m
LEFT JOIN employee AS s
ON m.super_id = s.emp_id;
SELECT m.first_name, s.first_name
FROM employee AS m
left JOIN employee AS s
ON m.super_id = s.emp_id;

Simple SQL Query to bring back null if no match found

EDIT
I've edited this question to make it a little more concise, if you see my edit history you will see my effort and 'what I've tried' but it was adding a lot of unnecessary noise and causing confusion so here is a summary of input and output:
People:
ID | FullName
--------------------
1 | Jimmy
2 | John
3 | Becky
PeopleJobRequirements:
ID | PersonId | Title
--------------------
1 | 1 | Some Requirement
2 | 1 | Another Requirement
3 | 2 | Some Requirement
4 | 3 | Another Requirement
Output:
FullName | RequirementTitle
---------------------------
Jimmy | Some Requirement
Jimmy | Another Requirement
John | Some Requirement
John | null
Becky | null
Becky | Another Requirement
Each person has 2 records, because that's how many distinct requirements there are in the table (distinct based on 'Title').
Assume there is no third table - the 'PeopleJobRequirements' is unique to each person (one person to many requirements), but there will be duplicate Titles in there (some people have the same job requirements).
Sincere apologies for any confusion caused by the recent updates.
CROSS JOIN to get equal record for each person and LEFT JOIN for matching records.
Following query should work in your scenario
select p.Id, p.FullName,r.Title
FROM People p
cross join (select distinct title from PeopleJobRequirements ) pj
left join PeopleJobRequirements r on p.id=r.personid and pj.Title=r.Title
order by fullname
Online Demo
Output
+----+----------+---------------------+
| Id | FullName | Title |
+----+----------+---------------------+
| 3 | Becky | Another Requirement |
+----+----------+---------------------+
| 3 | Becky | NULL |
+----+----------+---------------------+
| 1 | Jimmy | Some Requirement |
+----+----------+---------------------+
| 1 | Jimmy | Another Requirement |
+----+----------+---------------------+
| 2 | John | NULL |
+----+----------+---------------------+
| 2 | John | Some Requirement |
+----+----------+---------------------+
use left join, no need any subquery
select p.*,jr.*,jrr.*
from People p left join
PeopleJobRequirements jr on p.Id=jrPersonId
left join JobRoleRequirements jrr p.id=jrr.PersonId
according the explanation, People and PeopleJobRequirements tables have many to many relationship (n to n).
so first of all you'll need another table to relate these to table.
first do this and then a full join will make it right.

How to create a table from different query results SQL

I want to create a new table using the results from some queries. I might be looking at this the wrong way so please feel free to let me know. Because of this I will try to make this question simple without putting my code to match each employee number with each manager level column from table2
I have two tables, one has employee names and employee numbers example
table 1
+-------------+-----------+-------------+-------------+
| emplpyeenum | firstname | last name | location |
+-------------+-----------+-------------+-------------+
| 11 | joe | free | JE |
| 22 | jill | yoyo | XX |
| 33 | yoda | null | 9U |
+-------------+-----------+-------------+-------------+
and another table with employee numbers under each manager level so basically a hierarchy example
Table 2
+---------+----------+----------+
| manager | manager2 | manager3 |
+---------+----------+----------+
| 11 | 22 | 33 |
+---------+----------+----------+
I want to make a new table that will have the names besides the numbers, so for example but with employee number beside the names
+---------+--------+----------+
| level 1 | level2 | level3 |
+---------+--------+----------+
| jill | joe | yoda |
+---------+--------+----------+
How can I do this?
edit sorry guys I don't have permission to create a new table or view
Why not change your table2 to this?
+------------+----------+
| EmployeeId | ManagerId|
+------------+----------+
| 11 | NULL |
+------------+----------+
| 22 | 11 |
+------------+----------+
| 33 | 22 |
+------------+----------+
Then you can do what you want with the data. At least your data will be properly normalized. In your table2. What happen if employee 33 hire another employee below him? You will add another column?
Based on your available table, this should give you the result you want.
SELECT m1.firstname, m2.firstname, m3.firstname
FROM table2 t
LEFT JOIN table1 m1 ON m1.employeenum = t.manager
LEFT JOIN table1 m2 ON m2.employeenum = t.manager2
LEFT JOIN table1 m3 ON m3.employeenum = t.manager3
You can just do a basic create table, then do a insert select to that will fill the table the way you need it. All you have to do is replace the select statement that I provided with the one you used to create the levels table output.
create table Levels
(
level1 varchar(25),
level2 varchar(25),
level3 varchar(25)
)
insert into Levels(level1, level2, level3)
select * from tables --here you would put the select statement that you used to create the information. If you dont have this script then let me know

Sql data from row to column with reference to another column

Parent table
+====+===========+
| id | firstname |
+====+===========+
| 1 | abc |
+----+-----------+
| 2 | bcd |
+----+-----------+
| 3 | cde |
+----+-----------+
StudentRelationship table
+==========+==========+===========+
| relation | parentid | studentid |
+==========+==========+===========+
| father | 1 | s0001 |
+----------+----------+-----------+
| mother | 2 | s0001 |
+----------+----------+-----------+
| father | 3 | s0002 |
+----------+----------+-----------+
STUDENT table
+=======+===========+==========+=========+======+
| id | firstname | lastname | address | sex |
+=======+===========+==========+=========+======+
| s0001 | shdj | khb | jxx | male |
+-------+-----------+----------+---------+------+
It would be great if you could help me create a query which will return studentid ,name,father name,mother name,sex,address.
Based on what you've posted, then updated in your comments, I think this should work for you. I am sure someone with more advanced SQL skills can post a more elegant way to do this. But this is what I came up with:
SELECT DISTINCT cte.studentid
,studentFirstName
,studentLastName
,father.fatherFirstName
,mother.motherFirstName
,sex
,address
FROM cte
LEFT JOIN father ON cte.studentid = father.studentid
LEFT JOIN mother ON cte.studentid = mother.studentid
The following is an example where a student (Jeff Jones) has two fathers (let's say one of them is the step-father):
A few recommendations here:
Take a course on SQL syntax fundamentals (any type MySQL, T-SQL, etc..)
Read about FROM and JOIN
When posting your question here, the table examples should have better test data. "asdfkj", "shdsf", "Asdjkfdjkf" are horribly hard to
use to test code against because there is no context of what you are
looking at. I realize you are just posting an example, and the context
of the rows is partly insignificant, but it just makes for easier
question answering, and doesn't scare off people who would want to
answer your question.
Here is an DEMO you can play with, that has reasonable data in the fields you've mentioned.

Using a table to lookup multiple IDs on one row

I have two tables I am using at work to help me gain experience in writing SQL queries. One table contains a list of Applications and has three columns -
Application_Name, Application_Contact_ID and Business_Contact_ID. I then have a separate table called Contacts with two columns - Contact_ID and Contact_Name. I am trying to write a query that will list the Application_Name and Contact_Name for both the Applications_Contact_ID and Business_Contact_ID columns instead of the ID number itself.
I understand I need to JOIN the two tables but I haven't quite figured out how to formulate the correct statement. Help Please!
APPLICATIONS TABLE:
+------------------+------------------------+---------------------+
| Application_Name | Application_Contact_ID | Business_Contact_ID |
+------------------+------------------------+---------------------+
| Adobe | 23 | 23 |
| Word | 52 | 14 |
| NotePad++ | 44 | 989 |
+------------------+------------------------+---------------------+
CONTACTS TABLE:
+------------+--------------+
| Contact_ID | Contact_Name |
+------------+--------------+
| 23 | Tim |
| 52 | John |
| 14 | Jen |
| 44 | Carl |
| 989 | Sam |
+------------+--------------+
What I am trying to get is:
+------------------+--------------------------+-----------------------+
| Application_Name | Application_Contact_Name | Business_Contact_Name |
+------------------+--------------------------+-----------------------+
| Adobe | Tim | Tim |
| Word | John | Jen |
| NotePad++ | Carl | Sam |
+------------------+--------------------------+-----------------------+
I've tried the below but it is only returning the name for one of the columns:
SELECT Application_Name, Application_Contact_ID, Business_Contact_ID, Contact_Name
FROM Applications
JOIN Contact ON Contact_ID = Application_Contact_ID
This is a pretty critical and 101 part of SQL. Consider reading this other answer on a different question, which explains the joins in more depth. The trick to your query, is that you have to join the CONTACTS table twice, which is a bit hard to visualize, because you have to go there for both the application_contact_id and business_contact_id.
There are many flavors of joins (INNER, LEFT, RIGHT, etc.), which you'll want to familiarize yourself with for the future reference. Consider reading this article at the very least: https://www.techonthenet.com/sql_server/joins.php.
SELECT t1.application_name Application_Name,
t2.contact_name Application_Contact_name,
t3.contact_name Business_Contact_name
FROM applications t1
INNER JOIN contacts ON t2 t1.Application_Contact_ID = t2.contact_id -- join contacts for appName
INNER JOIN contacts ON t3 t1.business_Contact_ID = t3.contact_id; -- join contacts for busName