Delete entry from 2 tables - sql

I got 2 tables:
STUDENT (ID, name, surname, class)
SCORES (student_id, score_date (you can choose a date format), score , discipline)
I want to delete student Ionescu(and related entries from Score table)
I tried this
DELETE SCORE,STUDENT FROM STUDENT INNER JOIN SCORE
WHERE Score.StudentID=Student.Id
AND Student.Name = 'Ionescu';
and it's not working. How can I fix it?

You can delete the entries one by one, like this:
DELETE FROM SCORE WHERE score.StudentID in (select id from student where student.name = 'Ionescu');
This also makes the SQL-Query easier to read :-)
Ideally, you used the "ON DELETE CASCADE"-clause when creating the SCORE-Table - this would allow you to not worry about that SQL at all anymore, because the related entry will be deleted automatically as well. - Check this site for more Informations about Cascade: https://kb.objectrocket.com/postgresql/how-to-use-the-postgresql-delete-cascade-1369

You might want to consider a cascading delete constraint. But in the meantime, you can do this in one statement using returning:
with d as (
delete from student s
where s.name = 'Ionescu'
returning *
)
delete from score sc
where sc.studentid in (select d.id from d);

Related

SQL question: how to find rows that share all of the same rows in a composite table?

I'm working on my SQL project using the Oracle database for class, and I'm asked a question that I see far too often.
You have three tables:
STUDENT: SNO, SNAME
CLASS: CNO, CNAME
ATTENDANCE: SNO, CNO, Grade
The question I keep finding is of a similar type: Find the names of the students that attend in all of the classes that "John" (or anyone else) attends.
John attends three classes, so I have to find the students that also attend those three classes (could be more, but those three must be there). However, I won't always know how many classes John (or whoever) attends, so it can't be hardcoded like that.
SELECT jclass.CNO
FROM attendance jclass
INNER JOIN student on jclass.SNO = student.SNO
WHERE student.SNAME = 'John';
This gets me the classes that John attends. I tried to add the identifier for the other students:
SELECT student.SNAME
FROM student
INNER JOIN attendance on student.SNO = attendance.SNO
INNER JOIN class on attendance.CNO = class.CNO
WHERE student.SNAME <> 'John'
AND class.CNO IN (SELECT jclass.CNO
FROM attendance jclass
INNER JOIN student on jclass.SNO = student.SNO
WHERE student.SNAME = 'John');
However, this only gets me the students that appear in at least one of John's classes, rather than all of them. I can see why it's doing this, but I'm not sure how to fix it. It's the one big struggle I'm having with SQL.
Here is one way - assuming SNO is primary key in the first table, CNO is primary key in the second table, and (SNO, CNO) is (composite) primary key in the third table, and that the input student is given by a unique identifier (first name is distinctly NOT a unique identifier, so the problem stated in terms of giving "John" as the input makes no sense). Here I assume the "special" student is identified by SNO = 1001; you can make 1001 into a variable, or change it to a subquery that selects a (unique!!) SNO based on some other inputs.
I didn't try to make the query as efficient as possible, or use features you most likely haven't seen in your class. Rather, I tried to make it as elementary and as readable as possible.
select sno
from attendance
where cno in (select cno from attendance where sno = 1001)
group by sno
having count(*) = (select count(*) from attendance where sno = 1001)
;
The strategy is simple: the subquery in the in condition finds the classes attended by the "special" student, then from the attendance table we select only rows for those classes. Group by student, and count. Keep only the students for whom the count is equal to the total count for the "special" student. Note the last condition is about groups, not about input rows, so it belongs in the having clause.

How to query in a way that relates a name to an ID and then using that ID to sum up what comes under that ID

I'm a student learning SQL. With querying, if you are given data that you have to relate to a primary key in the same table that will then be used to identify data in another table, how do you query that? I only want a push in the right direction because I don't want to copy and paste someone's code.
For example:
You are to find out how many staff are being managed by a manager and you are to find this by using the managers first and last name (which is not the primary key). The database is below. ManagerNo and StaffID are the same.
Branch (BranchNo, ManagerNo)
Staff (staffID, fName, lName, position, BranchNo)
Thank you for your time
You would use two joins:
select s.*
from staff s join
branch b
on s.BranchNo = b.BranchNo join
staff sm
on sm.staffId = b.ManagerNo
where sm.fname = ? and sm.lname = ?;

oracle sql dev, Using a subquery, List buildings where user has no interest

