SQL : get one particular book owner - sql

I have a Student table with columns like this:
| email (PK) | name |
I have a book table with columns as such:
| bookid(PK) | title |
I have a copy table which have copies of books people own
| emailofOwner(FK to student.email) | bookid(FK to book.bookid) |
A student can of course own multiple books. My aim is to find names of students who own only 1 such book and nothing else BUT with a bookid = 3;
My attempt to get people who own only 1 book.
select c.emailofOwner
from copy c
group by c.emailofOwner
having count(*) = 1 ;

SELECT t1.name
FROM student t1
INNER JOIN
(
SELECT emailofOwner
FROM copy
GROUP BY emailofOwner
HAVING COUNT(DISTINCT bookid) = 1 AND MAX(bookid) = 3
) t2
ON t1.email = t2.emailofOwner
The above query uses a subquery to restrict to students who own one and only one book whose ID is 3. The subquery is identical to your attempt except that it adds the restriction that the max book ID is 3. In this case, since there will only be one book per retained group, this is simply checking the value of the book ID.

To get students with only
select s.name, s.email, count(*) as numBooks
from student s, copy c
where s.email = c.emailOfOwner
group by email
having count(*) = 1
And people with book 3 and ONLY book 3:
select s.name, s.email, count(*) as numBooks
from student s, copy c
where s.email = c.emailOfOwner
group by email
having count(*) = 1 and min(bookId) = 3;
Check out this SQL Fiddle.

Related

How to make a query with a collection in the select statement (not in the where clause)

I have 2 tables. The parent table (parent) has a one-to-many relation to child table.
The parent table (Pa_Projects_all) and the child pa_project_players
They are joined by person_id
Let's say I have a project (project_id = 1001)
Project Id Project Name
1001 This is a project
The Project Players has
The query should have something like this
Select Project_Id, Person_id, Name
from Pa_projects_all a, Pa_project_players b
where a.person_id = b.person_id
and a.project_id = 1001;
The expected result is
Project Id Person Id Name
1001 500 John Smith
501 Peter Carpenter
502 Steve Sun
Where the column Person Id and Name are what I think they should be a collection.
The query will go in a data definition for XML Publisher and the result will be in Excel.
So in Excel Column A = Project Id, Column B = Person ID, Column C = Name.
One row in Excel for the result.
in other words, one Excel row per project id.
Is this possible?
If you want to have this report in the SQL*Plus then you can use the BREAK ON statement.
SQL> break on Project_Id
SQL> Select Project_Id, Person_id, Name
2 from Pa_projects_all a, Pa_project_players b
3 where a.person_id = b.person_id
4 and a.project_id = 1001;
If you want to have it in any tool then you can use the analytical function ROW_NUMBER as follows:
SELECT CASE WHEN RN = 1 THEN Project_Id END AS Project_Id, Person_id, Name FROM
(Select Project_Id, Person_id, Name,
ROW_NUMBER() OVER (PARTITION BY Project_Id ORDER BY Person_id) AS RN
from Pa_projects_all a, Pa_project_players b
where a.person_id = b.person_id
and a.project_id = 1001)
ORDER BY RN;

How to resolve an id by an foreign table

I want to resolve an ID by another table, where the name of this id is stored.
SELECT d.id_data, string_agg(s.name_last,', ') AS authors, d.title, i.name
FROM data d, institution i, staffs s
WHERE d.id_staffs = s.id_staffs
AND d.id_institution = i.id_institution
GROUP BY d.id_data limit 100 ;
But how can I get the name of my Institution. I want that the SELECT shows me the institution name, which has stored the data. Something like that
id_data | authors | title | name
----------------------------------------------------------------
1 |Mustermann, Musterfrau | sunmaker | university cologne
2 |Schmidt, Müller | dry age | university berlin
I just need to resolve the id of the institution to his name.
Always use proper, explicit, standard JOIN syntax. Never use commas in the FROM clause.
Presumably, you want something like this:
SELECT d.id_data, string_agg(s.name_last,', ') AS authors,
d.title, i.name
FROM data d JOIN
institution i
ON d.id_staffs = s.id_staffs JOIN
staffs s
ON d.id_institution = i.id_institution
GROUP BY d.id_data, d.title, i.name
LIMIT 100 ;
That is, fix the GROUP BY to have all the unaggregated columns.

