Oracle performance issue Exists - sql

I have a small application for schools and I face the next issue
I have a query on Oracle that supposed to fetch all teachers that has a specific student in there classes:
select * from teacher
where exists
(
select * from students
where teacher.teacher_id = students.teacher_id
and lower(student_name) like '%john%'
)
But my query is taking a long time, I tried to use IN or even JOIN but that didn't solve my problem:
select * from teacher
where teacher.teacher_id in
(
select students.teacher_id from students
where lower(student_name) like '%john%'
)
and this is using JOIN:
select * from teacher
JOIN
(select students.teacher_id from students
where lower(student_name) like '%john%') STUD
ON STUD.teacher_id = teacher_id
actually all are giving me the same result but I found that using EXISTS was the fastest
query, but still taking much time -about 10 min-.
I think that there might be a way using %rowtype and nested tables to solve the problem...
I am a beginner in Oracle, so if some body can help I will be graceful.

select distinct * from teacher
join student on teacher.teacher_id = student.teacher_id
where student_name ..
and there should be an index on teacher_id of student

Related

How to display the desired rows of two tables using a subquery?

My subquery:
select studentName, Course.dataStart
from Student,
Course
where Student.id in (select Course.id from Course);
I need a solution to this (above) subquery (not a join)
Why does the SQL subquery display one date for each name? (task: display the names of students from the Student table and the course start date from the Course table using a subquery)
With the help of Join, I get it as it should: (but I need to do it with a subquery)
You seem to be using implicit join syntax, but really you should be using an explicit inner join:
SELECT s.studentName, c.dataStart
FROM Student s
INNER JOIN Course c
ON c.id = s.course_id;
If you really wanted to use the implicit join syntax, it should be something like this:
SELECT s.studentName, c.dataStart
FROM Student s, Course c
WHERE c.id = s.course_id;
But again, please use the first version as its syntax is considered the best way to do it.
You can apply join :
SELECT S.studentName, C.dataStart
FROM Student S
INNER JOIN Course C
ON C.id = S.course_id;
With Sub query:
Select studentName, (Select Course.dataStart from Course
Where Course.id = course_id)
From Student
Asuming that Course.Id field is Student.Id (although it seems strange to me), I think the only way to get the results you want with a subquery would be using it in the SELECT clause:
select studentName, (SELECT Course.dataStart FROM Course WHERE Course.Id = Student.Id)
from Student
This would fail if you have more than 1 row in Course per Student, in that case you could use (SELECT DISTINCT Course.dataStart...)

SQL: Find all rows in a table when the rows are a foreign key in another table