I am currently trying to get the database to list a buildings, BuildingNum, BuildingName and instname that have a user who has not Interest(interest.description = null).
(PK) = Primary Key
(FK) = Foreign Key
The database schema is as follows:
Building(buildingNum(PK), Description, instname, buildName, state, postcode)
User(UNum(PK), buildingNum(FK), Surname, FirstName, initials, title)
File(FileNum(PK), title)
UserAccount(FileNum(PK)(FK), UNum(PK)(FK))
Job(JobNum(PK), id, title)
Interest(JobNum(PK)(FK), UNum(PK)(FK), Description)
So far i have tried the following block of code:
select B.buildingNum, B.BuildName, B.instname
from Building B join User U
where B.deptNum = U.deptNum in (select I.Description
from interest I
where description = null);
I'm struggling with how to do this using a sub query, all i receive is an error as this doesn't work. Im not sure if i should be using the join like that or if i have added the subquery correctly. Thanks to anyone who can help.
If you want no interests, use exists:
select b.buildingNum, b.BuildName, b.instname
from Building b
where exists (select 1
from users u left join
interest i
on i.unum = u.unum
where b.deptNum = u.deptNum and
i.unum is null -- no interests
);
The subquery returns users (in a given building) that have no interests. The exists is simply saying that at least one exists.
As a note: = null is never used for comparisons. It never returns a true value. The correct syntax is is null.

How to select one field based on another field, and then subtract any with more than 1 result?

Ok, so I have been looking everywhere and i cant seem to find anything.
Heres what Im trying to do:
SELECT SFirstName, SLastName
FROM Advisors
WHERE Students <= ('1') and Students.AdvisorID=Advisors.AdvisorID;
The SName is student names, and i need to basically list advisors based on the number of active students for each, and then filter out any with more than one student.
Heres an attempt to code this with the DBO names and all.
SELECT AFirstName, ALastName
FROM DBO.Students, DBO.Advisors
WHERE Advisor.AdvisorID=Student.AdvisorID AND >1;
Basically the advisorID is a Foreign Key within the Students table, and I need to match it with the advisor table and then the >1 statement. I dont know how to reduce results by number yet however.
Whenever I try this it tells me it cannot find the advisor.advisorID or the student.AdvisorID. how would i do this while still using the foreign key from one and the primary key from the other to cross check matches.
If I understand you question correctly, you want to list the Advisors with less than 1 Students. If so, what you need is a LEFT JOIN coupled with COUNT and HAVING:
SELECT
a.AFirstName, a.ALastName
FROM dbo.Student s
LEFT JOIN dbo.Advisors a
ON a.AdvisorID = s.AdvisorID
GROUP BY
a.AdvisorID, a.AFirstName, a.ALastName
HAVING
COUNT(s.AdvisorID) <= 1
Notes:
Avoid using the old-style JOIN syntax.
Alias your tables to improve readability.

Query two tables linked to same primary key

long time reader first time poster.
I have what looks like a straight forward Access question.
I've an Employee table with these fields;
|EmployeeID|StaffName|Phone|Email|
EmployeeID is a foreign key in two tables - one for logging the status of a generator ie
|LogID|Status|Logged By|
and one for any incidents with the generator
|IncidentID|Description|Raised By|LogID|
I can create a query to link incidents and their log details eg
|IncidentID|Description|Raised By|LogID|Logged By|
This works if I put the EmployeeID number into 'Logged By' and 'Raised By' - if I put the StaffName - as the company wants, it doesn't work because it's selecting two StaffNames from the same primary key.
Any way around this?
Here is the Access code, the part where LoggedBy and RaisedBy are joined to EmployeeID is in bold.
SELECT [2_Incidents].IncidentID, [2_Incidents].Description, [8_Employees].StaffName AS [Raised By], [1_Log].LogID, [8_Employees].StaffName AS [Logged By]
FROM 8_Employees INNER JOIN (2_Incidents INNER JOIN 2_Incidents ON [1_Log].LogID = [2_Incidents].LogID) ON [8_Employees].EmployeeID = [1_Log].LoggedBy AND [8_Employees].EmployeeID = [2_Incidents].RaisedBy);
I've gotten around this by creating a copy of the Employees table but this could get awkward if there are more fields linked to Employees in the future (eg Closed By, Verified By etc)
If I understand you correctly, the solution is to join to the employee table twice.
In your log details you are doing something like SELECT * FROM incident JOIN log USING (LogID)...
You mention "selecting two StaffNames from the same primary key" - I take it that you are attempting to join Employee somehow with Raised By and Logged By.
If you actually join to two copies of employee: SELECT ... FROM incident JOIN log USING (LogId) JOIN employee ei ON (ei.employeeid = incident.raised_by) JOIN employee el ON (el.employeeid = log.logged_by) you can reference then the ei.staffname (incident raise by employee) and el.staffname (logged by employee)
PS. Sorry about the non-access syntax, but hopefully you can convert!