Convert two columns to values from another table? - sql

so I have the following table:
personID grade classID
13 7 147
13 7 456
19 8 123
19 8 789
25 7 123
25 7 456
25 7 789
82 8 147
82 8 456
155 7 456
155 7 789
I would like to keep the grade column, but have the personID and classID be mapped to another table to get the personName and ClassName - each are different tables, but the one separate table with the person name also has person ID, and the one with the class name has the classID so it makes it easy.
The result table should still be 11 columns long, just replaced with the persons name and class name for each row.

Is this done with a join?
Yes. The base idea is:
select
p.personID,
p.personName,
g.grade,
c.classID,
c.className
from grades g
inner join persons p on p.personID = g.personID
inner join classes c on c.classID = g.classID
This assumes the following data structures:
persons:
personID
personName
classes:
classID
className
grades:
personID --> foreign key to persons(personID)
grade
classID --> foreign key to classes(classID)

Select * from
gradeTable g
JOIN
personTable p
ON g.PersonID = p.PersonID
JOIN
classTable c
ON g.ClassID = c.ClassID

Related

SQL select with three tables and foreign keys

I have three tables :
field:
f_id
f_start
f_end
1
10
20
2
15
25
3
5
10
person :
p_id
p_name
1
Roger
2
John
3
Alicia
affect :
id
fk_field
fk_person
1
2
1
2
1
2
3
3
3
And I would like to select the dates and the names associated to. Like this
p_name
f_start
f_end
Roger
15
25
John
10
20
Alicia
5
10
I'm new to SQL and I don't know if i have to use JOIN or not... Thanks
You must join all 3 tables on their related columns:
SELECT p.p_name, f.f_start, f.f_end
FROM person p
INNER JOIN affect a ON a.fk_person = p.p_id
INNER JOIN field f ON f.f_id = a.fk_field;
Depending on your requirement you may need LEFT instead of INNER joins, but for this sample data the INNER joins will do.

I don't have the right answer in SQL

List the project numbers (PR_NO) for projects that have only received parts that are stored in (P_CITY) the same city as the project (show output of the query).
Table: EMP
Primary Key: E_NO
E_NO E_NAME E_RATE E_DEPT
1 A $400.00
2 B $200.00 1
3 C $150.00 2
4 D $150.00 3
5 E $120.00 1
6 F $100.00 1
7 G $100.00 2
8 H $50.00 2
9 I $50.00 3
10 J $50.00 3
11 K $150.00 3
Table: PART
Primary Key: P_NO
P_NO P_NAME P_CITY
1 P1 NY
2 P2 NY
3 P3 LA
4 P4 SF
5 P5 LA
6 P6 NY
Table: PROJECT
Primary Key: PR_NO
PR_NO PR_MGR PR_DEPT PR_LOC
1 2 1 NY
2 3 2 LA
3 2 1 NY
Table: SUPPLIER
Primary Key: S_NO
S_NO S_NAME S_LOC
1 S1 NY
2 S2 NY
3 S3 LA
Table: SUPPLY
Primary Key: P_NO + PR_NO + S_NO
Foreign Key: P_NO references PART
Foreign Key: PR_NO references PROJECT
Foreign Key: S_NO references SUPPLIER
P_NO PR_NO S_NO QTY
1 1 1 111
1 1 2 112
1 1 3 113
1 2 1 121
1 2 2 122
1 2 3 123
1 3 1 131
1 3 2 132
1 3 3 133
2 1 1 211
3 1 1 311
4 1 1 411
5 1 1 511
6 1 1 611
Table: WORK
Primary Key: E_NO + PR_NO
Foreign Key: E_NO references EMP
Foreign Key: PR_NO references PROJECT
E_NO PR_NO HRS
2 1 10
3 2 20
5 1 20
5 2 20
5 3 20
6 1 10
6 2 10
select distinct P.PR_NO
from PROJECT P, PART PA
where PA.P_CITY = P.PR_LOC;
The right answer question should be :
PR_NO
3
This is the error:
PR_NO
1
3
2
In order to achieve this task, you also need to use Supply table as follows:
select s.pr_no
from supply s
inner join part p
on p.p_no = s.p_no
group by s.pr_no
having max(p.p_city) = min(p.p_city)
and min(p.p_city) = (select pr_loc
from project
where pr_no = s.pr_no
)
I tested this query with your example data and it returns project #3 as expected:
SELECT DISTINCT S.PR_NO
FROM
SUPPLY S
INNER JOIN PROJECT PR
ON S.PR_NO = PR.PR_NO
INNER JOIN PART P
ON S.P_NO = P.P_NO
WHERE
PR.PR_LOC = P.P_CITY AND
NOT S.PR_NO IN
(
SELECT S2.PR_NO
FROM
SUPPLY S2
INNER JOIN PROJECT PR2
ON S2.PR_NO = PR2.PR_NO
INNER JOIN PART P2
ON S2.P_NO = P2.P_NO
WHERE PR2.PR_LOC <> P2.P_CITY
)
The SUPPLY table creates the link between PART and PROJECT. The main query looks for parts that are stored in the same city as the project. The sub-select in the where clause looks almost the same, except that it looks for parts stored in a different city. Projects with such cities are excluded with NOT S.PR_NO IN (...).
The main issue in your query is that you should correctly join the 2 tables PROJECT and PART before filtering; so in this case you should use the table SUPPLY.
SELECT DISTINCT S.PR_NO
FROM SUPPLY S
INNER JOIN PROJECT PR
ON S.PR_NO = PR.PR_NO
INNER JOIN PART P
ON S.P_NO = P.P_NO
WHERE PR.PR_LOC = P.P_CITY
AND S.PR_NO NOT IN ( SELECT DISTINCT S.PR_NO
FROM SUPPLY S
INNER JOIN PROJECT PR
ON S.PR_NO = PR.PR_NO
INNER JOIN PART P
ON S.P_NO = P.P_NO
WHERE PR.PR_LOC <> P.P_CITY );
Hope this helps!