The caveat here is I must complete this with only the following tools:
The basic SQL construct: SELECT FROM .. AS WHERE... Distinct is ok.
Set operators: UNION, INTERSECT, EXCEPT
Create temporary relations: CREATE VIEW... AS ...
Arithmetic operators like <, >, <=, == etc.
Subquery can be used only in the context of NOT IN or a subtraction operation. I.e. (select ... from... where not in (select...)
I can NOT use any join, limit, max, min, count, sum, having, group by, not exists, any exists, count, aggregate functions or anything else not listed in 1-5 above.
Schema:
People (id, name, age, address)
Courses (cid, name, department)
Grades (pid, cid, grade)
I satisfied the query but I used not exists (which I can't use). The sql below shows only people who took every class in the Courses table:
select People.name from People
where not exists
(select Courses.cid from Courses
where not exists
(select grades.cid from grades
where grades.cid = courses.cid and grades.pid = people.id))
Is there way to solve this by using not in or some other method that I am allowed to use? I've struggled with this for hours. If anyone can help with this goofy obstacle, I'll gladly upvote your answer and select your answer.
As Nick.McDermaid said you can use except to identify students that are missing classes and not in to exclude them.
1 Get the complete list with a cartesian product of people x courses. This is what grades would look like if every student has taken every course.
create view complete_view as
select people.id as pid, courses.id as cid
from people, courses
2 Use except to identify students that are missing at least one class
create view missing_view as select distinct pid from (
select pid, cid from complete_view
except
select pid, cid from grades
) t
3 Use not in to select students that aren't missing any classes
select * from people where id not in (select pid from missing_view)
As Nick suggests, you can use EXCEPT in this case. Here is the sample:
select People.name from People
EXCEPT
select People.name from People AS p
join Grades AS g on g.pid = p.id
join Courses as c on c.cid = g.cid
you can turn the first not exists into not in using a constant value.
select *
from People a
where 1 not in (
select 1
from courses b
...

EXISTS Syntax in sql?

I have two tables:
students(id, name, school_id)
schools(id, name)
I'm trying to use EXISTS in order to learn if there are any students that go to specific school, say "Harvard" for example. I know that EXISTS is used after WHERE but I'm wondering if I can do this:
SELECT EXISTS
(SELECT *
FROM students st, schools sch
WHERE st.school_id=sch.id AND sch.name="Harvard");
Is this query correct? I am working on MySQL Workbench and I don't get an error. But I don't know if it does what it's supposed to do.
If it's not, then what should I change? I just want to know if it's correct and if I can use this syntax in the future.
Note that the desired result is either yes or no (1 or 0).
How do I get this result?
Sorry if my question was unclear, I can edit it again if you still don't understand.
You could just get the count, then you would know if any exist - and if so, how many...
SELECT COUNT(*)
FROM students st, schools sch
WHERE st.school_id=sch.id AND sch.name="Harvard"
The EXISTS keyword is normally used as a pre-condition... i.e. "if not exists, then add this..."
This is a simple Select with a Join.
SELECT *
FROM Students S
JOIN Schools SC
ON S.School_id = SC.id
Where SC.Name = 'Harvard'
This will give you all the rows for the students that go to Harvard, if any. If you want to do a count you can do SELECT COUNT(*) instead or limit which columns are returned by indicating the specific columns in the SELECT statement
This will give you student names that go to Harvard. If you wish to see how many students learn at Harvard replace st.name with count(*).
Note that it doesn't matter what you put in SELECT list inside EXISTS statement, so choosing a constant value provides better performance than selecting columns.
SELECT
st.name
FROM
students st
WHERE
EXISTS(
SELECT 1 FROM schools s WHERE s.name= 'Harvard' AND s.id = st.school_id)
OR
SELECT
st.name
FROM
students st
INNER JOIN schools s ON
st.school_id = s.id
WHERE
s.name = 'Harvard'
Additional note: Code below would only yield result either true or false.
SELECT EXISTS ( <query> )
This means that below query would return true if there are any students that learn at Harvard.
SELECT EXISTS (
SELECT 1
FROM students st
INNER JOIN schools s ON st.school_id = s.id
WHERE s.name = 'Harvard' )

Opposite of UNION SQL Query

I have 2 tables :
interests (storing the interest ID and name)
person_interests(storing the person_id and interest_id)
How do I select all the interests that a particular person has not selected?
I have tried the following SQL Query and am still not getting the desired result
SELECT *
FROM interests LEFT JOIN person_interests
ON interests.id=person_interests.person_id
WHERE person_interests.id IS NULL
AND person_id=66;
Use NOT EXISTS
SELECT *
FROM interests
WHERE NOT EXISTS (
SELECT person_interests.interest_id
FROM person_interests
WHERE person_id = 66
AND interests.id = person_interests.interest_id
)
SELECT * from interests
WHERE interest_id NOT IN
(SELECT interest_id FROM person_interests WHERE person_id=66)
There are a couple things going on.
First, I think you have an error in your join. Shouldn't it be interests.id=person_interests.interest_id instead of interests.id=person_interests.person_id?
That aside, I still don't think you would be getting the desired result because your person_id filter is on the RIGHT side of your LEFT OUTER join, thus turning it back into an inner join. There are several ways to solve this. Here's what I would probably do:
SELECT *
FROM
(SELECT interests.*, person_id
FROM interests LEFT JOIN person_interests
ON interests.id=person_interests.interest_id
WHERE person_interests.id IS NULL )
WHERE person_id=66;

Simple SQL question with subqueries

I'm refreshing my SQL with the online Stanford database class exercises, found here. Here is the problem:
"Find names and grades of students who only have friends in the same
grade. Return the result sorted by grade, then by name within each
grade."
We have a highschooler table, with the attributes name, grade, id. Also, the likes table has attributes id1 and id2. id1 and id2 in likes correspond to id in highschooler.
Based on the problem section this comes from, I can tell that I'll need to use subqueries, but I'm not sure where. How should I approach this problem? None of the currently suggested solutions work.
Here is my current SQL statement, that is not working correctly (ignoring sorting):
select distinct
student1.id,
student1.name,
student1.grade
from
highschooler student1,
highschooler student2,
friend
where not exists (select *
from friend
where student1.id = id1
and student2.id = id2
and student1.grade = student2.grade
and student1.id <> student2.id);
I assumed that, if A was B's Friend, it's equal to B was A's friend.
CREATE VIEW Temp
AS
SELECT id,name,grade,id2,[grd2] FROM highschooler
INNER JOIN Likes ON highschooler.id = Likes.id1
INNER JOIN (SELECT id as [id2t], grade as [grd2] from highschooler) a ON a.id2t = Likes.id2
UNION ALL
SELECT id,name,grade,[id1] as [id2],[grd2] FROM highschooler
INNER JOIN Likes ON highschooler.id = Likes.id2
INNER JOIN (SELECT id as [id2t], grade as [grd2] from highschooler) a ON a.id2t = Likes.id1
The temp view let me have all the info i need.
CREATE VIEW PlayWithClassMate
AS
SELECT distinct id FROM Temp WHERE grade = grd2
This PlayWithClassMate view let me have all student who play with her/his classmate (some how, i think a person can play, with all his/her friend not their classmate).
CREATE VIEW IDResult
AS
SELECT id FROM (
SELECT id, COUNT(GRD2) as c FROM TEMP
WHERE id in (SELECT id FROM PlayWithClassMate)
GROUP BY ID) A
WHERE C>1
this IDResult view have all the id the question ask you.
Now, select whatever you need, inwhich its ID in IDResult
i think it's not the best, or it may be the worst, but it work.
(srr abt terribe grammar)
This is harder than it looks, because it requires preparing sets sequentially. But, there are a few ways to solve this one. Here's what quickly comes to mind:
First, find the friend-of-friend for everybody by grade producing something like:
[ID], [FoF ID], [Grade of FoF]
You really don't need [FoF ID], but it might help when debugging.
Then, as a second-order operation, you'll need to produce a list of [ID]s where [Grade of FoF] is equal to both the MAX() and MIN():
SELECT [ID], MAX(Grade of FoF) as A, MIN(Grade of FoF) as B FROM [the above] WHERE A=B
UPDATE:
I realized that I should also add that in the final qry: A=B and A=Grade. Then this solution works. Keep in mind: it only answers the question "Find names and grades of students who only have friends in the same grade." and it assumes friendship is one-directional. (Sorry, I had to leave something undone.)
For those that need to see some SQL, here you are. It's written for MS Access, but easily ported (start by removing the "()" in the inner-most query) to MySQL, PGSQL, or Oracle. Better still, no procedural extensions and no temp tables.
SELECT
name
FROM
(
SELECT
ID
,name
,grade
,min( friend_grade) as min_friend_grade
,max( friend_grade) as max_friend_grade
FROM
(
SELECT
hs1.ID
,hs1.name
,hs1.grade
,l.ID2 as friend_id
,hs2.name as friend_name
,hs2.grade as friend_grade
FROM
( highschooler hs1
INNER JOIN likes l ON (hs1.ID = l.ID1) )
INNER JOIN highschooler hs2 ON (l.ID2 = hs2.ID)
)FoF
GROUP BY
ID
,name
,grade
)FoF_max_min
WHERE
grade=min_friend_grade
AND min_friend_grade=max_friend_grade