Select name and surname in sql - sql

So I have four tables:
Team:
(PK) ID
Name
Player:
(PK) ID
(FK) Person
PlayerContract:
(PK) ID
(FK) Player
(FK) Team
Person:
(PK) ID
Name
Surname
I have to select all names and surnames of players which are in team X. How can I do it? I wanted to do this in this way:
SELECT
name,
surname
FROM
Person
WHERE
ID = SELECT Person
FROM Player
WHERE ID = SELECT Player
FROM PlayerContact
WHERE Team = SELECT ID
FROM Team
WHERE Name = "X";
Is it ok?

No, it's not ok, you can easily test your code. Anyway, what you need to do is to JOIN your tables:
SELECT pe.Name,
pe.Surname
FROM Player AS pl
INNER JOIN PlayerContract AS pc
ON pl.ID = pc.Player
INNER JOIN Team AS t
ON pc.Team = t.ID
INNER JOIN Person AS pe
ON pc.Person = pe.ID
WHERE t.Name = 'X'

It would work is you enclosed the subqueries in parentheses:
SELECT
name,
surname
FROM
Person
WHERE
ID = (SELECT Person
FROM Player
WHERE ID = (SELECT Player
FROM PlayerContact
WHERE Team = (SELECT ID
FROM Team
WHERE Name = "X")));
Note that the results may be different from a JOIN, for example, if a player is on more than one team (in which case the query above would not work since the second subquery would return two records).

Related

how can I write a postgresql query to find someone's cousin in database?

Person(ID, name, gender, fatherID, motherID, spouseID);
This is my database columns.
for example if id = 5 how can I find this person's cousins?
i have to use just person table. And cousin means someone's mother's and father's siblings' children.
I try to use nested queries but it was too many query to follow for the result.
For example that query find someone's siblings
SELECT name
FROM person
WHERE motherid = (SELECT motherid
FROM person
WHERE id = x)
AND fatherid = (SELECT fatherid
FROM person
WHERE id = x)
EXCEPT
(SELECT name FROM person WHERE id = x);
Maybe joining to the parents of the parents, then back to their children's children.
(untested notepad scribble)
SELECT DISTINCT
kid.name as kid,
cousin.name as cousin
FROM person kid
LEFT JOIN person AS parent
ON parent.id IN (kid.fatherid, kid.motherid)
LEFT JOIN person AS grandparent
ON grandparent.id IN (parent.fatherid, parent.motherid)
LEFT JOIN person AS auntcle
ON grandparent.id IN (auntcle.fatherid, auntcle.motherid)
AND auntcle.id != parent.id
LEFT JOIN person AS cousin
ON auntcle.id IN (cousin.fatherid, cousin.motherid)
WHERE cousin.fatherid != kid.fatherid AND cousin.motherid != kid.motherid -- redneck check

SQL query - list of items in one table not in another

I need some help with a SQL query. I have a table of courses and a table that contains user id and course id, denoting courses that user has taken (might not have taken any; no entry in that table for that user id).
I need a query to return the list of courses not taken.
Course Category table
CategoryID
Caegory
Courses table
CourseID
CategoryID
CourseName
...
UserCourse table
UserID
CourseID
you can use not exists
Select *
From Courses c
Where Not Exists (Select 1 From UserCourse uc Where uc.CourseID = c.CourseID)
This will just list the course
select *
from Courses C
Left join CourseCategory cc on
cc.CategoryID = c.CategoryID
where CourseID not in (Select CourseID from UserCourse where UserID = 14)
what i need is for a given user id and course category, what courses within that category have not been taken by this user
(This should have been in the request by the way.)
So:
Select from courses.
Limit to the desired category.
Limit to courses not in the set of courses taken by the user.
The query:
select *
from courses
where categoryid = 123
and courseid not in (select courseid from usercourse where userid = 456);
Another way of writing same query, which will perform faster.
select C.CourseID,C.CategoryID
from Courses C
Left join CourseCategory cc on
cc.CategoryID = c.CategoryID
left join UserCourse uc
on C.CourseID=uc.CourseID
where uc.CourseID is null

Most efficient SQL for this example

Table A: Person: id, name
Table B: Toys: id, person_id, toy_name
I have a search screen that includes a dropdown of fixed toy names.
A search is found if a subset of the total set of toys for a person is matched.
Example, a person name=bob has toys: doll, car, house, hat
A search is done for person name=bob and toys=doll, hat.
I want to return bob and ALL of his toys, not just what toys were searched for(doll, hat).
Bob is found because a subset of his toys are a match.
I don't know what the most efficient/least db calls way to accomplish this.
I can do a search for bob and get all of his toys, then parse through the result set to see if the searched for toys find a match, but that seems wrong, that the db call could return rows for which no match is found (and that seems wrong?).
okay,
select
p.id,
p.name,
t.id as toyid,
t.toy_name
from
person p
join
toys t
on p.id = t.person_id
where
p.id in (
select person_id from toys where toy_name = 'doll'
intersect
select person_id from toys where toy_name = 'hat');
Fiddle Here
If you normalise your schema a little further,
create table Person
(
Id int,
Name varchar(100)
);
create table Toy
(
Id int,
Name varchar(100)
);
create table PersonToy
(
Id int,
PersonId int,
ToyId int
);
It should make the complexity of the problem clearer. It will also save some space. A statement of the form,
select
p.Name PersonName,
t.Name ToyName
from
Person p
join
PersonToy pt
on pt.PersonId = p.Id
join
Toy t
on t.Id = pt.ToyId
where
p.Id in
(
select PersonId from PersonToy where ToyId = 1
intersect
select PersonId from PersonToy where ToyId = 4
);
will work efficiently.
Updated Fiddle
Here's one way to do it using a subquery and checking for the existence of Hat and Doll in the HAVING clause:
select p.id, p.name,
t.id as toyid, t.name as toyname
from person p
inner join toys t on p.id = t.person_id
inner join (
select person_id
from toys
group by person_id
having sum(name = 'hat') > 0 and
sum(name = 'doll') > 0
) t2 on p.id = t2.person_id
SQL Fiddle Demo