Oracle SQL - Return rows with a value based on multiple values in a second field

I need to return the rows that contain the employee names (in one field) who are only classified as Managers (not as Workers, or as Managers and Workers).
Managers and Workers values are in a second field.
So it might look like this:
+----------+------------+
| 'Miller' | 'Manager' |
| 'Jones' | 'Manager' |
| 'Jones' | 'Worker' |
+----------+------------+
In this instance I just want it to return 'Miller'.
I can get one or both, but not the ones where an employee is only classified as a Manager.
Any thoughts?
One method uses aggregation:
select name
from t
group by name
having min(classification) = max(classification) and min(classification) = 'manager';
Count the number of titles. If they have a title of 'Manager' and there's only one title, select the individual:
SELECT *
FROM PEOPLE p
INNER JOIN (SELECT NAME, COUNT(TITLE) AS TITLE_COUNT
FROM PEOPLE
GROUP BY NAME) c
ON c.NAME = p.NAME
WHERE p.TITLE = 'Manager' AND
c.TITLE_COUNT = 1;
dbfiddle here
Method with a subquery which should work well when there are not only 'Managers' and 'Workers' in the table:
SELECT t1.name FROM t t1
WHERE
t1.classification='Manager'
AND NOT EXISTS (
SELECT 1 FROM t t2 WHERE t1.name=t2.name AND t2.classification='Worker'
)

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

Sql Query Join in Oracle

I have Parent table and multiple child tables with foreign key constraint.
School Table
School ID EduDetails Genders Address_id EDUTYPE
1 2 M 3 FGN
And the child tables like
Education Details
EDU ID EducationType
2 Online
AKA Name
School Id AKA Name
1 Test School
1 School Test
Gender Table
Gender ID Gender Desc
M Male
I am using Left outer join for the parent and school table to fetch the results.
But My issue is, If AKA table has 5 counts matching the school Id and Gender table has only 1 records for that school Id.
So the results comes with 5 duplicate rows with school Information and also other child table information.
Is there any workaround to fix this issue. I tried using subquery and row_number over by function. But it is not working for me. Can anybody help me to solve this issue.
Thanks in advance for your time in looking this issue.
My required output should be like this
School_id AKA Name GenderDesc EductaionType
1 Test School Male Online
1 School Test
So I need to have Null values for the not matching records.
Since you want all the records in the AKA Name table, I've joined on that getting a Row_Number for each row. Then using that Row_Number, LEFT JOIN on the other tables.
SELECT S.SchoolId,
SA.AKAName,
G.GenderName,
ED.EducationType
FROM School s
JOIN
(SELECT SchoolId,
AKAName,
ROW_NUMBER() OVER (PARTITION BY SchoolId ORDER BY AKAName) rn
FROM SchoolAKA
) SA ON S.SchoolID = SA.SchoolId
LEFT JOIN
(SELECT EDUID,
EducationType,
ROW_NUMBER() OVER (ORDER BY EducationType) rn
FROM EduDetails
) ED ON S.EDUID = ED.EDUID AND SA.rn = ED.rn
LEFT JOIN
(SELECT GenderId,
GenderName,
ROW_NUMBER() OVER (ORDER BY GenderName) rn
FROM Genders
) G ON S.GenderId = G.GenderId AND SA.rn = G.rn
Here is the SQL Fiddle.
And here are the results:
SCHOOLID AKANAME GENDERNAME EDUCATIONTYPE
1 School Test Male Online
1 Test School (null) (null)