How to select record in 1 to many relation

I have a contact table and contactphone tables.
Each record in contacts can have many records in contactphone but only 1 can be primary at most (primary is Boolean but can be also NULL)
Contacts:
cid name address
1 Jack A
2 Bill B
3 Mor C
4 Jeff D
ContactsPhones:
cpid cid phone primary
1 1 140 True
2 1 150
3 2 170
I need to write a query that returns all contacts with their details, if they have more than 1 phone then present only primary.
Output:
cid name address phone
1 Jack A 140
2 Bill B 170
3 Mor C
4 Jeff D
What is most easiest way to do it?
Note that primary can have (True,False, Null)
This is a bit tricky. I think the easiest method is distinct on:
SELECT DISTINCT ON (cid) cid, name, address, cp.phone
FROM Contacts c LEFT JOIN
ContactsPhones cp
ON c.cid = cp.cpid
ORDER BY cid, (case when primary then 1 else 2 end);
You can use a LEFT JOIN to a derived table that uses ROW_NUMBER to specify more suitable record:
SELECT cid, name, address, phone
FROM Contacts AS c
LEFT JOIN (
SELECT phone, cpid,
ROW_NUMBER() OVER(PARTITION BY cpid
ORDER BY CASE
WHEN primary THEN 0
ELSE 1
END) AS rn
FROM ContactsPhones) AS cp
ON c.cid = cp.cpid AND cp.rn = 1

fetch parent category name if searching word is found subcategory of category master

i want to fetch parentcategory name.
SELECT u.id,u.fullname,u.email,u.ServiceDescription,
u.Skills,c.Name,r.Reviews,r.RatingValue,ru.Id,
ru.Fullname FROM UserDetails u
INNER JOIN VendorInCategory v ON v.VendorId=u.Id
INNER JOIN CategoryMaster c ON v.CategoryId=c.Id
left join Review r
on r.VendorId = u.id
left JOIN UserDetails ru ON r.CustomerId = ru.id
WHERE ((u.ServiceDescription LIKE '%Plaster%') OR (u.Skills LIKE '%Plaster%') OR
(c.Name LIKE '%Plaster%'))
ORDER BY ISNULL(r.RatingValue, 0) DESC;
now as you can see from below link my database design:
calculate average rating in sql server
here you can see plaster is subcategory of Construction(Id:40)
so i want to fetch parentcategory if any word i am searching for is found in categorymaster.
if word is found in subcategory then fetch its parentcategory name else fetch parentcategory name only.
can anybody please edit my query to achieve this functionality???
UserDetails:
id Name Servicedescription Skills
1 john Plaster plaster
2 abc construction construction
3 xyz plaster plaster
4 pqr null null(not vendor)
5 lmn null null(not vendor)
Review
id CustomerId Vendorid rating reviews
1 4 1 3 fdd
2 5 1 3 dfg
Now if i am seraching for "Plaster"
then output should be:
VendorId ServiceDescription Skills rating customername reviews ParentCategory
1 plaster plaster 3 pqr fdd construction
3 plaster plaster 3 lmn dfg Construction
here Construction(Id 40) is the ParentCategory of Plaster(Id 44) as u can see in image.if plaster does not have parentid then ParentCategory name should be plaster only.
Try this..
SELECT u.id,u.fullname,u.email,u.ServiceDescription,
u.Skills,c.Name,r.Reviews,r.RatingValue,ru.Id,
ru.Fullname,ISNULL(c1.Name,c.Name) AS CategoryName FROM UserDetails u
INNER JOIN VendorInCategory v ON v.VendorId=u.Id
INNER JOIN CategoryMaster c ON v.CategoryId=c.Id
LEFT JOIN CategoryMaster c1 ON c1.Id=c.ParentID
left join Review r
on r.VendorId = u.id
left JOIN UserDetails ru ON r.CustomerId = ru.id
WHERE ((u.ServiceDescription LIKE '%Plaster%') OR (u.Skills LIKE '%Plaster%') OR
(c.Name LIKE '%Plaster%'))
ORDER BY ISNULL(r.RatingValue, 0) DESC;
The below query will give parent:
SELECT c.Name AS Name,c1.Name As Parent FROM CategoryMaster c
LEFT JOIN CategoryMaster c1 on c1.ParentID=c.ID
Use Common table expression (CTE)
Recursive CTE calls
Sample User table structure
userId userName managerId
----------- ---------------- -----------
1 John NULL
2 Charles 1
3 Nicolas 2
4 Neil 5
5 Lynn 1
6 Vince 5
7 Claire 6
In order to get all top level managers name we need to use CTE recursive call.
WITH UserCTE AS (
SELECT userId, userName, managerId,0 AS steps
FROM dbo.Users
WHERE userId = 7
UNION ALL
SELECT mgr.userId, mgr.userName, mgr.managerId, usr.steps +1 AS steps
FROM UserCTE AS usr
INNER JOIN dbo.Users AS mgr
ON usr.managerId = mgr.userId
)
SELECT * FROM UserCTE AS u;
The above query will return all the managers name of userid = 7. Below is the output
userId userName managerId steps
----------- ---------------- ----------- -----------
7 Claire 6 0
6 Vince 5 1
5 Lynn 1 2
1 John NULL 3