SQL to select a table through a generic relationship

In a relational database I have three tables. Using SQL Server.
person(id, type)
student(id, person_id, type, student specific fields)
teacher(id, person_id, type, teacher specific fields)
Student and teacher are both people, therefore a student will have a record in both the person and student tables, as will the teacher. Student and teacher have foreign keys to person. Student and teacher have different field definitions therefore a union will NOT work.
Now I have the person's id and depending on whether the person is a student or teacher I would like to select * from the relevant table (not person).
For example, if the person is a student I would like my query to select the student table.
I can think of a few inefficient methods but I am looking for the optimum one.
I would suggest a UNION
SELECT student.*
FROM student
WHERE person_id= #id
UNION
SELECT teacher.*
FROM teacher
WHERE person_id= #id
if exists(select person_id from student where person_id = #id)
select * from student where person_id = #id
else
if exists(select person_id from teacher where person_id = #id)
select * from teacher where person_id = #id
If your RDBMS is SQLServer, then I would abstract a view along the lines of podiluska's union, mapping out specific fields in Student and Teacher to common names, and padding with NULLs where no mapping is possible
And assuming that Students and Teachers inherit from person (i.e. both are 0..1 to 1 with Person), then they can share the same primary key, i.e. no need for new surrogates keys on Teacher and Student.
I've assumed that person.type determines whether the person is a Student(S) or Teacher(T).
CREATE VIEW SubClassesOfPerson AS
SELECT p.id as PersonId,
p.name as PersonName,
p.OtherBaseFieldsHere,
s.SomeStudentSpecificField AS MappedField1,
s.SomeStudentSpecificFieldX AS MappedFieldX,
s.SomeStudentSpecificField as MappedFieldForStudentOnly,
NULL as MappedFieldForTeacherOnly -- Pad this because it can't be mapped
FROM person p
INNER JOIN student s
on s.person_id = p.id AND p.type = 'S'
UNION
SELECT p.id as PersonId,
p.name as PersonName,
p.OtherBaseFieldsHere,
t.SomeTeacherSpecificField AS MappedField1,
t.SomeTeacherSpecificFieldX AS MappedFieldX,
NULL as MappedFieldForStudentOnly, -- Pad this because it can't be mapped
t.SomeTeacherSpecificField as MappedFieldForTeacherOnly
FROM person p
INNER JOIN teacher t
on t.person_id = p.id AND p.type = 'T'

SQL Multiple Duplicate Row Detection

I'm trying to determine a correct way to isolate rows within a table that have the same values in 2 columns.
There are two tables, one (Name) with the person's names and IDs, and the other one (Nation) with people's IDs and their nations. I join the two tables with inner join, and now the new table columns consist of an ID, first name, last name, and nation. If I want to find pairs of people who have the same last name and are from the same nation, why isn't
select ID, FName, LName, Nation
from (Name inner join Nation on Name.ID = Nation.ID)
group by Name, Nation
having count(Name) > 1 and count(Nation) > 1
working?
I'm aiming for the result to be a table with columns:
ID -------First--------------- Last ---------Nation
where the last names and nations will be identical pairs while first names will be different.
I feel like the group by part isnt appropriate, but is there even an alternate way? Thanks for any help.
If you are using MS SQL Server:
select
*
from
(
select
Name.*,
Nation.Nation,
cnt = count(*) over(partition by LName, Nation)
from Name
join Nation on Nation.ID = Name.ID
) t
where cnt > 1
Try this:
SELECT * FROM (
SELECT Name.ID, Name.FName, Name.LName, Nation.Nation
FROM Name
INNER JOIN Nation ON (Name.ID = Nation.ID)
) a
INNER JOIN (
SELECT Name.ID, Name.FName, Name.LName, Nation.Nation
FROM Name
INNER JOIN Nation ON (Name.ID = Nation.ID)
) b ON (a.LName = b.LName AND a.Nation = b.Nation)
WHERE a.ID < b.ID
As Simon Righarts hinted, something's not right with the design.
Scenario 1)
If a name can have multiple nations, you would have 3 tables implementing an n:m relationship.
CREATE TABLE name (name_id int, name text, ...);
CREATE TABLE nation (nation_id int, nation text, ...);
CREATE TABLE nationality (name_id int references name(name_id)
,nation_id int references nation(nation_id)
... );
Query for the scenario:
SELECT a.name_id, a.fname, a.lname, n.nation
FROM name a
JOIN nationality na USING (name_id)
JOIN nation n USING (nation_id)
JOIN (
SELECT a.lname, na.nation_id
FROM name a
JOIN nationality na USING (name_id)
GROUP BY 1,2
HAVING count(*) > 1) x USING (lname, nation_id)
Scenario 2)
If a name can only have one nation, there would be a column nation_id in the table name:
CREATE TABLE name (name_id int
,name text
,nation_id int references nation(nation_id), ...);
CREATE TABLE nation (nation_id int, nation text, ...);
Query for this scenario:
SELECT a.name_id, a.fname, a.lname, n.nation
FROM name a
JOIN nation n USING (nation_id)
JOIN (
SELECT a.lname, a.nation_id
FROM name a
GROUP BY 1,2
HAVING count(*) > 1) x USING (lname, nation_id);
All multiple occurrences are included here, not just "pairs" - assuming you meant that.
Your actual description doesn't fit either scenario.