How to fetch records using NOT syntax? - mysql

I have the next tables: faculty, faculty_subjects and subjects. Every faculty has a list of subjects
How can I SELECT subjects that are not needed for this faculty ?
Note: if user adds some new subject, then this subject doesn't already have a record in faculty_subjects table, but it should be displayed in SELECT.
So to sum up - from the list of ALL subjects that exists I want to know the ones that doesn't needed for this faculty. How this can be achieved ?
I'm using MySQL database.
PS. if there is a more better title - please edit.

You can LEFT JOIN to the Faculty_Subject, with the particulary Faculty ID you are interested in used in the join predicate, then filter out the rows with a match using the WHERE predicate, so you are left with the subjects you want:
SELECT s.id, s.Name
FROM Subject AS s
LEFT JOIN Faculty_Subject AS fs
ON fs.Subject_idSubject = s.ID
AND fs.Faculty_idFaculty = 1
WHERE fs.id IS NULL;
Note, I would usually advise to use NOT EXISTS syntax:
SELECT s.id, s.Name
FROM Subject AS s
WHERE NOT EXISTS
( SELECT 1
FROM Faculty_Subject AS fs
WHERE fs.Subject_idSubject = s.ID
AND fs.Faculty_idFaculty = 1
);
Which I beleive is more logical to the reader, but in MySQL LEFT JOIN/IS NULL performs better than NOT EXISTS

SELECT name FROM Subject WHERE id NOT IN
(SELECT FS.Subject_idSubject
FROM Faculty_Subjects AS FS
INNER JOIN Faculty AS F ON F.id=FS.Faculty_idFaculty
WHERE F.name='Physics' AND FS.Subject_idSubject IS NOT NULL)

Related

Why am I getting a syntax error in mysql?

SELECT ID, Name
FROM Members
WHERE ID IN
(SELECT Member ID
FROM Team Member
WHERE Team ID IN
(SELECT ID
FROM Teams
WHERE Year = 2012 AND Country = 'Phillipines'))
When I input this query into phpmyadmin, I get error #1064 saying I do not have the correct syntax. How can resolve this?
The immediate error appears to be WHERE Team ID IN ... which should actually be WHERE TeamID IN .... There are also other similar errors. But, we can try rewriting your query using joins, so that it is more readable and maintainable:
SELECT m.ID, m.Name
FROM Members m
INNER JOIN TeamMember tm
ON m.ID = tm.MemberID
INNER JOIN Teams t
ON tm.TeamID = t.ID
WHERE
t.Year = 2012 AND t.Country = 'Phillipines';
In general, identifiers in SQL have to be a single word without any whitespace. If Team ID is actually a column name, then you would have to refer to it in MySQL using backticks. But, you should avoid doing this.
Your table Team Member and its columns Member ID and Team ID maybe mispelled.
I think you are using wrong variable name (Member ID) it should not have space in between Member and Id. in the same way (Team Member), (Team ID).
Please make sure you use the right name for column name and table name.
You can try below using join
SELECT a.ID, a.Name
FROM Members a inner join Team b on a.ID=b.MemberID
inner join Teams c on b.TeamID=c.ID
where c.Year = 2012 AND c.Country = 'Phillipines'

MySQL Join with looped reference query