SQL select a parent with children only in

if you have parent table
create table parent (
pid int not null,
name varchar(255)
)
and a parent-child join table
create table parent_child (
pid int not null,
cid int not null,
foreign key (pid) references parent(pid),
foreign key (cid) references child(cid)
)
create table child(
cid int not null,
name varchar(255)
)
How can I find all parent's names where all their children have names in the following list ('dave','henry','myriam','jill').
I don't want to see a parent if they have a child with a different name, but if they have 1 or more children and all their children have names in the list I want to see the parent's name.
I did find this https://stackoverflow.com/a/304314/1916621 that will help me find a parent with children of the exactly those names, but I can't figure out how to the parents who only have children with names in a subset of that list.
Extra points if someone knows performance tradeoff for different approaches.
SELECT
p.pid,
p.name
FROM
parent p
WHERE NOT EXISTS (
SELECT *
FROM
parent_child pc
JOIN child c
ON pc.cid = c.cid
AND c.name NOT IN ('dave','henry','myriam','jill')
WHERE
p.pid = pc.pid
) AND EXISTS (
SELECT *
FROM
parent_child pc
JOIN child c
ON pc.cid = c.cid
AND c.name IN ('dave','henry','myriam','jill')
WHERE
p.pid = pc.pid
)
Another method... no sub-queries, but additional DISTINCT needed to eliminate duplication of parent records from joining to the parent_child table.
SELECT DISTINCT
p.pid,
p.name
FROM
parent p
JOIN parent_child pc_exists ON pc_exists.pid = p.pid
JOIN child c_exists
ON c_exists.cid = pc_exists.cid
AND c_exists.name IN ('dave','henry','myriam','jill')
LEFT JOIN parent_child pc_notExists ON pc_notExists.pid = p.pid
LEFT JOIN child c_notExists
ON c_notExists.cid = pc_notExists.cid
AND c_notExists.name NOT IN ('dave','henry','myriam','jill')
WHERE
c_notExists.cid IS NULL
Here is my moderate bet:
Sample tables:
Parent
PID NAME
1 dad john
2 mum sandy
3 dad frank
4 mum kate
5 mum jean
Child
CID NAME
11 dave
22 maryam
33 henry
44 maryam
16 jill
17 lina
23 jack
34 jill
55 dave
Parent_Child
PID CID
1 11
1 16
1 17
2 22
3 33
4 44
2 23
5 55
3 34
Query:
select p.pid, p.name,
group_concat(c.name) as children
from parent as p
inner join parent_child as pc
on p.pid = pc.pid
join child as c
on pc.cid = c.cid
where c.name
in ('dave','henry','maryam','jill')
group by p.pid
;
Results:
PID NAME CHILDREN
1 dad john dave,jill
2 mum sandy maryam
3 dad frank henry,jill
4 mum kate maryam
5 mum jean dave
SQLFIDDLE
Using REGEXP and GROUP_CONCAT
It is something much better than in or find_in_set for SQL. The change I did, I used the list as a comma delimitted string ;)
*But the issue here: that group_concat string order has to be found in the comman delimitted string.* Unless we make the REGEXP much efficient :)
Query:
select x.pid, x.name,
x.children from(
select p.pid, p.name,
group_concat(c.name) as children,
count(c.name) as counts
from parent as p
inner join parent_child as pc
on p.pid = pc.pid
join child as c
on pc.cid = c.cid
group by p.pid) as x
where 'dave,maryam,henry,jill'
REGEXP x.children
;
Results:
PID NAME CHILDREN
3 dad frank henry,jill
4 mum kate maryam
5 mum jean dave
*SQLFIDDLE