I have a slightly complex table structure that I'm trying to query for a search function, but my queries keep timing out. Basically, it's a book search, and I'm focusing on the subject portion of that search.
The subjects table is simple (id and title), but there's a link table that refers it back to itself called subjects_subjects, which complicates things.
**subjects_subjects**
id (key)
subject_id (reference to subjects table)
see_subject_id (another reference to subjects table)
The reason for the looping reference is to catch subjects that don't contain any books, but point to subjects that do. For example, there's no books under the 'Travel' subject, so that subject has a link to 'Explorers' and 'Voyages' that do contain books. The point is to make searching easier.
So what I'm trying to do is allow the user to search for 'Travel', but return results from 'Explorers' and 'Voyages'. Here's my query that times out:
SELECT
BK.id,
BK.title
FROM
books BK
LEFT OUTER JOIN
books_subjects BS
ON BS.book_id = BK.id
WHERE
BS.subject_id IN (1639,3173)
OR BS.subject_id IN
(
SELECT
SS.see_subject_id
FROM
subjects_subjects SS
WHERE
SS.subject_id IN (1639,3173)
)
GROUP BY
BK.books_id
Extra info: There are 17000 books and over 3000 subjects in the database, with roughly 84000 book/subject references.
Can anyone help me figure out where am I going wrong here?
You're doing two things that MySQL optimizes poorly:
OR in the WHERE clause.
IN (SELECT ...)
Instead of OR, use two queries that you combine with OR. And instead of IN (SELECT ...) use a JOIN.
Also, you shouldn't use LEFT JOIN if you don't need to return rows from the first table with no matches in the second table, use INNER JOIN.
SELECT b.id, b.title
FROM books AS b
JOIN books_subjects AS bs ON bs.book_id = b.id
WHERE bs.subject_id IN (1639, 3173)
UNION
SELECT books AS b
JOIN books_subjects AS bs ON bs.book_id = b.id
JOIN subjects_subjects AS ss ON bs.subject_id = ss.see_subject_id
WHERE ss.subject_id IN (1639, 3173)

in MySQL can I compare id FK with varchar in another table

I have table with courseID, studentName and the other table is courseID , courseName user will search for the course name in the student table based on the student name
what sql syntax should I use ? join or inner join
If you use JOIN and specify a constraint (e.g. on a.courseId = b.courseId) it is exactly the same as if you use INNER JOIN, so according to your table structure you should do something like:
select * from studentTable
inner join courseTable
on studentTable.courseID = courseTable.courseID
where studentTable.studentName = 'Jack Black';
In MySQL you could also write
select * from studentTable, courseTable
where studentTable.courseID = courseTable.courseID
and studentName = 'Jack Black';
as it would internally be queried in the same way.
Check out the exact syntax for joins here: http://dev.mysql.com/doc/refman/5.7/en/join.html
You can try this:
select a.studentName,b.courseName
from tbl_name a
inner join tbl_name b
on a.courseID = b.courseID
You join when you are looking for a joined result, such as select all student names along with all their courses. E.g.:
select s.studentname, c.coursename
from course c
join student_takes_course s on s.courseid = c.courseid;
or (as the order of the tables doesn't matter):
select s.studentname, c.coursename
from student_takes_course s
join course c on s.courseid = c.courseid;
JOIN is just short for INNER JOIN. You can use either.
But when you are not interested in the joined result, then it's a good habit not to join. Such as when you simply want to show the course names from the course table, as in your example. You would then use IN or EXISTS usually:
select coursename
from course
where courseid in
(
select courseid
from student_takes_course
where studentname = 'Joe'
);
or
select c.coursename
from course c
where exists
(
select *
from student_takes_course s
where s.studentname = 'Joe'
and s.courseid = c.courseid
);
(I prefer IN clauses over EXISTS clauses for their simplicity and use them when possible.)

I am unsure: Is this an anti-join?

I am working on the first problem of the famous SQLzoos and am working on the using Null section: http://sqlzoo.net/wiki/Using_Null
The question is:
List the teachers who have NULL for their department.
The corresponding SQL query would be:
SELECT t.name
FROM teacher t
WHERE t.dept IS NULL
Is this a type of anti-join? Specifically, is this a left-anti-join?
This isn't a join at all.
The statement is filtering only records for teachers who don't have an assigned department.
Set Difference
The set difference of teachers and departments, teacher \ department would be a kind of "anti-join"
SELECT
t.name
FROM teacher t
LEFT JOIN department d ON d.id = t.dept_id
WHERE d.id IS NULL
At first glance, this statement does what your statement does, if the foreign key reference was enforced, it would guarantee to do exactly that. However, one use for this statement would be to retrieve teachers who are assigned to departments that have since been deleted (e.g. if the English Lit Dept. & English as 2nd Lang Dept. were reorganized as the English Dept.)
Symmetric Difference
Another "anti-join" would be the symmetric difference, which selects elements from both sets ONLY if they cannot be joined, i.e
(teacher \ department) U (department \ teacher)
I can't think of a motivating example using teachers and departments, but one way to write the symmetric difference on databases that support the FULL OUTER JOIN would be:
SELECT
t.name
FROM teacher t
FULL OUTER JOIN department d ON d.id = t.dept_id
WHERE d.id IS NULL OR t.id IS NULL
For MySQL, this statement would have to be written as the union of two statements.
SELECT
t.name teacher_name, d.name department_name
FROM teacher t
LEFT JOIN department d ON d.id = t.dept_id
WHERE d.id IS NULL
UNION ALL
SELECT
t.name teacher_name, d.name department_name
FROM teacher t
LEFT JOIN department d ON d.id = t.dept_id
WHERE t.id IS NULL
Looking through one of my projects, I found this one use of symmetric difference:
Context:
I have three tables: users, users_gameplay_summary, users_transactions_summary. I needed to email those users who created their accounts in the past 7 days AND one of the following
have transacted but have not played or played but have not transacted.
To get the list, I have this query (note, this was written for Postgresql, and won't work on MySQL, but it illustrates the symmetric difference use case):
SELECT
COALESCE(g.user_id, t.user_id) user_id
FROM users_gameplay_summary g
FULL OUTER JOIN users_transactions_summary t ON t.user_id = g.user_id
WHERE COALESCE(g.user_id, t.user_id) IN (
SELECT user_id
FROM users
WHERE created_at > CURRENT_DATE - '7 day'::interval)
AND (g.user_id IS NULL OR t.user_id IS NULL)
Not exactly, your not actually joining anything now,
in the case of a left anti join you would have access to the department name as well. (although it would be NULL)
Your sql code would be a correct answer for the question you gave though.
A left anti join would be:
SELECT t.name
FROM teacher t
LEFT JOIN dept d ON d.id = t.dept
WHERE d.id IS NULL
To solve this problem of listing teachers without assigned departments, you don't need a JOIN between teacher and dept tables.
dept table is basically a dictionary table that you join to, to translate ids to corresponding names.
teacher table has a dept column which normally could have a FOREIGN KEY constraint to id column in dept table.
Your query is not an ANTI-JOIN. This is a simple projection and selection query using one table.
SELECT t.name
FROM teacher t
WHERE t.dept IS NULL
For an ANTI-JOIN you would at least need a JOIN operation between more than one table at first.
Normally an ANTI-JOIN could look like:
Using LEFT JOIN
SELECT *
FROM table1 t1
LEFT JOIN table2 t2
ON t1.join_column = t2.join_column
WHERE t2.join_column IS NULL
Using NOT EXISTS
SELECT *
FROM table1 t1
WHERE NOT EXISTS (
SELECT 1
FROM table2 t2
WHERE t1.join_column = t2.join_column
)

Join query for getting non matching records

I have a question regarding MySql statement. I have 2 tables, record and training. Training table contains list of all courses, and record table contains list of courses that the user have attend. Training table is below
Record table format:
I want to get list non attending courses of each user. For eg: From the above table structure, user 277 is attending 130,167,128 & 2. So the non attending courses of user 277 is 3,4,5,7,8,9,147,11,12.
How can i write sql statement for getting the above result? Please help
Its something like this
SELECT * FROM training t WHERE t.id NOT IN (select trainingId from record where UserId=277 && piId=1) && t.status=1 ORDER BY t.categoryId
select id from training where id not in ( select trainingid from record where userid = yourid)
Get a list of users, cross join that with the training courses and then LEFT OUTER JOIN that to the table of training courses done by users. Us the WHERE clause to check there wasn't a match to the last table.
Untested, but something like this:-
SELECT sub_user.UserId, t.id
FROM
(
SELECT DISTINCT UserId from record
) sub_user
CROSS JOIN Training t
LEFT OUTER JOIN Record r
ON sub_user.UserId = r.UserId
AND r.trainingId = t.id
WHERE r.id IS NULL
If you have a table of users (which you probably do) then you could eliminate the sub query and replace it with that table.